如何在 Spring 服务或控制器中获取 JWT 声明

How to get JWT claims in a Spring Service or Controller

我已经在 Internet 的深处进行了 google 搜索,但在任何地方都找不到合适的答案。如何在 spring 服务中访问 JWT 中的声明?

我们有一个发布 JWT 的独立身份验证服务。我正在构建一个需要使用此 Jwt 的单独 spring 服务。我有用于签署 JWT 的私钥的 public 密钥,并且拼凑了足够多的教程以能够验证 JWT(使用 public 密钥)并允许访问我的控制器想要。

在我的服务中,我现在需要提取 JWT(以及其他)中的 userId 声明,以便我可以用它调用我的数据库等。

https://www.baeldung.com/spring-security-oauth-jwt(第 5.1 节)似乎是最相关的搜索结果:

@GetMapping("/user/info")
public Map<String, Object> getUserInfo(@AuthenticationPrincipal Jwt principal) {
    Map<String, String> map = new Hashtable<String, String>();
    map.put("user_name", principal.getClaimAsString("preferred_username"));
    map.put("organization", principal.getClaimAsString("organization"));
    return Collections.unmodifiableMap(map);
}

然而,当我的代码运行时,主体始终是 null。我假设我需要在某处实现一些其他接口。

我的应用程序中的所有路径都需要身份验证,所以我有:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
    // http.antMatcher("/**").authorizeRequests().anyRequest().permitAll();
    http.csrf().disable()
        .authorizeRequests()
        .antMatchers("/**").authenticated();
}

bfwg/angular-spring-starter

中有很好的示例代码

您必须向 HttpSecurity 配置添加身份验证过滤器:

 .addFilterBefore(new TokenAuthenticationFilter(tokenHelper, jwtUserDetailsService), BasicAuthenticationFilter.class);

TokenAuthenticationFilter class 完成这项工作。

@Override
public void doFilterInternal(
        HttpServletRequest request,
        HttpServletResponse response,
        FilterChain chain
) throws IOException, ServletException {

    String username;
    String authToken = tokenHelper.getToken(request);

    if (authToken != null) {
        // get username from token
        username = tokenHelper.getUsernameFromToken(authToken);
        if (username != null) {
            // get user
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            if (tokenHelper.validateToken(authToken, userDetails)) {
                // create authentication
                TokenBasedAuthentication authentication = new TokenBasedAuthentication(userDetails);
                authentication.setToken(authToken);
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
    }
    chain.doFilter(request, response);
}

并且它使用了一个助手class

public String getToken( HttpServletRequest request ) {
    /**
     *  Getting the token from Authentication header
     *  e.g Bearer your_token
     */
    String authHeader = getAuthHeaderFromHeader( request );
    if ( authHeader != null && authHeader.startsWith("Bearer ")) {
        return authHeader.substring(7);
    }

    return null;
}

public String getUsernameFromToken(String token) {
    String username;
    try {
        final Claims claims = this.getAllClaimsFromToken(token);
        username = claims.getSubject();
    } catch (Exception e) {
        username = null;
    }
    return username;
}

private Claims getAllClaimsFromToken(String token) {
    Claims claims;
    try {
        claims = Jwts.parser()
                .setSigningKey(SECRET)
                .parseClaimsJws(token)
                .getBody();
    } catch (Exception e) {
        claims = null;
    }
    return claims;
}+

重要的库是Jwt库,用于解码和解析JWT。这个使用

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

最新的spring开机版也有。

import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.KeyLengthException;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jwt.SignedJWT;

由于 JWT 是标准,因此可能两者都可以。

@EnableResourceServer 是生命周期结束的 spring-security-oauth 的一部分,您应该 migrate away,因为不建议将其用于新项目。

查看新 oauth2-resource-server support, which should allow @AuthenticationPrincipal Jwt principal to work correctly in your controller. Also, see this repository's SecurityConfiguration, or follow along with the video presentation from SpringOne 2021 的参考资料。

阅读参考文档时,您最感兴趣的是 overriding Spring Boot's configuration 使用您可用的 public 密钥提供您自己的 @Bean of JwtDecoder

您还可以选择提供自己的 JwtAuthenticationConverterConverter<Jwt, AbstractAuthenticationToken> 来访问声明并将它们映射到 Authentication,例如 JwtAuthenticationToken 根据需要。