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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 00:00:07