Spring Boot中跨微服务验证JWT令牌的实现方法
微服务B中验证微服务A生成的JWT令牌(无需UserDetailsService)
因为JWT是自包含令牌,本身携带了签名、有效期和用户断言信息,所以微服务B完全不需要依赖UserDetailsService,只需要做3件事:验证令牌签名合法性、检查令牌是否过期、断言中是否包含指定角色。下面是具体步骤:
1. 获取微服务A的签名公钥
微服务A生成JWT时肯定用了私钥签名,你需要拿到对应的公钥(绝对不能把私钥给到B)。获取方式二选一:
- 直接从A的配置文件中复制公钥内容,粘贴到B的配置文件里
- 让A提供一个公开接口(比如
GET /public-key)返回公钥,B启动时调用这个接口获取并缓存
2. 引入JWT验证依赖(以Spring Boot为例)
如果用Spring Security做资源服务器,直接引入依赖:
<!-- Maven --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-resource-server</artifactId> </dependency>
或者用JJWT库(更灵活):
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency>
3. 配置JWT验证规则
方式一:用Spring Security资源服务器(推荐,开箱即用)
编写配置类,指定公钥和安全拦截规则:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import java.security.interfaces.RSAPublicKey; import java.util.List; import java.util.stream.Collectors; @Configuration @EnableWebSecurity public class SecurityConfig { private final RSAPublicKey publicKey; public SecurityConfig(RSAPublicKey publicKey) { this.publicKey = publicKey; } @Bean public JwtDecoder jwtDecoder() { // 用公钥创建JWT解码器,自动验证签名和有效期 return NimbusJwtDecoder.withPublicKey(publicKey).build(); } protected void configure(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth // 允许POST请求需要指定角色,比如"ADMIN" .requestMatchers(HttpMethod.POST, "/your-api/**").hasAuthority("SCOPE_ADMIN") // 其他请求按需配置 .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt // 自定义角色提取逻辑,适配A生成的JWT角色字段 .jwtAuthenticationConverter(jwt -> { List<String> roles = jwt.getClaimAsStringList("roles"); var authorities = roles.stream() .map(role -> new SimpleGrantedAuthority("SCOPE_" + role)) .collect(Collectors.toList()); return new JwtAuthenticationToken(jwt, authorities); }) )); } }
注意:SCOPE_前缀是Spring Security的默认约定,若A生成的JWT角色无此前缀,需在转换时补充,或修改配置去掉前缀要求。
方式二:用JJWT手动验证(适合自定义场景)
如果不想用Spring Security,可手动写验证逻辑,比如在拦截器或过滤器中:
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import java.security.interfaces.RSAPublicKey; import java.util.List; public class JwtValidator { private final RSAPublicKey publicKey; public JwtValidator(RSAPublicKey publicKey) { this.publicKey = publicKey; } // 验证令牌并检查指定角色 public boolean validateTokenAndRole(String token, String requiredRole) { try { // 自动验证签名、有效期,解析令牌断言 Claims claims = Jwts.parserBuilder() .setSigningKey(publicKey) .build() .parseClaimsJws(token.replace("Bearer ", "")) .getBody(); // 从断言中提取角色列表并检查 List<String> roles = claims.get("roles", List.class); return roles != null && roles.contains(requiredRole); } catch (Exception e) { // 令牌无效、过期、签名错误都会触发异常,直接返回false return false; } } }
之后在POST接口控制器或自定义过滤器中调用此方法,验证不通过则返回401/403状态码。
关键注意事项
- 绝对不能在微服务B中存储A的签名私钥,必须用公钥验证
- 确保JWT角色字段名称与A生成时一致(比如A用的是
roles还是authorities) - JJWT和Spring Security会自动检查令牌有效期(
exp字段),无需手动编写逻辑 - 若A用对称加密(如HS256),需在B中存储相同密钥,但这种方式安全性低于非对称加密(RSA),不推荐在微服务架构中使用
内容的提问来源于stack exchange,提问作者Jessé Monguar
相关产品推荐
相关产品推荐

