页面刷新时JSESSIONID Cookie传递异常的修复方案
Spring Boot 3.2 OAuth2 刷新页面触发401授权错误问题
问题背景
按Spring Boot OAuth2官方文档,OAuth2授权后生成的JSESSIONID本应满足基础安全需求,但按下F5刷新页面后,会话Cookie被删除,首次请求触发授权错误,返回401状态码。当前异常处理配置如下:
http.exceptionHandling(exHandler -> exHandler.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)));
客户端(Angular 17)配置
客户端运行在localhost:4200,配置了携带凭证的请求拦截器:
export const authInterceptor: HttpInterceptorFn = (req, next) => { const clonedRequest = req.clone( { withCredentials: true } ); return next(clonedRequest); };
同时配置了错误拦截器,收到401时跳转至登录页:
export const errorInterceptor: HttpInterceptorFn = (req, next) => { const router = inject(Router); return next(req).pipe(catchError(err => { if (err.status === 401) { router.navigate(['/']); } return throwError(err); })); };
错误现象
- 获取JSESSIONID后Angular控制台无报错,但刷新页面后出现以下错误日志:
status: 401, statusText: 'Unknown Error', url: 'http://localhost:8080/api...', ok: false, name: 'HttpErrorResponse', message: 'Http failure response for http://localhost:8080/api...: 401 ', error: null
- 同一请求最终会返回正常结果,浏览器中所有请求状态显示为200,但控制台先收到401错误,再得到正常响应
- 刷新页面时,登录页会瞬间闪显
Spring Boot 3.2 安全配置代码
@Configuration @EnableWebSecurity public class SecurityConfiguration { @Autowired private OAuth2LoginSuccessHandler oauth2LoginSuccessHandler; @Value("${spring.security.cors.allowed-origin}") private String allowedOrigin; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.csrf(AbstractHttpConfigurer::disable); http.cors(cors -> cors.configurationSource(request -> { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.addAllowedOrigin(allowedOrigin); corsConfiguration.addAllowedMethod(CorsConfiguration.ALL); corsConfiguration.addAllowedHeader(CorsConfiguration.ALL); corsConfiguration.setAllowCredentials(true); return corsConfiguration; })); http.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests .requestMatchers(HttpMethod.OPTIONS).permitAll() // CORS 预检请求放行 .anyRequest().authenticated() ); http.oauth2Login(oauth2Login -> { oauth2Login.successHandler(oauth2LoginSuccessHandler); oauth2Login.failureHandler((request, response, exception) -> { request.getSession().setAttribute("error.message", exception.getMessage()); response.setStatus(HttpStatus.UNAUTHORIZED.value()); }); } ); http.exceptionHandling(exHandler -> exHandler.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))); http.httpBasic(AbstractHttpConfigurer::disable); return http.build(); } }
解决方案建议
- 调整Cookie的SameSite属性:默认JSESSIONID的SameSite属性为Lax,跨域场景下刷新页面可能无法携带。需设置为None,并开启Secure(生产环境需HTTPS),在Spring中新增配置:
@Bean public ServletContextInitializer servletContextInitializer() { return servletContext -> { servletContext.getSessionCookieConfig().setSameSite("None"); servletContext.getSessionCookieConfig().setSecure(true); // 本地开发可根据实际情况关闭 }; } - 替换认证入口处理器:当前
HttpStatusEntryPoint直接返回401,未触发OAuth2授权流程。替换为OAuth2AuthenticationEntryPoint,让未认证请求自动触发登录:http.exceptionHandling(exHandler -> exHandler.authenticationEntryPoint(new OAuth2AuthenticationEntryPoint())); - 优化Angular错误拦截逻辑:排除预检请求,避免误跳转登录页:
export const errorInterceptor: HttpInterceptorFn = (req, next) => { const router = inject(Router); return next(req).pipe(catchError(err => { if (err.status === 401 && req.method !== 'OPTIONS' && err.error !== null) { router.navigate(['/']); } return throwError(err); })); }; - 验证会话持久化配置:检查Spring Boot会话超时时间、是否开启会话持久化(如Redis),避免刷新页面时会话丢失。
内容的提问来源于stack exchange,提问作者shmel
相关产品推荐
相关产品推荐

