Spring Rest API授权配置遇403错误,求解决方案
问题分析与解决方案
你的核心问题是:REST接口场景下,登录请求中设置的SecurityContext仅在当前请求线程有效,后续请求无法继承认证信息,导致Spring Security判定未认证并返回403。以下是两种针对性解决方案:
方案1:启用会话管理(简单场景适用)
Spring Security默认支持会话认证,但需确保后续请求携带会话Cookie。
修改安全配置类
显式配置会话策略(默认即为IF_REQUIRED,显式配置更清晰):
@Configuration @EnableWebSecurity public class SecurityConfig { @Autowired UserDetailsService userDetailsService; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http .authorizeHttpRequests(auth -> auth .requestMatchers("/api/auth/**").permitAll() .requestMatchers("/api/image/**").authenticated() ) .csrf(csrf -> csrf.disable()) .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) ) .build(); } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception { return configuration.getAuthenticationManager(); } // 确保密码编码器Bean存在(如果之前没配置) @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
测试注意事项
在Postman中完成登录请求后,确保后续图片接口请求自动携带JSESSIONID Cookie(Postman默认会保存会话Cookie,可在请求的Cookies标签中确认)。
方案2:使用JWT令牌(无状态场景推荐)
REST架构通常推荐无状态认证,JWT是主流实现方式。
步骤1:添加JWT依赖(Maven)
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency>
步骤2:编写JWT工具类
@Component public class JwtUtil { // 生产环境建议从配置文件读取密钥,避免硬编码 private final String SECRET_KEY = "your-secure-secret-key"; // 令牌有效期:1天 private final long EXPIRATION_TIME = 86400000; public String generateToken(UserDetails userDetails) { return Jwts.builder() .setSubject(userDetails.getUsername()) .setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); } public String extractUsername(String token) { return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getSubject(); } public boolean validateToken(String token, UserDetails userDetails) { final String username = extractUsername(token); return username.equals(userDetails.getUsername()) && !isTokenExpired(token); } private boolean isTokenExpired(String token) { return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getExpiration().before(new Date()); } }
步骤3:编写JWT认证过滤器
@Component public class JwtAuthenticationFilter extends OncePerRequestFilter { @Autowired private JwtUtil jwtUtil; @Autowired private UserDetailsService userDetailsService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { String authHeader = request.getHeader("Authorization"); String jwt = null; String username = null; if (authHeader != null && authHeader.startsWith("Bearer ")) { jwt = authHeader.substring(7); username = jwtUtil.extractUsername(jwt); } if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = userDetailsService.loadUserByUsername(username); if (jwtUtil.validateToken(jwt, userDetails)) { UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authToken); } } chain.doFilter(request, response); } }
步骤4:修改登录接口返回JWT
@PostMapping("/sign-in") public ResponseEntity<?> authenticateUser(@RequestBody AuthDTO authDTO){ Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(authDTO.getUsername(),authDTO.getPassword())); SecurityContextHolder.getContext().setAuthentication(authentication); UserDetails userDetails = userService.loadUserByUsername(authentication.getName()); String jwtToken = jwtUtil.generateToken(userDetails); return ResponseEntity.ok(jwtToken); }
步骤5:更新安全配置类
@Configuration @EnableWebSecurity public class SecurityConfig { @Autowired UserDetailsService userDetailsService; @Autowired JwtAuthenticationFilter jwtAuthenticationFilter; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http .authorizeHttpRequests(auth -> auth .requestMatchers("/api/auth/**").permitAll() .requestMatchers("/api/image/**").authenticated() ) .csrf(csrf -> csrf.disable()) .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 禁用会话,实现无状态认证 ) .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) .build(); } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception { return configuration.getAuthenticationManager(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
测试注意事项
登录后获取JWT令牌,在图片接口请求头中添加:
Authorization: Bearer <你的JWT令牌>
内容的提问来源于stack exchange,提问作者Tachank.a
相关产品推荐
相关产品推荐

