SpringSecurity原理解析

目录

  • 1、认证流程
    • 源码查看
      • 1、判断当前请求是不是post请求
      • 2、调用子类的方法进行身份认证,认证成功之后,把认证信息封装到对象里面去
        • 2.1、查看UsernamePasswordAuthenticationToken
        • 2.2、调用authenticate方法进行身份认证
      • 3、认证成功/失败处理
  • 2、授权流程
    • 2.1、 ExceptionTranslationFilter 过滤器
    • 2.2、 FilterSecurityInterceptor过滤器
  • 3、认证信息共享详解
    • 3.1、SecurityContextPersistenceFilter 过滤器

1、认证流程

总体的认证流程如下:
SpringSecurity原理解析_第1张图片
登录的请求首先会被拦截器UsernamePasswordAuthenticationFilter拦截,第一次请求肯定是未认证,未认证通过对象AuthenticationManager,来委托AuthenticationProvider,去关联UserDetailService,去查数据库,判断用户是否是数据库中存在的用户,当认证通过后,把数据封装到UserDetail中,再然后把认证 的信息,封装到Authentication

源码查看

UsernamePasswordAuthenticationFilter认证过滤器,继承自AbstractAuthenticationProcessingFilter
它的doFilter方法,其实是在父类AbstractAuthenticationProcessingFilter中,它只是重写了一些方法而已。

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
     
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;
        1、判断当前请求是不是post请求
        if (!this.requiresAuthentication(request, response)) {
     
            chain.doFilter(request, response);
        } else {
     
            if (this.logger.isDebugEnabled()) {
     
                this.logger.debug("Request is to process authentication");
            }

            Authentication authResult;
            try {
     
            	//2、调用子类的方法进行身份认证,认证成功之后,把认证信息封装到对象里面去
                authResult = this.attemptAuthentication(request, response);
                if (authResult == null) {
     
                    return;
                }
				//3、认证成功之后,用session存储相关信息。session策略处理
                this.sessionStrategy.onAuthentication(authResult, request, response);
            } catch (InternalAuthenticationServiceException var8) {
     
                this.logger.error("An internal error occurred while trying to authenticate the user.", var8);
                //4、认证失败,做认证失败的处理
                this.unsuccessfulAuthentication(request, response, var8);
                return;
            } catch (AuthenticationException var9) {
     
            	//4、认证失败,做认证失败的处理
                this.unsuccessfulAuthentication(request, response, var9);
                return;
            }
			//5、认证成功后的处理
            if (this.continueChainBeforeSuccessfulAuthentication) {
     
            	//执行下一个过滤器
                chain.doFilter(request, response);
            }
			//调用认证成功后的方法
            this.successfulAuthentication(request, response, chain, authResult);
        }
    }

1、判断当前请求是不是post请求

这个requiresAuthenticationRequestMatcher其实最终是来源于子类UsernamePasswordAuthenticationFilter的构造方法
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
     
     return this.requiresAuthenticationRequestMatcher.matches(request);
 }
protected AbstractAuthenticationProcessingFilter(RequestMatcher requiresAuthenticationRequestMatcher) {
     
     Assert.notNull(requiresAuthenticationRequestMatcher, "requiresAuthenticationRequestMatcher cannot be null");
     this.requiresAuthenticationRequestMatcher = requiresAuthenticationRequestMatcher;
}
UsernamePasswordAuthenticationFilter的构造方法
public UsernamePasswordAuthenticationFilter() {
     
     super(new AntPathRequestMatcher("/login", "POST"));
}

如果我们自定义一个认证过滤器,只需要继承自UsernamePasswordAuthenticationFilter,然后在构造方法中,调用setRequiresAuthenticationRequestMatcher就能覆盖UsernamePasswordAuthenticationFilter设置的默认的登录路径。

2、调用子类的方法进行身份认证,认证成功之后,把认证信息封装到对象里面去

UsernamePasswordAuthenticationFilter

    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
     		
    	//判断是否是post请求
        if (this.postOnly && !request.getMethod().equals("POST")) {
     
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
     
        	//从请求的queryString中获取用户名和密码
            String username = this.obtainUsername(request);
            String password = this.obtainPassword(request);
            if (username == null) {
     
                username = "";
            }

            if (password == null) {
     
                password = "";
            }

            username = username.trim();
            //将用户名和密码封装到UsernamePasswordAuthenticationToken中
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            //再将request请求,封装到UsernamePasswordAuthenticationToken对象中
            this.setDetails(request, authRequest);
            //将未认证的信息调用authenticate方法进行认证
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }
//从请求的queryString中获取用户名和密码   
protected String obtainUsername(HttpServletRequest request) {
     
   return request.getParameter(this.usernameParameter);
}

