美文网首页springsecurity
认证授权 2 Spring Security + JWT (Sp

认证授权 2 Spring Security + JWT (Sp

作者: _River_ | 来源:发表于2021-05-04 22:10 被阅读0次
    1:Spring Security 介绍:
    Spring Security 是Spring 全家桶中非常强大的一个用来做身份 验证以及权限控制的框架,
    我们可以轻松地扩展它来满足我们当前系统安全性这方面的需求。
    
    流程概述:
    1:SecurityConfiguration 继承 WebSecurityConfigurerAdapter 配置 SpringMVC集成了 Spring Security
    
    2:SecurityConfiguration设置 鉴权过滤器(自定义过滤器)
        没有对请求进行真正的过滤 只是用于把Token中的重要用户信息存储在SecurityContextHolder中 
    
    3:鉴权过滤器 中对Token进行校验,并把校验通过的用户信息存放在 SecurityContextHolder 的 SecurityContext:    
    4:SecurityContext 的真正存储位置是在ThreadLocal 
    5: 在SecurityContextPersistenceFilter过滤器把 SecurityContext  存储到 ThreadLocal 中
    
    6:后续在整个项目中可以通过 SecurityContextHolder 获取Token的用户信息
    
    7:在SecurityConfiguration对存储在SecurityContextHolder中的用户信息做判断权限信息的判断
    
    2:WebSecurityConfigurerAdapter
    实际上可以理解为Spring Security  是在Spring MVC 这层进行操作的
    
    @EnableWebSecurity 注解使得 SpringMVC 集成了 Spring Security 的 web 安全
    支持WebSecurityConfigurerAdapter 重写了其中的特定方法,
    用于自定义 Spring Security 配置。 
    整个 Spring Security 的工作量,其实都是集中在该配置类。
    
    其中重写configure中可以添加逻辑 等等
    1:设置拦截路径
    2:设置放行路径
    3: 可以添加自定义过滤器 (鉴权过滤器)
    
    @EnableWebSecurity
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
        
        }
    }
    
    3:核心组件之SecurityContextHolder
    鉴权过滤器 中 对Token进行校验 假如Token是有效的 
    可以把Token的关键信息  存放到 SecurityContextHolder 中
    
    作用:保留系统当前的安全上下文细节,其中就包括当前使用系统的用户的信息。
    

    安全上下文细节怎么表示?
    用SecurityContext对象来表示
    每个用户都会有它的上下文,那这个SecurityContext保存在哪里呢?
    存储在一个SecurityContextHolder中,整个应用就一个SecurityContextHolder。
    

    SecurityContextHolder存储SecurityContext的方式?
    这要考虑到应用场景。
    (1)单机系统,即应用从开启到关闭的整个生命周期只有一个用户在使用。由于整个应用只需要保存一个SecurityContext(安全上下文即可)
    (2)多用户系统,比如典型的Web系统,整个生命周期可能同时有多个用户在使用。
           这时候应用需要保存多个SecurityContext(安全上下文),需要利用ThreadLocal进行保存,
           每个线程都可以利用ThreadLocal获取其自己的SecurityContext,及安全上下文。
    
    4:SecurityContextHolder 存储策略 SecurityContextHolderStrategy
    SecurityContextHolder利用了一个SecurityContextHolderStrategy(存储策略)进行上下文的存储。
    下面为SecurityContestHolderStrategy 接口 与 实现类源码
    这个接口提供创建、清空、获取、设置上下文的操作
    
    //SecurityContextHolderStrategy 接口源码
    public interface SecurityContextHolderStrategy {
    
        void clearContext();
    
        SecurityContext getContext();
    
        void setContext(SecurityContext context);
    
        SecurityContext createEmptyContext();
    
    }
    
    GlobalSecurityContextHolderStrategy实现类 实现 SecurityContextHolderStrategy
    全局的上下文存取策略,只存储一个上下文,对应前面说的单机系统。
    
    ThreadLocalSecurityContextHolderStrategy实现类 实现 SecurityContextHolderStrategy
    多用户系统的上下文存取策略  其ThreadLocal内部会用数组来存储多个对象的。
    
    5:SecurityContext 与 ThreadLocal
    SecurityContextHolder 中的数据,本质上是保存在 ThreadLocal 中,
    ThreadLocal会为每个线程开辟一个存储区域,来存储相应的对象。
    ThreadLocal 的的特点是存在它里边的数据,哪个线程存的,哪个线程才能访问到。
    
    这样就带来一个问题,当不同的请求进入到服务端之后,由不同的 thread 去处理,
    按理说后面的请求就可能无法获取到登录请求的线程存入的数据,
    例如登录请求在线程 A 中将登录用户信息存入 ThreadLocal,
    后面的请求来了,在线程 B 中处理,那此时就无法应该是获取到用户的登录信息。
    

    SecurityContext又是什么时候存放到ThreadLocal中?
    
    SecurityContextPersistenceFilter过滤器(SpringSecurity中自带的 优先级非常高的一个过滤器)
    在UsernamePasswordAuthenticationFilter之前执行
    
    以下为SecurityContextPersistenceFilter过滤器 核心源码:
    
    每一个请求到达服务端的时候,首先从 session 中找出来 SecurityContext ,
    然后设置到 SecurityContextHolder 中去,方便后续使用,当这个请求离开的时候,
    SecurityContextHolder 会被清空,SecurityContext 会被放回 session 中,方便下一个请求来的时候获取。
    
    
     在 doFilter 方法中,它首先会从 repo  中读取一个 SecurityContext 出来,这里的 repo 实际上就是 HttpSessionSecurityContextRepository,
     读取 SecurityContext 的操作会进入到 readSecurityContextFromSession 方法中,
     可以看到读取的核心方法 Object contextFromSession = httpSession.getAttribute(springSecurityContextKey);,
     这里的 springSecurityContextKey 对象的值就是 SPRING_SECURITY_CONTEXT,读取出来的对象最终会被转为一个 SecurityContext 对象。
    
    
    
    public class SecurityContextPersistenceFilter extends GenericFilterBean {
    
        public SecurityContextPersistenceFilter(SecurityContextRepository repo) {        
            this.repo = repo;
        }
    
        public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws IOException, ServletException {
            
            HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request,response);
            //请求进入 从Session中获取SecurityContext 
            SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
            try {
               //请求进入 SecurityContext  存储到 SecurityContextHolder 中
                SecurityContextHolder.setContext(contextBeforeChainExecution);
                chain.doFilter(holder.getRequest(), holder.getResponse());
            }
            finally {
                SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
                ///请求离开  清空SecurityContextHolder 中 SecurityContext 
                SecurityContextHolder.clearContext();
                //请求离开  SecurityContext  存储到Session 中
                repo.saveContext(contextAfterChainExecution, holder.getRequest(),holder.getResponse());
            }
        }
    }
    
    
    回到问题:
    请求A进来 开启线程A  这个时候该线程A获取 SecurityContext信息 存储到线程A的ThreadLocal
    同时经过鉴权过滤器的校验 把用户相关信息存储到SecurityContext中
    
    请求B进来 开启线程B  这个时候该线程B获取 SecurityContext 信息 存储到线程B的ThreadLocal
     同时经过鉴权过滤器的校验 把用户相关信息存储到SecurityContext中
    

    但是如果在线程A 中 新开一个内部线程B:
    使用SecurityContextHolder.getContext().getAuthentication(),这肯定获取不到用户信息
    
    因为新开的内部线程B中并没有经过 SecurityContextPersistenceFilter过滤器
    来获取并存放用户信息到Context中 
    

    项目连接

    请配合项目代码食用效果更佳:
    项目地址:
    https://github.com/hesuijin/hesuijin-study-project
    Git下载地址:
    https://github.com.cnpmjs.org/hesuijin/hesuijin-study-project.git
    
    spring-security-jwt-module 项目模块下  
    

    相关文章

      网友评论

        本文标题:认证授权 2 Spring Security + JWT (Sp

        本文链接:https://www.haomeiwen.com/subject/qkjqdltx.html