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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 00:13:26