2.1、查看UsernamePasswordAuthenticationToken

SpringSecurity原理解析_第2张图片
该类是Authentication的实现类
SpringSecurity原理解析_第3张图片

2.2、调用authenticate方法进行身份认证

ProviderManagerAuthenticationManager 接口的实现类,该接口是认证相关的核心接口,也是认证的入口。在实际开发中,我们可能有多种不同的认证方式,例如:用户名+密码、邮箱+密码、手机号+验证码等,而这些认证方式的入口始终只有一个,那就是AuthenticationManager。在该接口的常用实现类 ProviderManager 内部会维护一个List列表,存放多种认证方式,实际上这是委托者模式(Delegate)的应用。每种认证方式对应着一个 AuthenticationProviderAuthenticationManager 根据认证方式的不同(根据传入的 Authentication 类型判断)委托对应的 AuthenticationProvider 进行用户认证。

ProviderManagerauthenticate方法的认证过程。

    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
     
    	//获取传入的Authentication 类型的UsernamePasswordAuthenticationToken.class
        Class<? extends Authentication> toTest = authentication.getClass();
        AuthenticationException lastException = null;
        AuthenticationException parentException = null;
        Authentication result = null;
        Authentication parentResult = null;
        boolean debug = logger.isDebugEnabled();
        //获取认证方式列表List<AuthenticationProvider>的迭代器
        Iterator var8 = this.getProviders().iterator();
		//循环迭代
        while(var8.hasNext()) {
     
            AuthenticationProvider provider = (AuthenticationProvider)var8.next();
            //判断当前的AuthenticationProvider是否支持UsernamePasswordAuthenticationToken.class类型的Authentication 
            if (provider.supports(toTest)) {
     
                if (debug) {
     
                    logger.debug("Authentication attempt using " + provider.getClass().getName());
                }
                try {
     
                	//重点!!!!!!!!!!!!!!!!!!!!!!!!!!!
                	//成功找到适配当前认证方式的AuthenticationProvider,此处为DaoAuthenticationProvider
                	//如果认证成功,会返回一个标记已认证的Authentication对象
                    result = provider.authenticate(authentication);
                    if (result != null) {
     
                    	//认证成功,将传入的Authentication对象中的details信息拷贝到已认证的Authentication对象中
                        this.copyDetails(authentication, result);
                        break;
                    }
                } catch (InternalAuthenticationServiceException | AccountStatusException var13) {
     
                    this.prepareException(var13, authentication);
                    throw var13;
                } catch (AuthenticationException var14) {
     
                    lastException = var14;
                }
            }
        }
        if (result == null && this.parent != null) {
     
            try {
     
            	//认证失败,使用父类型的AuthenticationManager进行验证
                result = parentResult = this.parent.authenticate(authentication);
            } catch (ProviderNotFoundException var11) {
     
            } catch (AuthenticationException var12) {
     
                parentException = var12;
                lastException = var12;
            }
        }
        if (result != null) {
     
        	//认证成功之后,去除result的敏感信息,要求相关类实现CredentialsContainer接口
            if (this.eraseCredentialsAfterAuthentication && result instanceof CredentialsContainer) {
     
            	//去除过程就是调用CredentialsContainer接口的eraseCredentials方法
                ((CredentialsContainer)result).eraseCredentials();
            }
			//发布认证成功的事件
            if (parentResult == null) {
     
                this.eventPublisher.publishAuthenticationSuccess(result);
            }
            return result;
        } else {
     
        	//认证失败之后,抛出失败的异常信息
            if (lastException == null) {
     
                lastException = new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound", new Object[]{
     toTest.getName()}, "No AuthenticationProvider found for {0}"));
            }
            if (parentException == null) {
     
                this.prepareException((AuthenticationException)lastException, authentication);
            }
            throw lastException;
        }
    }

