【问题标题】:Why is my token being rejected? What is a resource ID? "Invalid token does not contain resource id (oauth2-resource)"为什么我的令牌被拒绝?什么是资源 ID? “无效令牌不包含资源 id (oauth2-resource)”
【发布时间】:2018-06-08 08:16:02
【问题描述】:

我正在尝试为 spring 项目配置 OAuth2。我正在使用我的工作场所提供的共享 UAA (oauth implementation from cloud foundry) 实例(因此我没有尝试创建授权服务器,并且授权服务器与资源服务器是分开的)。前端是一个单页应用程序,它使用隐式授权直接从授权服务器获取令牌。我有 SPA 设置,它在对微服务的每个 Web API 调用中添加 Authorization: Bearer <TOKEN> 标头。

我现在的问题是微服务。

我正在尝试使用此共享授权服务器对微服务进行身份验证。这里我可能有一个误解,买我目前的理解是这些微服务扮演着资源服务器的角色,因为它们承载着 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());
    }
}

现在每当我用Authorization: Bearer &lt;TOKEN&gt; 击中/api/** 时,我都会收到带有此错误的403

{
    "error": "access_denied",
    "error_description": "Invalid token does not contain resource id (oauth2-resource)"
}

所以这是我的问题:

  • 如何配置这些微服务以验证令牌并在控制器方法中插入 Principal 我目前已将其设置在 SPA 拥有并发送令牌的位置,并且我还拥有用于验证的公钥令牌的签名。我还使用jwt.io 测试了令牌,它显示“签名已验证”。
  • 什么是资源 ID?为什么我需要它,为什么它会导致上述错误?那只是春天的事吗??

谢谢!

【问题讨论】:

    标签: java spring oauth-2.0 jwt spring-security-oauth2


    【解决方案1】:

    Spring OAuth 在 JWT 令牌中需要“aud”claim。该声明的值应与您指定 Spring 应用程序的 resourceId 值匹配(如果未指定,则默认为“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);
        }
    

    【讨论】:

    • 注意:授权服务器使用keycloak时,resourceId要设置为client id。非常感谢您的回答!!!!!!!
    • 以上评论就是这个问题的答案。
    • 这个答案没有回答第二个问题What is a resource id? Why do I need it and why does it cause the error above? Is that a Spring only thing??。我找不到关于 resource id 一词的任何信息。我不知道为什么需要这个,我的客户端(前端 SPA)只发送访问令牌(不是 JWT,只是纯随机文本)。
    • 它对我有用,我只需要确保 aud 值(oauth_client_details 表的resource_ids 值)必须与 resources.resourceId(value_here) 匹配。谢谢
    • 这对我不起作用。我的代码中已经有 resources.resourceId 调用,但不起作用。
    【解决方案2】:

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

    • 授权服务器

         @Override
      public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
      oauthServer.tokenKeyAccess("permitAll()")
                  .checkTokenAccess("isAuthenticated()")
                  .passwordEncoder(oauthClientPasswordEncoder);
      

      }

      /**
      * 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);
       }
      
    • 资源服务器

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

    有了这个配置,我得到了

        {
           "error": "access_denied",
           "error_description": "Invalid token does not contain resource id 
           (xxxxx)"
         }
    

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

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

    【讨论】:

    • 谢谢,我也遇到了同样的问题
    • 如何使用不记名令牌在请求中添加它,我仍然面临同样的问题,我已经添加了这个配置。
    【解决方案3】:

    我在使用 spring 实现 oauth2.0 时遇到了同样的问题,这是我发现的关于 resourceid 的内容。

    Spring Security OAuth2架构分为Authorization Server和Resource Server资源服务器。我们可以为每个 Resource Server(一个微服务实例)设置一个 ResourceID。当 Authorization Server 授权给客户端时,你可以设置客户端可以访问哪些 Resource Server 资源服务。

    在授权服务器中为客户端配置ResourceID的目的是为了限制客户端可以访问的资源服务。

    要设置 ResourceID 参考下面的链接, https://www.fatalerrors.org/a/resource-of-spring-security-oauth2_-id-configuration-and-verification.html

    【讨论】:

      猜你喜欢
      • 2020-04-19
      • 2015-04-26
      • 2015-10-11
      • 2015-06-18
      • 1970-01-01
      • 2017-03-08
      • 1970-01-01
      • 1970-01-01
      • 2020-11-03
      相关资源
      最近更新 更多