Spring Security:SecurityContextHolder无法保存认证及JWT生成异常
问题与解决方案:Spring Security JWT生成与默认登录端点问题
问题描述
- 自定义的
JwtGeneratorFilter中,通过SecurityContextHolder.getContext().getAuthentication()获取的Authentication始终为null,导致无法生成JWT;查看BasicAuthenticationFilter时,发现其转换请求值为Authentication也返回null,无法定位错误原因。 - 自定义
/login端点的原因:请求Spring Security默认登录端点时未收到401错误。
疑问
- 如何在
BasicAuthenticationFilter中使用请求数据并存入SecurityContextHolder? - 如何正确请求Spring Security默认登录端点?
相关代码
JwtGeneratorFilter代码
public class JwtGeneratorFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null) { SecretKey secretKey = Keys.hmacShaKeyFor(SecurityConstants.JWT_KEY.getBytes(StandardCharsets.UTF_8)); String jwt = Jwts.builder().setIssuer("Demo App").setSubject("Jwt token").claim("userName", authentication.getName()).setIssuedAt(new Date()).setExpiration(new Date((new Date().getTime() + 30000000))).signWith(secretKey).compact(); response.setHeader(SecurityConstants.JWT_HEADER, jwt); } filterChain.doFilter(request, response); } }
SecurityConfig配置代码
@Configuration @EnableWebSecurity(debug = true) public class SecurityConfig { @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception { return configuration.getAuthenticationManager(); } @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); config.setAllowedOrigins(Collections.singletonList("http://localhost:4200")); config.setAllowedMethods(Collections.singletonList("*")); config.setAllowCredentials(true); config.setAllowedHeaders(Collections.singletonList("*")); config.setExposedHeaders(Arrays.asList("Authorization")); config.setMaxAge(3600L); source.registerCorsConfiguration("/**", config); return source; } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.cors(cors -> cors.configurationSource(corsConfigurationSource())); http.csrf(csrf -> csrf.disable()).sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)).authorizeHttpRequests(req -> req.requestMatchers("/auth/**").permitAll().anyRequest().authenticated()); http.formLogin(Customizer.withDefaults()) .httpBasic(Customizer.withDefaults()); http.addFilterAfter(new JwtGeneratorFilter(), BasicAuthenticationFilter.class); return http.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
自定义/login接口代码
@PostMapping("/login") public String login(@RequestBody User user) { Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword())); return user.toString(); }
注:authenticate返回的Authentication对象中authenticated为true,但仍无法生成JWT。
解决方案
BasicAuthenticationFilter的逻辑是从请求头中获取认证信息,而手动调用authenticationManager.authenticate()完成的认证不会触发过滤器流程,因此SecurityContextHolder中不会存入认证信息,导致JwtGeneratorFilter拿不到Authentication。- 初学者可直接沿用默认认证逻辑:将登录信息按Basic Auth格式放在请求头中,此时
BasicAuthenticationFilter会自动处理认证,并将Authentication存入SecurityContextHolder,后续JwtGeneratorFilter就能正常生成JWT。 - 若需自定义登录流程,可按以下步骤实现:
- 在自定义
/login接口中,通过AuthenticationManager完成用户认证。 - 认证通过后,手动将
Authentication对象存入SecurityContextHolder.getContext().setAuthentication(authentication)。 - 直接在接口中生成JWT并返回给前端(无需依赖
JwtGeneratorFilter,或确保过滤器能捕获到此时SecurityContext中的信息)。
- 在自定义
内容的提问来源于stack exchange,提问作者Batuhan
相关产品推荐
相关产品推荐