看重点:DaoAuthenticationProviderauthenticate方法,实际上是父类AbstractUserDetailsAuthenticationProviderauthenticate方法

    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
     
        Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, () -> {
     
            return this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported");
        });
        String username = authentication.getPrincipal() == null ? "NONE_PROVIDED" : authentication.getName();
        boolean cacheWasUsed = true;
        //尝试从缓存中获取user
        UserDetails user = this.userCache.getUserFromCache(username);
        if (user == null) {
     
            cacheWasUsed = false;

            try {
     
            	//重点!!!!!
           		//缓存中没有获取到用户,直接去数据库中检索用户
                user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
            } catch (UsernameNotFoundException var6) {
     
                this.logger.debug("User '" + username + "' not found");
                if (this.hideUserNotFoundExceptions) {
     
                    throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
                }

                throw var6;
            }

            Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");
        }

        try {
     
            this.preAuthenticationChecks.check(user);
            this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
        } catch (AuthenticationException var7) {
     
            if (!cacheWasUsed) {
     
                throw var7;
            }

            cacheWasUsed = false;
            user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
            this.preAuthenticationChecks.check(user);
            this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
        }

        this.postAuthenticationChecks.check(user);
        if (!cacheWasUsed) {
     
        	//把获取到的用户放到缓存中
            this.userCache.putUserInCache(user);
        }

        Object principalToReturn = user;
        if (this.forcePrincipalAsString) {
     
            principalToReturn = user.getUsername();
        }

        return this.createSuccessAuthentication(principalToReturn, authentication, user);
    }

看重点:发现retrieveUser方法被子类DaoAuthenticationProvider,重写了

protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
     
    this.prepareTimingAttackProtection();

    try {
     
    	//获取一个UserDetailsService,并执行它的loadUserByUsername方法,来获取一个用户
    	//查询数据库获取用户的逻辑,就写在loadUserByUsername中
        UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
        if (loadedUser == null) {
     
            throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
        } else {
     
            return loadedUser;
        }
    } catch (UsernameNotFoundException var4) {
     
        this.mitigateAgainstTimingAttack(authentication);
        throw var4;
    } catch (InternalAuthenticationServiceException var5) {
     
        throw var5;
    } catch (Exception var6) {
     
        throw new InternalAuthenticationServiceException(var6.getMessage(), var6);
    }
}

认证过程最重要的部分就解析完了

3、认证成功/失败处理

回到AbstractAuthenticationProcessingFilter,查看successfulAuthenticationunsuccessfulAuthentication方法

protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
     
    if (this.logger.isDebugEnabled()) {
     
        this.logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
    }
	//将认证成功的用户信息对象Authentication封装进SecurityContext对象中
	//SecurityContextHolder是对ThreadLocal的一个封装
    SecurityContextHolder.getContext().setAuthentication(authResult);
    //remenberMe的处理
    this.rememberMeServices.loginSuccess(request, response, authResult);
    if (this.eventPublisher != null) {
     
    	//发布认证成功的事件
        this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
    }
	//调用认证成功处理器
    this.successHandler.onAuthenticationSuccess(request, response, authResult);
}
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
     
	//清除该线程在SecurityContextHolder中对应的SecurityContext对象
     SecurityContextHolder.clearContext();
     if (this.logger.isDebugEnabled()) {
     
         this.logger.debug("Authentication request failed: " + failed.toString(), failed);
         this.logger.debug("Updated SecurityContextHolder to contain null Authentication");
         this.logger.debug("Delegating to authentication failure handler " + this.failureHandler);
     }
	 //rememberMe的处理
     this.rememberMeServices.loginFail(request, response);
     //调用认证失败处理器
     this.failureHandler.onAuthenticationFailure(request, response, failed);
 }

2、授权流程

上一个部分通过源码的方式介绍了认证流程,下面介绍权限访问流程,主要是对ExceptionTranslationFilter 过滤器和 FilterSecurityInterceptor 过滤器进行介绍。

2.1、 ExceptionTranslationFilter 过滤器

该过滤器是用于处理异常的,不需要我们配置,对于前端提交的请求会直接放行,捕获后续抛出的异常并进行处理(例如:权限访问限制)。具体源码如下:
SpringSecurity原理解析_第4张图片

2.2、 FilterSecurityInterceptor过滤器

FilterSecurityInterceptor 是过滤器链的最后一个过滤器,该过滤器是过滤器链的最后一个过滤器,根据资源权限配置来判断当前请求是否有权限访问对应的资源。如果访问受限会抛出相关异常,最终所抛出的异常会由前一个过滤器。

ExceptionTranslationFilter 进行捕获和处理。具体源码如下:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
     
     FilterInvocation fi = new FilterInvocation(request, response, chain);
     this.invoke(fi);
 }
