Spring Boot多服务JWT认证403权限错误求助
问题排查与修复方案
问题背景
开发包含3个Spring Boot服务的项目:
users服务提供用户注册、登录公开端点,生成JWT Tokenplans服务的多数端点要求Marketing Director角色权限,携带JWT访问时返回403 Forbidden,JWT解析显示角色正确但提示签名无效,所有服务使用相同RSA密钥对
核心问题与修复步骤
1. 自定义JWT权限转换器未生效
Plans服务的SecurityConfig中定义了jwtAuthenticationConverter Bean,但未绑定到OAuth2资源服务器的JWT配置,导致角色转换逻辑未执行。
修复代码(Plans服务SecurityConfig):
@Override protected void configure(HttpSecurity http) throws Exception { // ... 原有配置 ... // 绑定自定义转换器到JWT资源服务器 http.authorizeRequests() // ... 原有授权规则 ... .and().oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter())) ); }
2. JWT角色声明格式不匹配转换器预期
Users服务生成JWT时,将角色用空格拼接为单个字符串存入roles Claim,但默认转换器会把整个字符串当作单一权限,无法正确解析为角色。
修复方案二选一:
方案A:修改Users服务,将角色存入列表
// 替换原scope生成逻辑 final List<String> roles = authentication.getAuthorities().stream() .map(GrantedAuthority::getAuthority) .collect(Collectors.toList()); // 构建JWT声明时传入列表 final JwtClaimsSet claims = JwtClaimsSet.builder() .issuer("example.io") .issuedAt(now) .expiresAt(now.plusSeconds(expiry)) .subject(format("%s,%s", user.getId(), user.getUsername())) .claim("roles", roles) // 用列表替代字符串 .build();
方案B:自定义转换器分割字符串角色
@Bean public JwtAuthenticationConverter jwtAuthenticationConverter() { return new JwtAuthenticationConverter() { @Override protected Collection<GrantedAuthority> extractAuthorities(Jwt jwt) { String rolesString = jwt.getClaimAsString("roles"); if (rolesString == null) { return Collections.emptyList(); } // 分割空格分隔的角色,添加ROLE_前缀 return Arrays.stream(rolesString.split(" ")) .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) .collect(Collectors.toList()); } }; }
3. hasRole方法的权限前缀冲突
Spring Security的hasRole("X")会自动拼接ROLE_前缀,而你的角色包含空格,建议直接使用hasAuthority明确指定完整权限名,避免歧义。
修改Plans服务授权规则:
.antMatchers("/api/plan/**").hasAuthority("ROLE_Marketing Director")
4. 签名无效的验证点
- 确认配置文件中的RSA密钥格式正确,YAML配置需用
|保留换行:jwt: public-key: | -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA... ... -----END PUBLIC KEY----- - 检查Users和Plans服务的密钥配置完全一致,无复制错误或格式丢失。
5. 优化Users服务安全配置(可选)
取消@EnableGlobalMethodSecurity的注释,便于后续方法级权限控制:
@EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true)
内容的提问来源于stack exchange,提问作者Rui Santelmo- 1210861
相关产品推荐
相关产品推荐

