SpringBoot+SPA前后端分离下如何持久化登录状态?
我使用SpringBoot作为后端、SPA作为前端,希望登录成功后记住当前用户的userId与权限,通过访问/current接口获取用户信息。我的Controller与SecurityConfig代码如下:
Controller代码
public RespResult<Object> login() { List<SimpleGrantedAuthority> permissions = new ArrayList<>(); permissions.add(new SimpleGrantedAuthority("admin")); Long userId = (long) 122; SecurityContext context = SecurityContextHolder.createEmptyContext(); Authentication authentication = new UsernamePasswordAuthenticationToken(userId, null, permissions); context.setAuthentication(authentication); SecurityContextHolder.setContext(context); return new RespResult<Object>(200, "", null); } @GetMapping("/current") public RespResult<Object> getCurrentUseer() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); Long userId = Long.parseLong(authentication.getPrincipal().toString()); System.out.println(userId); return new RespResult<Object>(200, "", null); }
SecurityConfig代码
@EnableWebSecurity @Configuration(proxyBeanMethods = false) public class DefaultSecurityConfig { @Bean public WebSecurityCustomizer webSecurityCustomizer() { String[] antMatchersAnonymous = {"/public/**", "/assets/**", "/webjars/**"}; return web -> web.ignoring() .requestMatchers(antMatchersAnonymous) .requestMatchers(HttpMethod.OPTIONS); } @Bean public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { http.csrf().disable(); return http.build(); } }
目前我可以正常访问登录控制器,但发现SpringBoot未向前端设置Cookie,导致无法记住当前用户的userId,请问该如何解决?
问题核心是你手动设置的SecurityContext仅存在于当前请求线程,未持久化到会话或Cookie,导致后续请求无法获取用户信息。需配置Spring Security的会话管理,让它自动维护登录状态并生成会话Cookie。
1. 启用会话管理配置
修改DefaultSecurityConfig中的defaultSecurityFilterChain方法,添加会话管理策略:
@Bean public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { http.csrf().disable() .sessionManagement(session -> session // 按需创建会话,登录成功后自动生成JSESSIONID Cookie .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) ); return http.build(); }
SessionCreationPolicy.IF_REQUIRED为默认策略,当认证成功后,Spring Security会自动将SecurityContext存入会话,并向客户端发送JSESSIONID Cookie。
2. 规范登录接口实现
无需手动操作SecurityContextHolder,改用AuthenticationManager完成认证,让Spring Security自动维护会话:
@Autowired private AuthenticationManager authenticationManager; public RespResult<Object> login() { List<SimpleGrantedAuthority> permissions = new ArrayList<>(); permissions.add(new SimpleGrantedAuthority("admin")); Long userId = 122L; // 通过AuthenticationManager完成认证 Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(userId, null, permissions) ); // 认证成功后,Spring Security自动处理会话与Cookie return new RespResult<>(200, "登录成功", null); }
如果是从数据库查询用户权限的真实场景,这种方式更符合Spring Security规范,也能自动触发会话创建。
3. 跨域场景配置(SPA与后端跨域时)
若前端SPA和后端部署在不同域名下,需配置CORS允许携带Cookie:
后端CORS配置
@Bean public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { http.csrf().disable() .cors(cors -> cors.configurationSource(corsConfigurationSource())) .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) ); return http.build(); } @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); // 替换为你的前端域名,例如http://localhost:3000 config.setAllowedOrigins(Collections.singletonList("http://localhost:3000")); config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowedHeaders(Collections.singletonList("*")); // 允许携带Cookie config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return source; }
前端请求配置
发起请求时需开启withCredentials,以Axios为例:
axios.get('/current', { withCredentials: true })
4. 验证效果
登录成功后,查看浏览器Cookie列表,应存在JSESSIONID。此时访问/current接口,即可从SecurityContextHolder中获取到正确的用户ID与权限。
内容的提问来源于stack exchange,提问作者hezf

