Spring Boot自定义Cookie认证集成 适配HTTP认证与@PreAuthorize
适配方案实现步骤
整体思路
不改动现有控制器代码、不影响原有HTTP认证逻辑,通过Spring Security过滤器链的扩展能力接入自定义Cookie认证流程,最终复用框架原生权限匹配能力打通@PreAuthorize校验逻辑。
第一步:实现自定义Cookie认证过滤器
直接复用已有的LoginToken解密、有效性校验逻辑,编写请求级过滤器专门处理携带EncryptedLoginToken的请求:
- 从当前请求的Cookie数组中提取
EncryptedLoginToken值 - 未检测到该Cookie时直接放行,交给原有HTTP认证流程处理,完全不干扰现有逻辑
- 检测到Cookie时,调用现有解密逻辑生成
LoginToken对象并执行有效性校验 - 校验通过后,把Token中携带的所有权限转换为Spring Security可识别的权限对象,构造完整的认证信息存入全局安全上下文
核心实现代码示例:
public class EncryptedLoginTokenAuthFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { Cookie[] cookies = request.getCookies(); String encryptedTokenValue = null; if (cookies != null) { for (Cookie cookie : cookies) { if ("EncryptedLoginToken".equals(cookie.getName())) { encryptedTokenValue = cookie.getValue(); break; } } } // 无目标Cookie直接放行,走原有认证流程 if (!StringUtils.hasText(encryptedTokenValue)) { filterChain.doFilter(request, response); return; } try { LoginToken loginToken = LoginToken.decrypt(encryptedTokenValue); // Token无效直接返回401 if (!loginToken.isValid()) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } // 将Token中的权限转换为框架识别的权限列表 List<GrantedAuthority> authorityList = loginToken.getPermissions().stream() .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); // 构造认证对象存入安全上下文 Authentication authentication = new UsernamePasswordAuthenticationToken( loginToken.getUserId(), encryptedTokenValue, authorityList ); SecurityContextHolder.getContext().setAuthentication(authentication); } catch (Exception e) { // 解密异常直接返回401 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } filterChain.doFilter(request, response); } }
第二步:将自定义过滤器接入Spring Security过滤链
在原有安全配置类中,把自定义过滤器注册到过滤链的靠前位置,确保它先于原有HTTP认证过滤器执行,同时不破坏原有认证逻辑:
@Configuration @EnableWebSecurity @EnableMethodSecurity // Spring Boot 3.x版本开启方法级权限校验的注解,旧版本替换为@EnableGlobalMethodSecurity(prePostEnabled = true) public class CustomSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // 保留全部原有安全配置,包括原有HTTP认证、接口路径规则、CSRF设置等逻辑完全不做修改 http // 原有配置比如http.httpBasic()、请求路径授权规则等 .addFilterBefore(new EncryptedLoginTokenAuthFilter(), BasicAuthenticationFilter.class); return http.build(); } }
第三步:打通@PreAuthorize的权限校验链路
这一步不需要写额外代码,也不需要修改原有控制器上的@PreAuthorize注解:
- Spring Security的权限校验逻辑是统一从
SecurityContextHolder中读取当前认证用户的身份、权限列表,框架原生的hasAuthority('xxx')表达式,本质就是在当前认证对象的权限列表中匹配对应字符串 - 我们在认证过滤器中已经把自定义Cookie解密出的权限值,全部转换为了框架可识别的
SimpleGrantedAuthority对象,因此权限匹配逻辑可以无缝衔接 - 只要解密后的Token有效、且包含
hasAuthority指定的权限值,校验就会自动通过,和原本手写的userHasPermission(permission)逻辑完全等价 - 原来业务代码中手动调用
userHasPermission的逻辑可以完全移除,所有校验会由框架在方法级拦截时自动触发
注意事项
- 两种认证方式可完全共存:未携带自定义Cookie的请求会被过滤器直接放行,走原有HTTP认证流程,没有侵入性
- 自定义Token校验要做好异常兜底:包括解密失败、签名不匹配、Token过期的场景,要直接返回401,不要将无效认证信息存入安全上下文
- 如果业务需要处理请求同时携带两种认证凭证的场景,可在过滤器逻辑中增加判断,定义优先级规则,比如优先使用Cookie认证、校验两种凭证的身份一致性等
内容的提问来源于stack exchange,提问作者spraff
相关产品推荐
相关产品推荐

