【Spring Security实战系列】Spring Security实战(一)

版权声明:【本文为博主原创文章或收集整理,未经博主允许不得转载】 https://blog.csdn.net/zsq520520/article/details/77880491
一 概要
Spring Security,这是一种基于 Spring AOP 和 Servlet 过滤器的安全框架。它提供全面的安全性解决方案,同时在 Web 请求级和方法调用级处理身份确认和授权。
如何使用spring security,总共有四种用法,从简到深为:
1、不用数据库,全部数据写在配置文件,这个也是官方文档里面的demo;
2、使用数据库,根据spring security默认实现代码设计数据库,也就是说数据库已经固定了,这种方法不灵活,而且那个数据库设计得很简陋,实用性差;
3、spring security和Acegi不同,它不能修改默认filter了,但支持插入filter,所以根据这个,我们可以插入自己的filter来灵活使用;
4、暴力手段,修改源码,前面说的修改默认filter只是修改配置文件以替换filter而已,这种是直接改了里面的源码,但是这种不符合OO设计原则,而且不实际,不可用。
这里过多的spring security解释和作用就不在这里赘述了,请自行搜索。目前最新版本的Spring Security为5.0.x,但是我这里用了稳定版本3.2.10。下面例子为一个简单的Spring Security配置应用。
二 新建一个web maven项目
创建Maven项目过程再次省略......
新建好项目之后在webapp下添加了两个jsp文件,adminPage.jsp和index.jsp。其中adminPage.jsp只有那些拥有ROLE_ADMIN,ROLE_USER其中一种权限的用户才能访问,而index.jsp只允许那些拥有ROLE_USER权限的用户才能访问。

三 配置过滤器
为了在项目中使用Spring Security控制权限,首先要在web.xml中配置过滤器,这样我们就可以控制对这个项目的每个请求了。
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
     http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">
  <display-name>SpringSecurity</display-name>
  <!-- 加载配置文件 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:applicationContext-security.xml</param-value>
  </context-param>
  <!-- 加载日志配置文件 -->
  <context-param>
    <param-name>logbackConfigLocation</param-name>
    <param-value>classpath:logback/logback.xml</param-value>
  </context-param>

  <!-- spring security 的过滤器配置 -->
  <filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <!-- Spring 容器启动监听器 -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

<!--日志文件监听-->
  <listener>
    <listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListener</listener-class>
  </listener>
</web-app>
四 Spring Security及日志文件配置
上述web中设计两个配置文件(logback.xml和applicationContext-security.xml)。故、第一、我们要在resources下创建新的日志文件logback.xml文件,配置如下:
<?xml version="1.0" encoding="UTF-8"?>
 
<!--
当scan属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:
设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:
当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-->
<!--该规则是 logback 的核心。级别排序为: TRACE < DEBUG < INFO < WARN < ERROR-->
<configuration scan="true" debug="false">
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="logs" value="E:/idea-security/ssecurity-page/logs" />
    <!--读取MySQL数据库配置文件信息-->
    <!--<property resource="db_logback.properties"></property>-->
    <timestamp key="bySecond" datePattern="yyyy-MM-dd HH:mm:ss"/>
    <contextName>${bySecond}</contextName>
    <!--<property name="APP_Name" value="${catalina.home}/logs"></property>-->

    <!-- 用于指定日志输出方式和输出格式 -->
    <!-- 将日志信息输入到控制台 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!-- 日志输出编码 -->
        <Encoding>utf8</Encoding>
        <encoder>
        	<!-- %-20(%d{HH:mm:ss.SSS} [%thread]) %-5level %logger{80} - %msg%n -->
            <pattern>%-20(%d{HH:mm:ss.SSS} [%thread]) %-5level %logger{80} - %msg%n</pattern>
        </encoder>
        <!--格式化输出:inherited
        %d表示日期,
        %thread表示线程名,
        %-5level:级别从左显示5个字符宽度
        %msg:日志消息,
        %n是换行符-->
        <!--<layout class="ch.qos.logback.classic.PatternLayout">-->
        <!--<pattern>%-20(%d{HH:mm:ss.SSS} [%thread]) %-5level %logger{80} - %msg%n</pattern>-->
        <!--</layout>-->
    </appender>

    <!-- 将日志写入文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <Encoding>utf-8</Encoding>
        <!--如果是 true,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true。-->
        <append>true</append>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${logs}/logbackWeb.%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>10</MaxHistory>
            <!--窗口索引最小值-->
            <minIndex>1</minIndex>
            <!--窗口索引最大值,当用户指定的窗口过大时,会自动将窗口设置为12-->
            <maxIndex>3</maxIndex>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
        <!--日志文件最大的大小-->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>

    </appender>

    <!--日志输出器 -->
    <!--name指定线程针对的包路径,level是日志级别,<appender-ref>定义使用那种appender-->
    <logger name="org.springframework.jdbc.core.StatementCreatorUtils"
            additivity="false" level="TRACE">
        <appender-ref ref="STDOUT" />
    </logger>
    <logger name="org.springframework.jdbc.core" additivity="true">
        <level value="DEBUG" />
        <appender-ref ref="STDOUT" />
    </logger>
    <logger name="java.sql.Connection" additivity="true">
        <level value="DEBUG" />
        <appender-ref ref="STDOUT" />
    </logger>
    <logger name="java.sql.Statement" additivity="true">
        <level value="DEBUG" />
        <appender-ref ref="STDOUT" />
    </logger>
    <logger name="java.sql.PreparedStatement" additivity="true">
        <level value="DEBUG" />
        <appender-ref ref="STDOUT" />
    </logger>
    <logger name="java.sql.ResultSet" additivity="true">
        <level value="DEBUG" />
        <appender-ref ref="STDOUT" />
    </logger>

    <!-- 用来设置某一个包或者具体的某一个类的日志打印级别,包名要与实际项目一致 -->
    <!--<logger name="org.apache" level="DEBUG"/>-->
    <!-- 将级别为“DEBUG”及大于“DEBUG”的日志信息传递给root处理,本身并不打印 -->
    <!--<logger name="com.zxct.edu" level="DEBUG"/>-->

    <!-- 日志输出级别 name="jdbc.sqltiming"-->
    <!--<logger name="org.apache" level="DEBUG"/>-->
    <!-- 用来设置某一个包或者具体的某一个类的日志打印级别,包名要与实际项目一致 -->
    <!--<logger name="com.zxct.edu" level="DEBUG" />-->
    <!-- 将级别为“DEBUG”及大于“DEBUG”的日志信息传递给root处理,本身并不打印 -->

    <!--<logger name="com.zxct.edu" additivity="true">
        <level value="WARN" />
        <appender-ref ref="mysqlApper"/>
    </logger>-->

    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE" />
    </root>

