Spring cloud Zuul 和 JWT 刷新令牌

Posted

技术标签:

【中文标题】Spring cloud Zuul 和 JWT 刷新令牌【英文标题】:Spring cloud Zuul and JWT refresh token 【发布时间】:2018-01-28 23:29:58 【问题描述】:

我有一个使用 Spring Cloud 组件(eureka、zuul 和一个身份验证服务器)的本地编排环境。这些组件都作为单独的独立服务实现。然后,我拥有越来越多的组合 UI/资源服务,其中各个服务都有自己的 UI。 UI 使用 thymeleaf 模板放在服务器端,但本质上是在浏览器中运行的 angularjs 单页应用程序。

单个 Zuul 服务位于所有 ui/资源服务的前端。我已经注释了所有的 ui/resource 服务@EnableResourceServer 并添加了@EnableOAuth2Sso 到 Zuul 服务器。

在 Zuul 的 application.properties 中,我有以下属性:

security.oauth2.client.accessTokenUri=http://localhost:8771/uaa/oauth/token
security.oauth2.client.userAuthorizationUri=http://localhost:8771/uaa/oauth/authorize
security.oauth2.client.clientId=waharoa
security.oauth2.client.clientSecret=waharoa
security.oauth2.client.preEstablishedRedirectUri=http://localhost:81/login
security.oauth2.client.registeredRedirectUri=http://localhost:81/login
security.oauth2.client.useCurrentUri=false
security.oauth2.resource.jwt.keyValue=-----BEGIN PUBLIC KEY-----[ETC omitted]...

这一切似乎都像宣传的那样工作。我的问题是令牌何时过期。

在身份验证服务器中,我将令牌设置为 60 秒后过期,刷新令牌设置为 12 小时后过期。当令牌过期时,zuul 服务器无法获取新令牌。

在 zuul 服务器上,这会出现在日志中:

BadCredentialsException : 无法获得由 OAuth2TokenRelayFilter.getAccessToken 抛出的有效访问令牌

更新: 我在Zuul服务中开启了org.springframework.security.oauth的调试,得到如下

    17:12:33.279 DEBUG o.s.s.o.c.t.g.c.AuthorizationCodeAccessTokenProvider - Retrieving token from http://localhost:8771/uaa/oauth/token
    17:12:33.289 DEBUG o.s.s.o.c.t.g.c.AuthorizationCodeAccessTokenProvider - Encoding and sending form: grant_type=[refresh_token], refresh_token=[eyJhbGciOiJS[...deleted...]VgGRHGT8OJ2yDfNVvNA]
    17:12:37.279 WARN  o.s.c.n.z.f.post.SendErrorFilter - Error during filtering
[blah blah stacktrace many lines omitted]
Caused by: org.springframework.security.authentication.BadCredentialsException: Cannot obtain valid access token
        at org.springframework.cloud.security.oauth2.proxy.OAuth2TokenRelayFilter.getAccessToken(OAuth2TokenRelayFilter.java:99)
        at org.springframework.cloud.security.oauth2.proxy.OAuth2TokenRelayFilter.run(OAuth2TokenRelayFilter.java:79)
        at com.netflix.zuul.ZuulFilter.runFilter(ZuulFilter.java:112)
        at com.netflix.zuul.FilterProcessor.processZuulFilter(FilterProcessor.java:193)
        ... 106 common frames omitted

在Auth(uaa)服务端可以看到zuul客户端(waharoa)进行身份验证,获取正确用户的详细信息,然后打印:

17:12:37.288 DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed

我认为这意味着身份验证服务器已经完成了它需要做的事情并回复了请求? Zuul 服务上的某些设置似乎不正确,有什么建议吗?

有人可以告知我需要在此处发布的其他信息,以找出令牌刷新不起作用的原因。我是一个spring cloud noob,这个约定黑魔法对我来说不是很清楚(我已经搜索并搜索了我认为是常见用例的示例,但一无所获)。

注意2:我在Zuul端已经有下面的bean了

