Spring Security 框架学习

概述

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,
充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,
为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

执行流程时序图

在这里插入图片描述

认证和授权流程图

在这里插入图片描述

责任链的设计模式

SpringSecurity 采用的是责任链的设计模式,它有一条很长的过滤器链。不同的过滤器对应不同的功能:

  • WebAsyncManagerIntegrationFilter:将 Security 上下文与 Spring Web 中用于处理异步请求映射的 WebAsyncManager 进行集成。

  • SecurityContextPersistenceFilter:在每次请求处理之前将该请求相关的安全上下文信息加载到 SecurityContextHolder 中,然后在该次请求处理完成之后,将 SecurityContextHolder 中关于这次请求的信息存储到一个“仓储”中,然后将 SecurityContextHolder 中的信息清除,例如在Session中维护一个用户的安全信息就是这个过滤器处理的。

  • HeaderWriterFilter:用于将头信息加入响应中。

  • CsrfFilter:用于处理跨站请求伪造。

  • LogoutFilter:用于处理退出登录。

  • UsernamePasswordAuthenticationFilter:用于处理基于表单的登录请求,从表单中获取用户名和密码。默认情况下处理来自 /login 的请求。从表单中获取用户名和密码时,默认使用的表单 name 值为 username 和 password,这两个值可以通过设置这个过滤器的usernameParameter 和 passwordParameter 两个参数的值进行修改。

  • DefaultLoginPageGeneratingFilter:如果没有配置登录页面,那系统初始化时就会配置这个过滤器,并且用于在需要进行登录时生成一个登录表单页面。

  • BasicAuthenticationFilter:检测和处理 http basic 认证。

  • RequestCacheAwareFilter:用来处理请求的缓存。

  • SecurityContextHolderAwareRequestFilter:主要是包装请求对象request。

  • AnonymousAuthenticationFilter:检测 SecurityContextHolder 中是否存在 Authentication 对象,如果不存在为其提供一个匿名 Authentication。

  • SessionManagementFilter:管理 session 的过滤器

  • ExceptionTranslationFilter:处理 AccessDeniedException 和 AuthenticationException 异常。

  • FilterSecurityInterceptor:可以看做过滤器链的出口。

  • RememberMeAuthenticationFilter:当用户没有登录而直接访问资源时, 从 cookie 里找出用户的信息, 如果 Spring Security 能够识别出用户提供的remember me cookie, 用户将不必填写用户名和密码, 而是直接登录进入系统,该过滤器默认不开启。

Spring Security快速入门