</configuration>
第二、在resources下新建applicationContext-security.xml,配置如下
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
             xmlns:beans="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:sec="http://www.springframework.org/schema/security"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-3.1.xsd
                        http://www.springframework.org/schema/tx
                        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
                        http://www.springframework.org/schema/security
                        http://www.springframework.org/schema/security/spring-security.xsd">


    <!-- 配置不过滤的资源(静态资源及登录相关).是忽略拦截某些资源的意思,主要是针对静态资源 -->
    <http pattern="/**/*.css" security="none"></http>
    <http pattern="/**/*.jpg" security="none"></http>
    <http pattern="/**/*.jpeg" security="none"></http>
    <http pattern="/**/*.gif" security="none"></http>
    <http pattern="/**/*.png" security="none"></http>
    <http pattern="/js/*.js" security="none"></http>

    <http pattern="/login.jsp" security="none"></http>
    <http pattern="/getCode" security="none" /><!-- 不过滤验证码 -->
    <http pattern="/test/**" security="none"></http><!-- 不过滤测试内容 -->

    <http auto-config="true">
        <!-- 表示访问app.jsp时,需要ROLE_SERVICE权限 -->
        <intercept-url pattern="/adminPage.jsp" access="hasRole('ROLE_ADMIN')"></intercept-url>
        <!--表示访问任何资源都需要ROLE_ADMIN权限。-->
        <intercept-url pattern="/**" access="hasRole('ROLE_USER')"></intercept-url>
    </http>

    <authentication-manager>
        <authentication-provider>
            <user-service>
                <user name="admin" password="123" authorities="ROLE_USER, ROLE_ADMIN" />
                <user name="user" password="123" authorities="ROLE_USER" />
            </user-service>
        </authentication-provider>
    </authentication-manager>
