Spring Security与Angular前端认证持久化故障排查
问题背景
修改Spring Security实现后,Postman可正常登录并访问/test端点,但Angular登录成功后调用该端点返回403 Forbidden,Spring日志显示请求为匿名访问。排查发现Angular请求未携带登录返回的JSESSIONID Cookie,即使添加了设置withCredentials: true的HttpInterceptor,问题仍存在。
相关代码
Spring Security配置
@Override protected void configure(HttpSecurity http) throws Exception { http .cors().and() .csrf().disable() .authorizeRequests() .antMatchers("/login", "/users").permitAll() .anyRequest().fullyAuthenticated() .and() .addFilter(getAuthenticationFilter()) .logout().logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()); }
自定义AuthenticationFilter
private UsernamePasswordAuthenticationFilter getAuthenticationFilter() { UsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter(this.authenticationManager); filter.setAuthenticationSuccessHandler(new HttpStatusReturningAuthenticationSuccessHandler()); filter.setAuthenticationFailureHandler(new HttpStatusReturningAuthenticationFailureHandler()); return filter; }
受保护端点
@GetMapping(path = "/test") public ResponseEntity<ExcelUploadResponse> test() { LOGGER.info("In test"); return new ResponseEntity<>(new ExcelUploadResponse("Test OK!"), HttpStatus.OK); }
Angular AuthInterceptor
@Injectable() export class AuthInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler) { const authRequest = req.clone({ withCredentials: true }); return next.handle(authRequest); } }
测试情况
- Postman:POST
/api/login返回200,响应带JSESSIONID Cookie;后续GET/api/test携带该Cookie,返回200。 - Angular:登录返回200,但GET
/test返回403,请求未携带JSESSIONID,Spring判定为匿名访问。
解决步骤
1. 显式配置CORS允许携带凭证
Spring默认CORS配置未开启凭证允许,需自定义配置:
@Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); // 替换为你的Angular实际域名,比如http://localhost:4200 configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200")); configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); configuration.setAllowedHeaders(Arrays.asList("*")); // 关键:允许跨域携带Cookie configuration.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; }
修改Security配置,使用自定义CORS配置:
@Override protected void configure(HttpSecurity http) throws Exception { http .cors(cors -> cors.configurationSource(corsConfigurationSource())) .csrf().disable() .authorizeRequests() .antMatchers("/login", "/users").permitAll() .anyRequest().fullyAuthenticated() .and() .addFilter(getAuthenticationFilter()) .logout().logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()); }
2. 确认Angular拦截器已正确注册
在AppModule中确保拦截器被添加到提供者数组:
import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { AuthInterceptor } from './auth.interceptor'; @NgModule({ // ...其他模块配置 providers: [ { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } ] }) export class AppModule { }
3. 调整JSESSIONID Cookie的SameSite属性
Spring默认的SameSite属性为Lax,跨域场景下可能无法携带Cookie,可修改为None(HTTPS环境需同时开启Secure属性):
@Configuration public class CookieConfig { @Bean public ServletContextInitializer servletContextInitializer() { return servletContext -> { servletContext.getSessionCookieConfig().setSameSite("None"); // HTTPS环境下开启以下配置 // servletContext.getSessionCookieConfig().setSecure(true); }; } }
4. 校验域名匹配一致性
确保Angular请求的域名和Spring配置的allowedOrigins完全一致,比如Angular运行在http://localhost:4200,就不能只写http://localhost,必须带端口号。
验证方法
修改完成后重启服务,通过浏览器开发者工具(Network标签)检查:
- 登录请求的响应头中,
Set-Cookie是否包含JSESSIONID,且属性符合预期; /test请求的请求头中,Cookie字段是否携带JSESSIONID。
内容的提问来源于stack exchange,提问作者Popy
相关产品推荐
相关产品推荐