@Bean
    public OAuth2RestTemplate oauth2RestTemplate(OAuth2ProtectedResourceDetails resource, OAuth2ClientContext context) 
        return new OAuth2RestTemplate(resource, context);
    

按照@AlexK 的建议,我还在 Auth 端添加了以下 UserDetailsS​​ervice Bean

@Bean
    @Override
    public UserDetailsService userDetailsServiceBean() throws Exception 
        return super.userDetailsServiceBean();
    

并将其添加到我的身份验证服务器配置中

@Autowired
    private UserDetailsService userDetailsService;

@Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 
        endpoints.tokenStore(tokenStore()).tokenEnhancer(jwtTokenEnhancer())
                .authenticationManager(authenticationManager).userDetailsService(userDetailsService)
            .reuseRefreshTokens(false);

但结果相同。 refresh_token 发生了,但是当响应到达 Zuul 过滤器时它似乎仍然消失了。

注 3:

@AlexK 实际上是正确的。我发现在刷新令牌时,它不仅仅是从令牌存储中刷新,它需要调用底层 UserDetailsS​​ervice 以再次获取用户详细信息。当我从 Active Directory 获取详细信息时,这需要经过大量试验和错误才能解决,但现在可以像宣传的那样工作。我的(缺失的)简单 UserDetailsS​​ervice bean 自动装配到配置中,如 Note 2 所示:

@Bean(name = "ldapUserDetailsService")
public UserDetailsService userDetailsService() 
    FilterBasedLdapUserSearch userSearch = new FilterBasedLdapUserSearch(searchBase, "(sAMAccountName=0)",
            contextSource());
    LdapUserDetailsService result = new LdapUserDetailsService(userSearch);
    result.setUserDetailsMapper(new InetOrgPersonContextMapper());
    return result;

【问题讨论】:

【参考方案1】:

我认为所有必要的线索都在this Q 和 A

简而言之:

    有问题的线索 - 有必要在 Zuul/UIApp 端实现 OAuth2RestTemplate。正如Spring Boot reference 中所说,它不是默认创建的 另一部分在该答案中 - 有必要在 OAuth 服务器端进行某些修改

之后,您的 access_token 会由 refresh_token 自动刷新。

附: 但是,当您 refresh_token 令牌过期时,您仍然会得到相同的错误!要处理它,您可以在获得新 access_token 的同时自动更新 refresh_token。在auth-server代码的AuthorizationServerEndpointsConfigurer配置中使用reuseRefreshTokens(false):

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
        throws Exception 
    endpoints
        .authenticationManager(authenticationManager)
        .userDetailsService(userDetailsService)
        .reuseRefreshTokens(false); // <--that's the key to get new refresh_token at the same time as new access_token

更详尽的解释here

【讨论】:

感谢您的回复。我已经在 Zuul 端有了 OAuth2RestTemplate。我确实注意到来自身份验证的信息说没有 UserDetailsS​​ervice 所以我根据您的链接添加了它。不幸的是,我仍然得到相同的结果。 refresh)token 过程似乎工作正常,但 Zuul 过滤器仍然抛出异常。我仍然不知道为什么会这样。 你很准。我的 UserDetailsS​​ervice 出现问题。 恭喜!也许它可能对未来的读者有用,以了解您的 UserDetailsS​​ervice 遇到了什么样的问题。我也有兴趣 我在问题中添加了一个更新说明,以包含我必须添加以连接 Active Directory 的代码。再次感谢。

以上是关于Spring cloud Zuul 和 JWT 刷新令牌的主要内容,如果未能解决你的问题,请参考以下文章

Spring Cloud Zuul API 网关不会为无状态会话转发 JWT 令牌

Spring Cloud Eureka Netflix zuul 过滤器

Spring Cloud Zuul 作为 API 网关

最全面的改造Zuul网关为Spring Cloud Gateway(包含Zuul核心实现和Spring Cloud Gateway核心实现)

7Spring -Cloud-路由网管Spring Cloud Zuul

如何在 Spring-Cloud 中将 ConsulDiscoveryClient 与 Zuul 和 Sidecar 一起使用