spring-boot - Spring Cloud Zuul 和 JWT 刷新 token

标签 spring-boot spring-security jwt netflix-zuul cloudfoundry-uaa

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

单个 Zuul 服务面向所有 ui/resource 服务。我已经注释了所有的ui/资源服务@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]...

这一切似乎都如宣传的那样有效。我的问题是 token 何时到期。

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

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

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

更新:
我在 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 服务上的设置似乎不正确,有什么建议吗?

有人可以告诉我需要在此处发布哪些其他信息以找出 token 刷新不起作用的原因。我是一个 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 实际上就在现场。我发现,当 token 刷新时,它不仅仅是从 token 存储中刷新,它还需要调用底层 UserDetailsS​​ervice 以再次获取用户详细信息。当我从 Active Directory 获取详细信息时,这需要进行大量的反复试验才能解决,但现在正在按宣传的那样工作。我的(丢失的)简单 UserDetailsS​​ervice bean Autowiring 到配置中,如 所示注释 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;
}

最佳答案

我认为所有必要的线索都在this问与答

简而言之:

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

  • 之后,您将通过 refresh_token 自动刷新 access_token。

    附言
    但是当您 refresh_token token 过期时,您仍然会遇到相同的错误!为了解决这个问题,您可以在获得新的 access_token 的同时自动更新您的 refresh_token。在 auth-server 代码的 AuthorizationServerEndpointsConfigurer 配置中使用重用RefreshTokens(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

    关于spring-boot - Spring Cloud Zuul 和 JWT 刷新 token ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45787802/

    相关文章:

    java - @SpringBootTest 使用测试属性(测试数据库)文件运行

    java - Spring 启动。 Bean 依赖于服务

    ruby-on-rails - 使用自定义 token 的 Firebase 身份验证

    reactjs - Flask 和 React - Spotify 授权后处理 token

    java - Hibernate 更新一对多映射中的子条目

    spring-boot - 从命令行参数覆盖 Spring 中的应用程序属性值

    java - 如何将 java cron 表达式转换为 'number of times per day' 值?

    grails - 如何根据拒绝访问的原因使Grails的Spring Security Core呈现不同的页面

    grails - authenticatedUser 为 null 但原则不为 null

    flask - 更改密码时如何防止使用旧的flask-jwt token