public void invoke(FilterInvocation fi) throws IOException, ServletException {
     
	if (fi.getRequest() != null && fi.getRequest().getAttribute("__spring_security_filterSecurityInterceptor_filterApplied") != null && this.observeOncePerRequest) {
     
	    fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
	} else {
     
	    if (fi.getRequest() != null && this.observeOncePerRequest) {
     
	        fi.getRequest().setAttribute("__spring_security_filterSecurityInterceptor_filterApplied", Boolean.TRUE);
	    }
		//根据资源权限配置来判断当前请求是否有权限访问对应的资源
		//如果不能访问则抛出异常
	    InterceptorStatusToken token = super.beforeInvocation(fi);
	
	    try {
     
	    	//访问相关资源,通过Springmvc的核心组件DispatchServlet进行访问
	        fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
	    } finally {
     
	    	//待请求完成后会在 finallyInvocation() 中将原来的 SecurityContext 重新设置给SecurityContextHolder。
	        super.finallyInvocation(token);
	    }
         // 正常请求结束,最后也会执行(afterInvocation 内部会调用finallyInvocation )
	    super.afterInvocation(token, (Object)null);
	}
}

需要注意,Spring Security 的过滤器链是配置在 SpringMVC 的核心组件DispatcherServlet 运行之前。也就是说,请求通过 Spring Security 的所有过滤器,不意味着能够正常访问资源,该请求还需要通过 SpringMVC 的拦截器链。

看看beforeInvocation

    protected InterceptorStatusToken beforeInvocation(Object object) {
     
        Assert.notNull(object, "Object was null");
        boolean debug = this.logger.isDebugEnabled();
        if (!this.getSecureObjectClass().isAssignableFrom(object.getClass())) {
     
            throw new IllegalArgumentException("Security invocation attempted for object " + object.getClass().getName() + " but AbstractSecurityInterceptor only configured to support secure objects of type: " + this.getSecureObjectClass());
        } else {
     
            Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
            if (attributes != null && !attributes.isEmpty()) {
     
                if (debug) {
     
                    this.logger.debug("Secure object: " + object + "; Attributes: " + attributes);
                }

                if (SecurityContextHolder.getContext().getAuthentication() == null) {
     
                    this.credentialsNotFound(this.messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound", "An Authentication object was not found in the SecurityContext"), object, attributes);
                }
				//判断是否需要进行身份认证
                Authentication authenticated = this.authenticateIfRequired();

                try {
     
                	//使用获取到的ConfigAttribute ,继续调用访问控制器AccessDecisionManager对当前请求进行鉴权。
                	//无论鉴权通过或是不通后,Spring Security 框架均使用了观察者模式,来通知其它Bean,当前请求的鉴权结果。
                	//如果鉴权不通过,则会抛出 AccessDeniedException 异常,即访问受限,然后会被 ExceptionTranslationFilter 捕获,最终解析后调转到对应的鉴权失败页面
                	//如果鉴权通过,AbstractSecurityInterceptor 通常会继续请求
                    this.accessDecisionManager.decide(authenticated, object, attributes);
                } catch (AccessDeniedException var7) {
     
                    this.publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, var7));
                    throw var7;
                }

                if (debug) {
     
                    this.logger.debug("Authorization successful");
                }

                if (this.publishAuthorizationSuccess) {
     
                    this.publishEvent(new AuthorizedEvent(object, attributes, authenticated));
                }
				//通过 RunAsManager 在现有 Authentication 基础上构建一个新的Authentication
                Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
                if (runAs == null) {
     
                    if (debug) {
     
                        this.logger.debug("RunAsManager did not change Authentication object");
                    }

                    return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);
                } else {
     
                    if (debug) {
     
                        this.logger.debug("Switching to RunAs Authentication: " + runAs);
                    }

                    SecurityContext origCtx = SecurityContextHolder.getContext();
                    SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
                    //如果新的 Authentication 不为空则将产生一个新的 SecurityContext,并把新产生的Authentication 存放在其中
                    //这样在请求受保护资源时从 SecurityContext中 获取到的 Authentication 就是新产生的 Authentication。
                    SecurityContextHolder.getContext().setAuthentication(runAs);
                    return new InterceptorStatusToken(origCtx, true, attributes, object);
                }
             // rejectPublicInvocations 属性,默认为 false。此属性含义为拒绝公共请求   
            } else if (this.rejectPublicInvocations) {
     
                throw new IllegalArgumentException("Secure object invocation " + object + " was denied as public invocations are not allowed via this interceptor. This indicates a configuration error because the rejectPublicInvocations property is set to 'true'");
            } else {
     
                if (debug) {
     
                    this.logger.debug("Public object - authentication not attempted");
                }

                this.publishEvent(new PublicInvocationEvent(object));
                return null;
            }
        }
    }

authenticateIfRequired:判断是否需要进行身份认证

