【问题标题】:How to get JWT claims in a Spring Service or Controller如何在 Spring Service 或 Controller 中获取 JWT 声明
【发布时间】:2021-11-13 01:26:34
【问题描述】:

我已经用谷歌搜索了互联网的深度,但在任何地方都找不到合适的答案。如何在 Spring 服务中访问 JWT 中的声明?

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

在我的服务中,我现在需要在 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();
}

【问题讨论】:

    标签: spring spring-boot spring-security jwt


    【解决方案1】:

    @EnableResourceServerspring-security-oauth 的一部分,这是生命的终结,你应该 migrate away 因为它不建议用于新项目。

    查看新的oauth2-resource-server 支持的参考,它应该允许@AuthenticationPrincipal Jwt principal 在您的控制器中正常工作。另外,请参阅this repository'sSecurityConfiguration,或关注video presentation from SpringOne 2021

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

    您还可以选择提供自己的@BeanJwtAuthenticationConverterConverter&lt;Jwt, AbstractAuthenticationToken&gt; 以访问声明并将它们映射到Authentication,如需要时JwtAuthenticationToken

    【讨论】:

      【解决方案2】:

      bfwg/angular-spring-starter 有很好的示例代码

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

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

      TokenAuthenticationFilter 类可以完成这项工作。

      @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);
      }
      

      它使用了一个辅助类

      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 boot版本也有。

      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 是一种标准,因此可能任何一个都可以。

      【讨论】:

      • 谢谢。很快就会试试这个。这是否有效地归结为过滤器这样做: SecurityContextHolder.getContext().setAuthentication(authentication) ?这就是“填充”@AuthenticationPrinciple 的原因吗?
      • 在这种情况下,是的,我相信是的。我还看到了更多安全特定的过滤器接口,它们直接返回AuthenticationToken。 Spring 框架通常有几种方法可以做同样的事情。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-12-27
      • 2016-01-06
      • 2019-12-12
      • 2015-09-17
      • 2017-02-13
      • 2019-01-31
      • 2019-06-22
      相关资源
      最近更新 更多