JWT Token认证无法访问其他请求,遇403错误求助
JWT认证403错误排查及修复
问题描述
刚接触JWT Authentication,已实现注册和认证环节生成JWT Token,但使用该Token访问其他需要认证的请求时出现403错误,无法定位问题。
相关代码
JwtService.java
public class JwtService { private static final String SECRET_KEY = "This is my secret key"; public String extractUsername(String token) { return extractClaim(token, Claims::getSubject); } public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) { final Claims claims = extractAllClaims(token); return claimsResolver.apply(claims); } // create token depends on user credentials public String generateToken(UserDetails userDetails) { return generateToken(new HashMap<>(), userDetails); } public String generateToken(Map<String, Object> extraClaims, UserDetails userDetails) { return Jwts .builder() .setClaims(extraClaims) .setSubject(userDetails.getUsername()) //check if the token is valid or not .setIssuedAt(new Date(System.currentTimeMillis())) //set how long this token is active .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 24)) .signWith(getSignInKey(),SignatureAlgorithm.HS256) .compact(); } // method to validate token public boolean isTokenValid(String token, UserDetails userDetails) { final String username = extractUsername(token); return (username.equals(userDetails.getUsername())) && isTokenExpired(token); } private boolean isTokenExpired(String token) { return extractExpiration(token).before(new Date()); } private Date extractExpiration(String token) { return extractClaim(token, Claims :: getExpiration); } private Claims extractAllClaims(String token) { return Jwts .parserBuilder() .setSigningKey(getSignInKey()) .build() .parseClaimsJws(token) .getBody(); } private Key getSignInKey() { byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY); return Keys.hmacShaKeyFor(keyBytes); } }
SecurityConfiguration.java
@Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfiguration { private final JwtAuthenticationFilter jwtAuthFilter; private final AuthenticationProvider authenticationProvider; // @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{ http .csrf() .disable() .authorizeHttpRequests() // .antMatchers("/v1/api/**") .antMatchers("/v1/api/auth/**") .permitAll() .anyRequest() .authenticated() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authenticationProvider(authenticationProvider) .addFilterBefore(jwtAuthFilter,UsernamePasswordAuthenticationFilter.class); return http.build(); } }
JwtAuthenticationFilter.java
@Component @RequiredArgsConstructor public class JwtAuthenticationFilter extends OncePerRequestFilter{ private final JwtService jwtService; private final UserDetailsService userDetailsService; @Override protected void doFilterInternal( @NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) throws ServletException, IOException { // TODO Auto-generated method stub final String authHeader = request.getHeader("Authorization"); final String jwt; final String username; if(authHeader == null || !authHeader.startsWith("Bearer ")) { filterChain.doFilter(request, response); return; } jwt = authHeader.substring(7); username = jwtService.extractUsername(jwt); //check if the user is authorized or not if(username !=null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); // check if token is valid or not if(jwtService.isTokenValid(jwt, userDetails)) { UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( username, null,userDetails.getAuthorities()); authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); // update authentication token SecurityContextHolder.getContext().setAuthentication(authToken); } } filterChain.doFilter(request, response); } }
AuthenticationController.java
@RestController @RequestMapping("/v1/api/auth") @RequiredArgsConstructor public class AuthenticationController { @Autowired private final IUserService iUserService; @Autowired private final IRoleService iRoleService; private final JwtService jwtService; private final AuthenticationManager authenticationManager; private final BCryptPasswordEncoder bcrypt; @PostMapping(value = "/authenticate") public ResponseEntity<?> authenticate(@RequestBody LoginDTO dto) { authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(dto.getUsername(), dto.getPassword())); User user = iUserService.findByUsername(dto.getUsername()); String jwtToken = jwtService.generateToken(user); return ResponseEntity.ok().body(jwtToken); } @PostMapping(value = "/register") public ResponseEntity<?> register(@RequestBody RegisterDTO dto) { User user = dto.convertToEntity(); List<Role> roles = new ArrayList<>(); roles.add(iRoleService.findRoleByName("ROLE_CUSTOMER")); user.setRoles(roles); user.setPassword(bcrypt.encode(user.getPassword())); iUserService.registerUser(user); String jwtToken = jwtService.generateToken(user); return ResponseEntity.ok().body(jwtToken); } }
问题排查及修复
1. 核心逻辑错误:Token有效性判断反转
在JwtService的isTokenValid方法中,判断逻辑完全反转:
return (username.equals(userDetails.getUsername())) && isTokenExpired(token);
isTokenExpired(token)返回true代表Token已过期,但代码却将过期Token判定为有效,导致所有未过期的合法Token都被判定无效,无法设置认证上下文,最终返回403。
修复后代码:
public boolean isTokenValid(String token, UserDetails userDetails) { final String username = extractUsername(token); return (username.equals(userDetails.getUsername())) && !isTokenExpired(token); }
2. 次要问题:SECRET_KEY格式不合法
当前使用的SECRET_KEY = "This is my secret key"不是合法的Base64字符串,虽然能生成Token,但解析时可能出现潜在异常。建议生成符合HS256要求的密钥:
- 生成256位(32字节)随机密钥并编码为Base64格式,替换原有SECRET_KEY。
- 生成密钥代码示例:
SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256); String base64Key = Encoders.BASE64.encode(key.getEncoded());
3. 优化建议:认证Token构造规范
在JwtAuthenticationFilter中,构造UsernamePasswordAuthenticationToken时,建议传入完整的userDetails对象而非仅用户名,以便携带完整用户信息:
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities());
内容的提问来源于stack exchange,提问作者CupOfTea00
相关产品推荐
相关产品推荐

