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
相关产品推荐
相关产品推荐