</beans:beans>
说明:
  1. 文件的头部为,声明在xml中使用Spring Security提供的命名空间。
  2. http部分配置如何拦截用户请求。auto-config='true'将自动配置几种常用的权限控制机制,包括form, anonymous, rememberMe。
  3. 利用intercept-url来判断用户需要具有何种权限才能访问对应的url资源,可以在pattern中指定一个特定的url资源,也可以使用通配符指定一组类似的url资源。例子中定义的两个intercepter-url,第一个用来控制对/admin.jsp的访问,第二个使用了通配符/**,说明它将控制对系统中所有url资源的访问。
  4. 其实在实际使用中,Spring Security采用的是一种就近原则,就是说当用户访问的url资源满足多个intercepter-url时,系统将使用第一个符合条件的intercept-url进行权限控制。在我们这个例子中就是,当用户访问/admin.jsp时,虽然两个intercept-url都满足要求,但因为第一个intercept-url排在上面,所以Spring Security会使用第一个intercept-url中的配置处理对/adminPage.jsp的请求,也就是说,只有那些拥有了ROLE_ADMIN权限的用户才能访问/adminPage.jsp。
  5. access指定的权限都是以ROLE_开头的,实际上这与Spring Security中的Voter机制有着千丝万缕的联系,只有包含了特定前缀的字符串才会被Spring Security处理。目前来说我们只需要记住这一点就可以了。
  6. user-service中定义了两个用户,admin和user。为了简便起见,我们使用明文定义了两个用户对应的密码,这只是为了当前演示的方便,之后的例子中我们会使用Spring Security提供的加密方式,避免用户密码被他人窃取
  7. 最重要的部分是authorities,这里定义了这个用户登陆之后将会拥有的权限,它与上面intercept-url中定义的权限内容一一对应。每个用户可以同时拥有多个权限,例子中的admin用户就拥有ROLE_ADMIN和ROLE_USER两种权限,这使得admin用户在登陆之后可以访问ROLE_ADMIN和ROLE_USER允许访问的所有资源。与之对应的是,user用户就只拥有ROLE_USER权限,所以他只能访问ROLE_USER允许访问的资源,而不能访问ROLE_ADMIN允许访问的资源。
五 pom.xml文件:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>cn.quan.ssm.sec</groupId>
  <artifactId>ssecurity</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>

  <name>ssecurity Maven Webapp</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java-version>1.8</java-version>

    <junit-test.version>4.12</junit-test.version>
    <security.version>4.2.3.RELEASE</security.version>
    <spring.version>4.3.10.RELEASE</spring.version>
    <logback.version>1.2.3</logback.version>
    <logback-ext-spring.version>0.1.4</logback-ext-spring.version>
    <jstl.version>1.2</jstl.version>
    <loging.version>1.2</loging.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit-test.version}</version>
      <scope>test</scope>
    </dependency>
    <!-- spring   start -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <!-- spring  end -->
    <!-- spring-security   start -->
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-web</artifactId>
      <version>${security.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-config</artifactId>
      <version>${security.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-core</artifactId>
      <version>${security.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-test</artifactId>
      <version>${security.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-taglibs</artifactId>
      <version>${security.version}</version>
    </dependency>

    <!-- spring-security  end -->
    <!-- logback start -->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>${logback.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-core</artifactId>
      <version>${logback.version}</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-access</artifactId>
      <version>${logback.version}</version>
    </dependency>
    <dependency>
      <groupId>org.logback-extensions</groupId>
      <artifactId>logback-ext-spring</artifactId>
      <version>${logback-ext-spring.version}</version>
    </dependency>
    <!-- logback end -->
    <!-- common start -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>${jstl.version}</version>
    </dependency>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>${loging.version}</version>
    </dependency>
    <!-- common end -->
  </dependencies>
  <build>
    <finalName>ssecurity</finalName>
  </build>
</project>
这样一个项目就构建完成了,部署到tomcat进行测试。
六 结果
在浏览器上输入:http://localhost:8888/SpringSecurityDemo/,因为没有登陆,所以无法访问index.jsp页面,这个时候spring security就起作用了,对资源进行拦截,因为没有符合权限的用户登陆,所以就跳转到登陆页面,其中这个登陆页面是Spring Security自动生成的,这也是auto-config=”true”起的作用之一


然后输入用户名和密码,成功跳转到index.jsp页面。


这里因为admin用户有ROLE_ADMIN和ROLE_USER权限,而index.jsp页面ROLE_USER权限即可访问,所以admin用户可以成功访问index.jsp和adminPage.jsp页面。
下面再来测试用户user,注意已经登陆了的话,应该重启浏览器,要不然会一直记住用户,无法做测试。



从上图中可以看到,登陆用户user,可以访问index.jsp页面但是无法访问adminPage.jsp。这是因为user用户只有ROLE_USER权限,而adminPage.jsp页面需要ROLE_USER权限,所以就拒绝访问。
以上就是一个简单的spring security配置应用。

特别说明:
相关源码的下载可以到http://download.csdn.net/download/zsq520520/9968528进行下载。项目结构详情如下:

该资源是Spring Security实战七篇文档中组织的源码,详情如下:
ssecurity项目是Spring Security实战(一和二)的源码;
ssecurity-db项目是Spring Security实战(三)的源码;
ssceurity-page项目是Spring Security实战(四)的源码;
ssecurity-pageClass项目是Spring Security实战(五)的源码;
ssecurity-customFilter项目是Spring Security实战(六)的源码;
ssecurity-rememberMe项目是Spring Security实战(七)的源码;
本人开发工具是IDEA,每个项目中的代码均可以运行并测试。Eclipse也是一样可以运行的。欢迎下载学习,如有问题,多多指教!

猜你喜欢

转载自blog.csdn.net/zsq520520/article/details/77880491