private Authentication authenticateIfRequired() {
     
	//从SecurityContextHolder获取authentication 
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    //判断是否已经认证过了。
    //还记得UsernamePasswordAuthenticationToken的构造方法有设置是否认证过吗
    if (authentication.isAuthenticated() && !this.alwaysReauthenticate) {
     
        if (this.logger.isDebugEnabled()) {
     
            this.logger.debug("Previously Authenticated: " + authentication);
        }
		//认证过了,直接返回authentication
        return authentication;
    } else {
     
    	//没有认证过,调用相应的ProviderManager去认证
        authentication = this.authenticationManager.authenticate(authentication);
        if (this.logger.isDebugEnabled()) {
     
            this.logger.debug("Successfully Authenticated: " + authentication);
        }
		//认证完,把authentication 设置进SecurityContext中
        SecurityContextHolder.getContext().setAuthentication(authentication);
        return authentication;
    }
}

3、认证信息共享详解

一般认证成功后的用户信息是通过 Session 在多个请求之间共享,那么 Spring Security 中是如何实现将已认证的用户信息对象 AuthenticationSession 绑定的进行具体分析。
SpringSecurity原理解析_第5张图片
在前面讲解认证成功的处理方法 successfulAuthentication() 时,有以下代码:
SpringSecurity原理解析_第6张图片
查 看 SecurityContext 接 口 及 其 实 现 类 SecurityContextImpl , 该 类 其 实 就 是 对Authentication 的封装:
SpringSecurity原理解析_第7张图片
SpringSecurity原理解析_第8张图片
查看 SecurityContextHolder 类 , 该类其实是对ThreadLocal的封装,存储SecurityContext对象:
SpringSecurity原理解析_第9张图片

3.1、SecurityContextPersistenceFilter 过滤器

前面提到过,在 UsernamePasswordAuthenticationFilter 过滤器认证成功之后,会在认证成功的处理方法中将已认证的用户信息对Authentication 封装进SecurityContext,并存入 SecurityContextHolder

之后,响应会通过 SecurityContextPersistenceFilter 过滤器,该过滤器的位置在所有过滤器的最前面,请求到来先进它,响应返回最后一个通过它,所以在该过滤器中处理已认证的用户信息对象 AuthenticationSession 绑定。

认证成功的响应通过 SecurityContextPersistenceFilter 过滤器时,会从SecurityContextHolder 中取出封装了已认证用户信息对象 AuthenticationSecurityContext,放进 Session 中。当请求再次到来时,请求首先经过该过滤器,该过滤器会判断当前请求的 Session 是否存SecurityContext 对象,如果有则将该对象取出再次放入 SecurityContextHolder 中,之后该请求所在的线程获得认证用户信息,后续的资源访问不需要进行身份认证;当响应再次返回时,该过滤器同样从 SecurityContextHolder 取出SecurityContext 对象,放入 Session 中。具体源码如下:

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
     
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;
        if (request.getAttribute("__spring_security_scpf_applied") != null) {
     
            chain.doFilter(request, response);
        } else {
     
            boolean debug = this.logger.isDebugEnabled();
            request.setAttribute("__spring_security_scpf_applied", Boolean.TRUE);
            if (this.forceEagerSessionCreation) {
     
                HttpSession session = request.getSession();
                if (debug && session.isNew()) {
     
                    this.logger.debug("Eagerly created session: " + session.getId());
                }
            }

            HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
            //请求到来,检查当前session中是否存有SecurityContext对象,如果有,从session中取出该对象
            //如果没有,创建一个空的SecurityContext对象
            SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);
            boolean var13 = false;

            try {
     
                var13 = true;
                //将上述获得的SecurityContext对象放入到SecurityContextHolder中
                SecurityContextHolder.setContext(contextBeforeChainExecution);
                //进入到下一个过滤器
                chain.doFilter(holder.getRequest(), holder.getResponse());
                var13 = false;
            } finally {
     
                if (var13) {
     
                	//响应返回时,从SecurityContextHolder中取出SecurityContext
                    SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
                    //移除SecurityContextHolder中的SecurityContext
                    SecurityContextHolder.clearContext();
                    //将取出的SecurityContext对象放进session中
                    this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
                    request.removeAttribute("__spring_security_scpf_applied");
                    if (debug) {
     
                        this.logger.debug("SecurityContextHolder now cleared, as request processing completed");
                    }

                }
            }
            SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
            SecurityContextHolder.clearContext();
            this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
            request.removeAttribute("__spring_security_scpf_applied");
            if (debug) {
     
                this.logger.debug("SecurityContextHolder now cleared, as request processing completed");
            }

        }
    }

SecurityContextPersistenceFilter 是在SecurityContextConfigurer中被addFilter

你可能感兴趣的:(spring)