A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

spring security简介
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。 Spring Security该框架存在两个重要的核心功能: 认证和授权
# spring security入门案例
## 导入相关的依赖包
    <properties>
  <spring.version>4.2.4.RELEASE</spring.version>
</properties>
<dependencies>
  <!-- 引入spring的依赖 -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>${spring.version}</version>
  </dependency>
  
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <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>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-test</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-jdbc</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <!-- 引入spring security的依赖 -->
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-web</artifactId>
   <version>4.1.0.RELEASE</version>
  </dependency>
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-config</artifactId>
   <version>4.1.0.RELEASE</version>
  </dependency>
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>servlet-api</artifactId>
   <version>2.5</version>
   <scope>provided</scope>
  </dependency>
</dependencies>

<build>

  <plugins>
   <!-- java编译插件 -->
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.2</version>
    <configuration>
     <source>1.7</source>
     <target>1.7</target>
     <encoding>UTF-8</encoding>
    </configuration>
   </plugin>
   <plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <configuration>
     <port>9090</port>        <!-- 指定端口 -->
     <path>/</path>         <!-- 请求路径 -->
    </configuration>
   </plugin>
  </plugins>
</build>
## web.xml文件的配置
    <!-- 配置spring的核心监听器 -->
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:spring-security.xml</param-value>
</context-param>
<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 配置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-security.xml文件的配置
### 文件头配置
我们使用到了spring security就需要引入spring security的头信息配置,如下:
    <?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/security http://www.springframework.org/s ... spring-security.xsd">
</beans>
在这里我们把security的头信息的配置作为了默认配置,那么以后在进行编写security的相关配置的时候就不需要<security:xxx>前缀了,方便我们后期的代码书写!
### 文件内容
<!-- 页面的拦截规则    use-expressions:是否启动SPEL表达式 默认是true -->
<http use-expressions="false">
  <intercept-url pattern="/**" access="ROLE_USER"/> <!-- 当前用户必须有ROLE_USER的角色 才可以访问根目录及所属子目录的资源 -->
  <form-login />           <!-- 开启表单登陆功能 -->
</http>

<!-- 认证管理器 -->
<authentication-manager>
  <authentication-provider>
   <user-service>
    <user name="admin" password="123456" authorities="ROLE_USER"/>
   </user-service>
  </authentication-provider>
</authentication-manager>
## 测试
访问<http://localhost:9090/>路径,发现我们请求的index.html页面被拦截了,跳转到了一个登陆页面,这个登录页面是由spring security给我们生成的!这是因为我们当时配置的拦截规则是/** 表示拦截所有的请求!

输入在spring-security.xml文件中配置的用户名和密码点击login按钮!登陆成功以后就可以看到我们的index.html中的内容!


spring security的配置介绍
## web.xml中配置的DelegatingFilterProxy
DelegatingFilterProxy类存在与spring-web包中,其作用就是一个filter的代理,用这个类的好处是可以通过spring容器来管理filter的生命周期,还有就是,可以通过spring注入的形式,来代理一个filter执行!DelegatingFilterProxy类继承GenericFilterBean,间接实现了Filter这个接口,故而该类属于一个过滤器。那么就会有实现Filter中init、doFilter、destroy三个方法。
1. 代理具体实现
首先我们看init方法,我们知道当filter初始化时会执行init方法,从源码中我们可以找到具体代码,该方法在GenericFilterBean类中实现,具体功能是,将该类封装成spring特有形式的类,方便spring维护,并且调用initFilterBean方法,该方法放在子类(DelegatingFilterProxy)实现,该方法主要目的是,找到在spring中维护的目标filter,具体实现看下面代码:
   @Override
  public final void init(FilterConfig filterConfig) throws ServletException {
   Assert.notNull(filterConfig, "FilterConfig must not be null");
   if (logger.isDebugEnabled()) {
    logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");
   }

   this.filterConfig = filterConfig;

   // Set bean properties from init parameters.
   try {
    // 将该类封装成spring特有的bean形式,方便spring维护
    PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
    BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
    ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
    bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
    initBeanWrapper(bw);
    bw.setPropertyValues(pvs, true);
   }
   catch (BeansException ex) {
    String msg = "Failed to set bean properties on filter '" +
     filterConfig.getFilterName() + "': " + ex.getMessage();
    logger.error(msg, ex);
    throw new NestedServletException(msg, ex);
   }

   // 该方法在子类中实现,我们可以到DelegatingFilterPoxy中去看看,具体完成了那些工作?
   //1、找到要代理bean的id--》targetBeanName
   //2、在spring,bean容器中找到具体被代理的filter--》delegate
   initFilterBean();

   if (logger.isDebugEnabled()) {
    logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");
   }
  }
2.  initFilterBean()该方法主要完成两个功能
1.  找到被代理类在spring中配置的id并赋值给targetBeanName
2.  使用找到的id从spring容器中找到具体被代理的类,并赋值给delegate
     
   @Override
   protected void initFilterBean() throws ServletException {
    synchronized (this.delegateMonitor) {
     if (this.delegate == null) {
      // 找到要被代理的filter在spring中配置的id
      if (this.targetBeanName == null) {
       this.targetBeanName = getFilterName();
      }
      
      WebApplicationContext wac = findWebApplicationContext();
      if (wac != null) {
       // 找到具体被代理的filter
       this.delegate = initDelegate(wac);
      }
     }
    }
   }
