You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 16:35:34