依赖和配置文件
  • 配置框架依赖
 <!-- 安全框架 -->
            <spring.security.version>5.0.6.RELEASE</spring.security.version>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-web</artifactId>
                <version>${spring.security.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-config</artifactId>
                <version>${spring.security.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-taglibs</artifactId>
                <version>${spring.security.version}</version>
            </dependency>
  • 项目启动加载web.xml 配置过滤器链的执行对象
<!--4、springsecurity委派过滤器-->
	<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框架核心配置文件
	<!--spring监听器-->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			classpath:applicationContext-security.xml
		</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

  • spring-security核心配置文件 约束头
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:security="http://www.springframework.org/schema/security"
       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/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd
            http://www.springframework.org/schema/security
            http://www.springframework.org/schema/security/spring-security.xsd">
    
    
    
</beans>
    
    
  • 核心配置信息
  <!-- 配置框架不需要拦截的资源   pattern  表示访问的资源路径或名称   security none  表示不要拦截  -->
      <security:http pattern="/login.html" security="none"/>
      <!--谷歌浏览器,如果访问页面失败,会自动请求一次/favicon.ico,保存为上次请求-->
      <security:http pattern="/favicon.ico" security="none"/>
      <!-- 2、配置授权信息 配置拦截的规则 auto-config="使用自带的页面"
    use-expressions="是否使用spel表达式",如果使用表达式:hasRole('ROLE_USER') -->
      <security:http auto-config="true" use-expressions="false">
            <!-- 配置拦截的请求地址,任何请求地址都必须有ROLE_USER的权限 -->
      <security:intercept-url pattern="/**" access="ROLE_USER"/>
            <!-- 3. 自定义登录页面-->
            <!--
                      login-page:登录页面
                      login-processing-url:登录处理url路径
                      default-target-url: 登录成功的页面
                      authentication-failure-url:登录失败的页面
                      authentication-failure-forward-url   不管用户认证通过之前访问的资源是什么,只要认证通过 都自动跳转到index.html页面
                       default-target-url="/index.html"  指定的页面
                  -->
            <security:form-login
                    login-page="/login.html"
                    username-parameter="username"
                    password-parameter="password"
                    login-processing-url="/login"
                    default-target-url="/index.html"
                    always-use-default-target="true"
                    authentication-failure-forward-url="/fail.html"
            ></security:form-login>
            <!--关闭自定义登录表单页面的token校验,如果不关闭,底层会进行令牌校验-->
            <security:csrf disabled="true"></security:csrf>

      </security:http>
       <!--1、配置认证信息   authentication-manager   -->
      <security:authentication-manager>
            <!--指定用户和密码的提供者 UserDetails接口的实现类  bean  id  userService-->
            <!--<security:authentication-provider user-service-ref="userService"/>-->
            <!--内存中的测试配置-->
            <!--* 在内存中配置链式的账号密码-->
            <!--* {noop}不使用加密-->
            <!--* authorities:角色,认证的角色必须是一个用户-->
            <security:authentication-provider>
                <security:user-service>
                    <security:user name="admin" password="{noop}admin" authorities="ROLE_USER"/>
                </security:user-service>
            </security:authentication-provider>
      </security:authentication-manager>
权限开发xml
  • 编写UserDetailsService接口的实现方法: loadUserByusername方法,添加权限和角色 角色必须以ROLE_ 前缀开头 !
authorities.add(new SimpleGrantedAuthority("ROLE_ADD"));
  • 在核心配置文件 applicationContext-security.xml中配置资源访问权限即可, 使用 security-intercept-url 可以配置多个权限访问!
    <!-- 配置拦截的请求地址,任何请求地址都必须有ROLE_USER的权限 -->
     <security:intercept-url pattern="/index.html" access="isAuthenticated()"/>
     <security:intercept-url pattern="/aa.html" access="hasAuthority('ROLE_ADD')"/>
注解权限开发
  1. 在核心加载配置文件中开启全局注解,注意因为权限框架底层基于aop实现,作用springweb容器有效,所以开启注解的配置一般在spring-mvc.xml中配置开启 不可以再监听器加载的配置文件中开启注解
 <security:global-method-security pre-post-annotations="enabled"/>
  1. 控制权限都会在Controller对象指定的方法添加对应的注解即可
    三种选择方式:
    1. //    @PreAuthorize("hasRole('ROLE_ADD')") //  对某个方法执行 必须有ROLE_ADD 权限 单一角色控制

    2. //    @Secured({ "ROLE_ADD", "ROLE_QUERY" })    或者关系  只要有一种角色即可访问此方法
  
    3. @PreAuthorize("hasRole('ROLE_ADD') and hasRole('ROLE_QUERY')")// 并列关系 必须同时具备两种角色才可以访问
案例测试
角色控制:@PreAuthorize("hasRole('ROLE_ADMIN')")

权限控制:@PreAuthorize("hasAuthority('add')")
<!--开启注解方式权限控制-->  
<security:global-method-security pre-post-annotations="enabled" />
@RestController
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/addUser.do")  //  spEL
    @PreAuthorize("hasRole('ROLE_ADD')")
    public  String  addUser(){
        System.out.println("----adduser-----");
        return  "add";
    }
}

对密码进行加密处理

前面我们使用的密码都是明文的,这是非常不安全的。一般情况下用户的密码需要进行加密后再保存到数据库中。
常见的密码加密方式有:

3DES、AES、DES:使用对称加密算法,可以通过解密来还原出原始密码

MD5、SHA1:使用单向HASH算法,无法通过计算还原出原始密码,但是可以建立彩虹表进行查表破解

bcrypt:将salt随机并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而无需单独处理salt问题,相同的密码每次加密的后都不一样,但通过其内部的算法还是可以将不同的密码匹配成功。

加密后的格式一般为:

$2a 10 10 10/bTVvqqlH9UiE0ZJZ7N2Me3RIgUCdgMheyTgV0B4cMCSokPa.6oCa

加密后字符串的长度为固定的60位。

其中:$是分割符,无意义;2a是bcrypt加密版本号;10是cost的值(2的10次方,进行多少次hash计算);

而后的前22位是salt值;再然后的字符串就是密码的密文了。

测试bcrypt加密
public static void main(String[] args) {  

    BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();  
    String encode1 = bCryptPasswordEncoder.encode("1234");  
    String encode2 = bCryptPasswordEncoder.encode("1234");  
    System.out.println(encode1);  
    System.out.println(encode2);  

    //使用明文密码和加密后的密码进行匹配  
    System.out.println(bCryptPasswordEncoder.matches("1234",encode2));  
    System.out.println(bCryptPasswordEncoder.matches("1234",encode1));  

}

将bcrypt加密方式应用到security框架中,使用bcrypt对密码进行加密
实现步骤
  • 在spring-security.xml文件中装配bcrypt密码加密对象,然后指定bcrypt加密策略
  <!--1、配置认证信息   authentication-manager   -->
    <security:authentication-manager>
        <!--指定用户和密码的提供者 UserDetails接口的实现类  bean  id  userService-->
        <!--内存中的测试配置-->
        <!--* 在内存中配置链式的账号密码-->
        <!--* {noop}不使用加密-->
        <!--* authorities:角色,认证的角色必须是一个用户  User -->
        <security:authentication-provider user-service-ref="userService">
            <security:password-encoder ref="bCryptPasswordEncoder"></security:password-encoder>
        </security:authentication-provider>
    </security:authentication-manager>
    <!--spring security  对密码进行加密处理-->
    <bean id="bCryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>
  • 注入BcryptPasswordEncoder加密对象,然后对密码进行加密
//模拟数据库查询用户信息
@Component("userService")
public class UserService implements UserDetailsService {
    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;
    //模拟数据库,将数据放入map集合中
    private void initData() {
        String pas = bCryptPasswordEncoder.encode("123");
        User user = new User();
        user.setUsername("zs");
        //数据库对密码加密
        user.setPassword(pas);

        User user1 = new User();
        user1.setUsername("lisi");
        user1.setPassword(pas);

        map.put("user1", user);
        map.put("user2", user1);

    }
  • 测试
    启动工程,然后使用map中的用户名和密码进行登陆测试:lisi 和 zs 登录访问 注意 加密之后 要记得去除掉“{noop}”

退出登录

用户完成登录后Spring Security框架会记录当前用户认证状态为已认证状态,即表示用户登录成功了。那用户如何退出登录呢?我们可以在spring-security.xml文件中的<security:http>标签中进行如下配置:

<!--  
 logout:退出登录  
 logout-url:退出登录操作对应的请求路径  
 logout-success-url:退出登录后的跳转页面

invalidate-session:清空session(是Security中的session,不是HttpSession)  
-->  
<security:logout logout-url="/logout.do"  
                logout-success-url="/login.html" invalidate-session="true"/>  

通过上面的配置可以发现,如果用户要退出登录,只需要请求/logout.do这个URL地址就可以,同时会将当前session失效,最后页面会跳转到login.html页面

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值