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

JWT令牌格式异常致403禁止访问及角色授权问题求助

问题排查与解决方案

一、令牌格式异常(Malformed Token)与JsonParseException 问题解决

1. 清除令牌中的控制字符

控制台提示的code 5是不可见控制字符ENQ,大概率是令牌生成或传递过程中混入的:

  • 把生成的JWT复制到纯文本编辑器(如Notepad++),开启「显示所有字符」功能,检查是否存在奇怪的控制符号,删除后重新复制到Postman使用。
  • 排查JWT生成逻辑:确保所有存入Claims的字段(如用户名、角色)都是可打印字符,可以用StringUtils.isPrintable()校验字段值,避免从数据库或配置中读取到带隐藏字符的内容。

2. 修正JWT过滤器的解析逻辑

常见的解析错误点:

  • Bearer前缀处理:确保从Authorization头截取令牌时,正确处理前缀,避免多截/少截空格:
    private String getJwtFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        // 严格匹配前缀,注意大小写和空格
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7).trim(); // 末尾加trim()避免残留空格
        }
        return null;
    }
    
  • Base64URL解码正确性:JWT的Payload采用Base64URL编码,而非普通Base64,确保解析时使用正确的解码方式(推荐用JWT官方库如jjwt的内置方法,不要手动解码)。

二、基于角色的授权不生效(无需@PreAuthorize)

1. 规范Claims中Role字段的格式

  • 存入Claims的角色必须是集合类型(List),而非单个字符串,支持多角色场景:
    // 生成JWT时的Claims设置
    Map<String, Object> claims = new HashMap<>();
    List<String> roles = userDetails.getAuthorities().stream()
            .map(auth -> auth.getAuthority().replace("ROLE_", ""))
            .collect(Collectors.toList());
    claims.put("roles", roles);
    

2. 正确填充Authentication权限

解析JWT后,需将Claims中的角色转换为Spring Security识别的GrantedAuthority对象,并存入SecurityContext:

// 在JWT过滤器的doFilterInternal方法中
Claims claims = tokenProvider.getClaimsFromToken(token);
List<String> roles = claims.get("roles", List.class);

List<GrantedAuthority> authorities = roles.stream()
        // 注意Spring Security默认要求角色前缀为ROLE_,若JWT中未带则需补充
        .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
        .collect(Collectors.toList());

UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
        claims.getSubject(), null, authorities);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

SecurityContextHolder.getContext().setAuthentication(authentication);

3. 配置全局URL-角色映射(替代@PreAuthorize)

在SecurityConfig中直接绑定URL路径与所需角色,无需逐个接口加注解:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    private final JwtAuthenticationFilter jwtAuthenticationFilter;

    public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
        this.jwtAuthenticationFilter = jwtAuthenticationFilter;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/public/**").permitAll() // 公开接口
                        .requestMatchers("/admin/**").hasRole("ADMIN") // 仅ADMIN可访问
                        .requestMatchers("/user/**").hasAnyRole("ADMIN", "USER") // ADMIN/USER均可访问
                        .anyRequest().authenticated() // 其余接口需认证
                )
                .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }
}

三、额外检查项

  • 验证JWT密钥:确保生成与解析JWT使用的是同一密钥,且密钥格式正确(若使用Base64编码的密钥,需确保解码正确)。
  • Postman请求头:检查Authorization头是否只有Bearer + 令牌,无多余空格或特殊字符。

内容的提问来源于stack exchange,提问作者deven parekh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 15:13:21