3. getFilterName()该方法的作用是,获取被代理的filter在spring中配置的id
   
  protected final String getFilterName() {
   // 找到被代理filter在spring中配置的id
   return (this.filterConfig != null ? this.filterConfig.getFilterName() : this.beanName);
  }
通过源码的查看,我们配置的DelegatingFilterProxy就是为了让其通过我们配置的filterName从spring容器中获取spring security对应的过滤器链.因此我们这个filterName需要是: springSecurityFilterChain 因为我们并没有在DelegatingFilterProxy过滤器中配置一些初始化参数!
## spring-security.xml文件
该文件为spring security的核心配置文件,在该文件中我们需要配置两部分的内容: 第一部分就是授权(通过<http>标签进行配置) , 第二部分就是认证(通过<authentication-manager>进行配置)
### 认证管理器的配置
    <authentication-manager>
  <authentication-provider>
   <user-service>
    <user name="admin" password="123456" authorities="ROLE_USER"/>
   </user-service>
  </authentication-provider>
</authentication-manager>
authentication-manager 元素指定了一个 AuthenticationManager,其需要一个 AuthenticationProvider(对应 authentication-provider 元素)来进行真正的认证,默认情况下 authentication-provider 对应一个 DaoAuthenticationProvider,其需要 UserDetailsService(对应 user-service 元素)来获取用户信息 UserDetails(对应 user 元素)。这里我们只是简单的使用 user 元素来定义用户,而实际应用中这些信息通常都是需要从数据库等地方获取的,这个将放到后续再讲。这里我们定义一个用户: 用户名为admin 密码为:123456该用户对应的角色为ROLE_USER(关于这个角色我们需要在授权模块中进行配置)
### 配置授权
我们使用<http>元素来定义web的权限控制
<http use-expressions="false">
   <intercept-url pattern="/**" access="ROLE_USER"/>
   <form-login />           
</http>
如上定义中,intercept-url 定义了一个权限控制的规则。pattern 属性表示我们将对哪些 url 进行权限控制,其也可以是一个正则表达式,如上的写法表示我们将对所有的 URL 进行权限控制;access 属性表示在请求对应的 URL 时需要什么权限,默认配置时它应该是一个以逗号分隔的角色列表,请求的用户只需拥有其中的一个角色就能成功访问对应的 URL。关于access的配置必须要以"ROLE_"开始. use-expressions用于是否使用sqel表达式来解析access,默认取值为true. 如果使用sqel表达式来解析access,那么access的应该写成: hasRole('USER').这样写不是很方便因此我们把use-expressions这个值设置成为了false.
### form-login 元素介绍
http 元素下的 form-login 元素是用来定义表单登录信息的。当我们什么属性都不指定的时候 Spring Security 会为我们生成一个默认的登录页面。如果不想使用默认的登录页面,我们可以指定自己的登录页面。
使用自定义登录页面
自定义登录页面是通过 login-page 属性来指定的。提到 login-page 我们不得不提另外几个属性。
1. username-parameter:表示登录时用户名使用的是哪个参数,默认是 “username”。
2. password-parameter:表示登录时密码使用的是哪个参数,默认是 “password”。
3. login-processing-url:表示登录时提交的地址,默认是 “/login”。这个只是 Spring Security 用来标记登录页面使用的提交地址,真正关于登录这个请求是不需要用户自己处理的。
所以,我们可以通过如下定义使 Spring Security 在需要用户登录时跳转到我们自定义的登录页面。
  <http use-expressions="false">
   <intercept-url pattern="/**" access="ROLE_USER" />     
   <form-login login-page="/login.html" login-processing-url="/userLogin" username-parameter="userName" password-parameter="password"/>
  </http>
需要注意的是,我们之前配置的是所有的请求都需要 ROLE_USER 权限,这意味着我们自定义的 “/login.html” 也需要登录权限,这样就会形成一个死循环了。解决办法是我们需要给 “/login.html” 放行。我们也可以通过指定一个 http 元素的安全性为 none 来达到相同的效果. 如下
<http pattern="/login.html" security="none"></http>
根据上面的配置,我们自定义的登录页面的内容应该是这样子的:
   
<form action="/userLogin" method="post">
  用户名:<input name="userName"><br>
  密码:<input name="password"><br>
  <input type="submit" value="登录">
</form>
我们输入用户名和密码以后进行登录,登录以后会出现如下错误:

pring security为了防止CSRF攻击,需要在每个页面中验证成功登录后创建的csrf token值,而我们在静态页面中又无法传递这个token,因此我们可以禁用CSRF这个功能禁用!在<http>标签中加入如下配置:
    <csrf disabled="true"/>
在进行登录,我们就可以看到登录成功以后访问的首页信息了.
## http-basic介绍
之前介绍的都是基于 form-login 的表单登录,其实 Spring Security 还支持弹窗进行认证。通过定义 http 元素下的 http-basic 元素可以达到这一效果。
<http use-expressions="false">
  <http-basic/>
  <intercept-url pattern="/**" access="ROLE_USER" />     
  <csrf disabled="true"/>
</http>
此时,如果我们访问受 Spring Security 保护的资源时,系统将会弹出一个窗口来要求我们进行登录认证。效果如下:



需要注意的是当我们同时定义了 http-basic 和 form-login 元素时,form-login 将具有更高的优先级。即在需要认证的时候 Spring Security 将引导我们到登录页面,而不是弹出一个窗口。



0 个回复

您需要登录后才可以回帖 登录 | 加入黑马