为什么我的令牌被拒绝了?什么是资源ID?"无效令牌不包含资源ID(oauth2-resource)"

Ric*_*ler 12 java spring oauth-2.0 jwt spring-security-oauth2

我正在尝试为spring项目配置OAuth2.我正在使用我的工作场所提供的共享UAA(来自云代工厂的oauth实现)实例(因此我不打算创建授权服务器,并且授权服务器与资源服务器分开).前端是单页面应用程序,它使用隐式授权直接从授权服务器获取令牌.我有SPA设置,它Authorization: Bearer <TOKEN>在每个Web API调用微服务时添加标题.

我现在的问题是微服务.

我正在尝试使用此共享授权服务器来验证微服务.我可能在这里有一个误解,购买我目前的理解是这些微服务扮演资源服务器的角色,因为它们托管SPA用来获取数据的端点.

所以我尝试配置像这样的微服务:

@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
        .authorizeRequests()
        .antMatchers("/api/**").authenticated();
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setVerifierKey("-----BEGIN PUBLIC KEY-----<key omitted>-----END PUBLIC KEY-----");
        return converter;
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        return defaultTokenServices;
    }


    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
         resources.tokenServices(tokenServices());
    }
}
Run Code Online (Sandbox Code Playgroud)

现在每当我点击/api/**Authorization: Bearer <TOKEN>,我得到一个403有这个错误:

{
    "error": "access_denied",
    "error_description": "Invalid token does not contain resource id (oauth2-resource)"
}
Run Code Online (Sandbox Code Playgroud)

所以这是我的问题:

  • 如何配置这些微服务以验证令牌并插入Principal控制器方法?我目前已经设置了SPA所在的位置并发送令牌,我还有用于验证令牌签名的公钥.我还使用jwt.io测试令牌,并说"Signature Verified".
  • 什么是资源ID?为什么我需要它,为什么会导致上面的错误?那只是一个春天吗?

谢谢!

tso*_*akp 25

Spring OAuth期望在JWT令牌中声明 "aud" .该声明的值应与resourceId您指定的Spring应用程序的值匹配(如果未指定,则默认为"oauth2-resource").

要解决您的问题,您需要:

1)登录您的共享UAA并确保它包含"aud"声明.

2)将"aud"声明的值更改为"oauth2-resource",或者最好在Spring应用更新中resourceId更改为该声明的值,如下所示:

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
         resources.tokenServices(tokenServices());
         resources.resourceId(value from the aud claim you got from UAA server);
    }
Run Code Online (Sandbox Code Playgroud)

  • 这个答案没有回答第二个问题`什么是资源ID?为什么我需要它,为什么会导致上述错误?那是春天唯一的东西吗??`。我找不到有关术语“资源 ID”的任何信息。我不知道为什么我需要这个,我的客户端(前端 SPA)只发送访问令牌(不是 JWT,只是纯随机文本)。 (3认同)
  • 注意:当授权服务器使用keycloak时,resourceId应该设置为客户端id。非常感谢您的回答!!!!!!! (2认同)

小智 5

我添加了一个类似的问题。就我而言,我使用了 jdbc 身份验证,而我的授权服务器和资源服务器是两个独立的 API。

  • 授权服务器

       @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
    oauthServer.tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()")
                .passwordEncoder(oauthClientPasswordEncoder);
    
    Run Code Online (Sandbox Code Playgroud)

    }

    /**
    * Define the client details service. The client may be define either as in memory or in database.
     * Here client with be fetch from the specify database
      */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
       clients.jdbc(dataSource);
    }
    
    /**
    * Define the authorization by providing authentificationManager
    * And the token enhancement
     */
     @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
    endpoints.tokenStore(tokenStore())
                .tokenEnhancer(getTokenEnhancer())
                .authenticationManager(authenticationManager).userDetailsService(userDetailsService);
     }
    
    Run Code Online (Sandbox Code Playgroud)
  • 资源服务器

    public class OAuth2ResourceServerConfig extends 
        ResourceServerConfigurerAdapter {
    
        private TokenExtractor tokenExtractor = new BearerTokenExtractor();
    
        @Autowired
        private DataSource dataSource;
    
        @Bean
        public TokenStore tokenStore() {
          return new JdbcTokenStore(dataSource);
        }
    
         @Override
         public void configure(HttpSecurity http) throws Exception {
               http.addFilterAfter(new OncePerRequestFilter() {
               @Override
               protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                FilterChain filterChain) throws ServletException, IOException {
            // We don't want to allow access to a resource with no token so clear
            // the security context in case it is actually an OAuth2Authentication
            if (tokenExtractor.extract(request) == null) {
                SecurityContextHolder.clearContext();
            }
            filterChain.doFilter(request, response);
        }
    }, AbstractPreAuthenticatedProcessingFilter.class);
    http.csrf().disable();
    http.authorizeRequests().anyRequest().authenticated();
     }
    
      @Bean
      public AccessTokenConverter accessTokenConverter() {
         return new DefaultAccessTokenConverter();
      }
    
      @Bean
      public RemoteTokenServices remoteTokenServices(final @Value("${auth.server.url}") String checkTokenUrl,
        final @Value("${auth.resource.server.clientId}") String clientId,
        final @Value("${auth.resource.server.clientsecret}") String clientSecret) {
    
           final RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
           remoteTokenServices.setCheckTokenEndpointUrl(checkTokenUrl);
           remoteTokenServices.setClientId(clientId);
           remoteTokenServices.setClientSecret(clientSecret);
          remoteTokenServices.setAccessTokenConverter(accessTokenConverter());
    return remoteTokenServices;
       }
    
    Run Code Online (Sandbox Code Playgroud)

有了这个配置,我得到了

    {
       "error": "access_denied",
       "error_description": "Invalid token does not contain resource id 
       (xxxxx)"
     }
Run Code Online (Sandbox Code Playgroud)

为了解决这个问题,我不得不添加

    private String resourceIds= "xxxxx". !! maked sure that this resourceids is store in oauth_client_details for the clientid I used to get the token
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
          resources.resourceId(resourceIds).tokenStore(tokenStore());
      }
Run Code Online (Sandbox Code Playgroud)