1.Session Management
1.1.必须明确调用SecurityContextRepository保存SecurityContext
在Spring Security 5中,默认行为是SecurityContext使用SecurityContextPersistenceFilter自动保存到SecurityContextRepository
。
//版本5.7.11 //SecurityContextPersistenceFilter中核心代码 HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);try {SecurityContextHolder.setContext(contextBeforeChainExecution);//...}finally {SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();SecurityContextHolder.clearContext();// 保存this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());}
SecurityContextPersistenceFilter
已被作废
在Spring Security 6中,默认行为是SecurityContextHolderFilter只会从SecurityContextRepository
读取SecurityContext
并将其填充到SecurityContextHolder
中。如果用户希望SecurityContext
在请求之间保持不变,他们现在必须使用SecurityContextRepository
显式保存SecurityContext
//版本6.5.2 private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws ServletException, IOException {Supplier<SecurityContext> deferredContext = this.securityContextRepository.loadDeferredContext(request);try {this.securityContextHolderStrategy.setDeferredContext(deferredContext);chain.doFilter(request, response);}finally {// 没有保存this.securityContextHolderStrategy.clearContext();request.removeAttribute(FILTER_APPLIED);}}
1.2.登录成功的通用保存逻辑
如果是登录成功,根据不同的登录方式设置,以下是必须要设置的
// 版本6.5.2 SecurityContext context = this.securityContextHolderStrategy.createEmptyContext(); context.setAuthentication(authenticationResult); this.securityContextHolderStrategy.setContext(context); this.securityContextRepository.saveContext(context, request, response);
用户名密码登录可参见AbstractAuthenticationProcessingFilter
//版本5.7.11 protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,Authentication authResult) throws IOException, ServletException {SecurityContext context = SecurityContextHolder.createEmptyContext();context.setAuthentication(authResult);SecurityContextHolder.setContext(context);this.securityContextRepository.saveContext(context, request, response);if (this.logger.isDebugEnabled()) {this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));}this.rememberMeServices.loginSuccess(request, response, authResult);if (this.eventPublisher != null) {this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));}this.successHandler.onAuthenticationSuccess(request, response, authResult);} // 版本6.5.2 private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy(); protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,Authentication authResult) throws IOException, ServletException {//创建SecurityContext的对象不同SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();context.setAuthentication(authResult);this.securityContextHolderStrategy.setContext(context);// 显示保存this.securityContextRepository.saveContext(context, request, response);if (this.logger.isDebugEnabled()) {this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));}this.rememberMeServices.loginSuccess(request, response, authResult);if (this.eventPublisher != null) {this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));}this.successHandler.onAuthenticationSuccess(request, response, authResult);}
1.3.SecurityContextRepository
默认的初始化
//版本6.5.2 HttpSessionSecurityContextRepository httpSecurityRepository = new HttpSessionSecurityContextRepository(); DelegatingSecurityContextRepository defaultRepository = new DelegatingSecurityContextRepository(httpSecurityRepository, new RequestAttributeSecurityContextRepository()); return defaultRepository; //版本5.7.11 HttpSessionSecurityContextRepository httpSecurityRepository; = new HttpSessionSecurityContextRepository(); return httpSecurityRepository;
1.4.SecurityContextRepository的接口
public interface SecurityContextRepository { //5.7.11中使用的接口@DeprecatedSecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder); //最新6.5.2中使用接口,目前兼容,作废的loadContext后续会被删除default DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {Supplier<SecurityContext> supplier = () -> loadContext(new HttpRequestResponseHolder(request, null));return new SupplierDeferredSecurityContext(SingletonSupplier.of(supplier),SecurityContextHolder.getContextHolderStrategy());} void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response); boolean containsContext(HttpServletRequest request); }
2.csrf的token
private CsrfTokenRepository tokenRepository; //5.7.11通过一下方法获取 CsrfToken token = tokenRepository.loadToken(request); if (token != null) {userDetails.getUserDTO().setToken(token.getToken()); } //6.5.2使用以下方式 DeferredCsrfToken token = tokenRepository.loadDeferredToken(request, response); if (token != null) {userDetails.getUserDTO().setToken(token.get().getToken()); }
3.授权
//5.7.11 使用以下方式 在6中已被标记作废 FilterSecurityInterceptor AccessDecisionManager//6.5.2 已通过 AuthorizationManager控制public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry authorizationManagerRequestMatcherRegistry) {HttpSecurity http = authorizationManagerRequestMatcherRegistry.and(); AuthorizationManager<RequestAuthorizationContext> requestAuthorizationManager = new xxxx authorizationManagerRequestMatcherRegistry.requestMatchers(accessRequestMatcherToArray()).access(xxRequestAuthorizationManager); }
4.SecurityFilterChain的配置
<1>HttpSecurity
中的apply
方法将被作废
//升级前 http.apply(xxxCaptchaVerifier()) //升级后 http.with(xxxCaptchaVerifier(), withDefaults())
<2>xxExceptionHandlingCustomizer
中and
方法将被作废,主要获取HttpSecurity
,然后获取ApplicationContext
,获取Bean
// 升级前 public class XxExceptionHandlingCustomizer extends XxAbstractCustomizer<ExceptionHandlingConfigurer<HttpSecurity>> {@Overridepublic void customize(ExceptionHandlingConfigurer<HttpSecurity> exceptionHandlingConfigurer) {//.and()已作废HttpSecurity http = exceptionHandlingConfigurer.and();Http401UnauthorizedEntryPoint http401UnauthorizedEntryPoint = getBean(http, Http401UnauthorizedEntryPoint.class);} }// 升级后 public class XxExceptionHandlingCustomizer extends XxAbstractCustomizer<ExceptionHandlingConfigurer<HttpSecurity>> {public XxExceptionHandlingCustomizer(ApplicationContext context) {super(context);}@Overridepublic void customize(ExceptionHandlingConfigurer<HttpSecurity> exceptionHandlingConfigurer) {Http401UnauthorizedEntryPoint http401UnauthorizedEntryPoint = getBean(Http401UnauthorizedEntryPoint.class);exceptionHandlingConfigurer.authenticationEntryPoint(http401UnauthorizedEntryPoint);}}
<3>HttpSecurity
中多个配置不在使用and
方法,统一使用Customizer<T>
接口配置
//升级前 http.cors().and().securityContext(xxxSecurityContextCustomizer()) //升级后 //只开启cors,不自定义配置 http.cors(withDefaults()); //开启cors,自定义配置 http.cors(corsConfigurer -> {})