Spring 云 Zuul 和 JWT 刷新令牌
Spring cloud Zuul and JWT refresh token
我有一个使用 spring 云组件(eureka、zuul 和 auth 服务器)的本地编排环境。这些组件都作为单独的独立服务实现。然后我有越来越多的组合 UI/resource 服务,其中各个服务都有自己的 UI。 UI 使用 thymeleaf 模板放在服务器端,但本质上是 angularjs 浏览器中 运行 的单页应用程序。
一个 Zuul 服务面向所有 ui/resource 服务。我已经注释了所有 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]...
这一切似乎都像宣传的那样有效。我的问题是令牌何时过期。
在 Auth 服务器中,我将令牌设置为 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 服务似乎设置不正确,有什么建议吗?
有人可以告诉我还需要哪些其他信息 post 才能弄清楚为什么令牌刷新不起作用。我是一个 spring 云菜鸟,这个约定黑魔法对我来说不是很清楚(我已经搜索并搜索了我认为是常见用例但一无所获的示例)。
注2:我在Zuul端已经有了下面的bean
@Bean
public OAuth2RestTemplate oauth2RestTemplate(OAuth2ProtectedResourceDetails resource, OAuth2ClientContext context) {
return new OAuth2RestTemplate(resource, context);
}
根据@AlexK 的建议,我还在 Auth 端添加了以下 UserDetailsService 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 实际上是对的。我发现,当令牌被刷新时,它不仅仅是从令牌存储中刷新,它需要调用底层的 UserDetailsService 来再次获取用户详细信息。当我从 Active Directory 获取详细信息时,这需要大量的试验和错误才能解决,但现在可以像宣传的那样工作。我的(丢失的)简单 UserDetailsService bean 已自动连接到配置中,如 注 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 Q 和 A
简而言之:
- 有问题的线索 - 有必要在您的 Zuul/UIApp 端实施 OAuth2RestTemplate。正如 Spring Boot reference 中所说,它不是默认创建的
- 另一部分在该答案中 - 有必要在 OAuth 服务器端进行某些修改
之后,refresh_token 会自动刷新您的 access_token。
P.S。
但是当你 refresh_token 令牌过期时,你仍然会得到同样的错误!要解决这个问题,您可以让您的 refresh_token 在您获得新的 access_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
}
解释更透彻
我有一个使用 spring 云组件(eureka、zuul 和 auth 服务器)的本地编排环境。这些组件都作为单独的独立服务实现。然后我有越来越多的组合 UI/resource 服务,其中各个服务都有自己的 UI。 UI 使用 thymeleaf 模板放在服务器端,但本质上是 angularjs 浏览器中 运行 的单页应用程序。
一个 Zuul 服务面向所有 ui/resource 服务。我已经注释了所有 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]...
这一切似乎都像宣传的那样有效。我的问题是令牌何时过期。
在 Auth 服务器中,我将令牌设置为 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 服务似乎设置不正确,有什么建议吗?
有人可以告诉我还需要哪些其他信息 post 才能弄清楚为什么令牌刷新不起作用。我是一个 spring 云菜鸟,这个约定黑魔法对我来说不是很清楚(我已经搜索并搜索了我认为是常见用例但一无所获的示例)。
注2:我在Zuul端已经有了下面的bean
@Bean
public OAuth2RestTemplate oauth2RestTemplate(OAuth2ProtectedResourceDetails resource, OAuth2ClientContext context) {
return new OAuth2RestTemplate(resource, context);
}
根据@AlexK 的建议,我还在 Auth 端添加了以下 UserDetailsService 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 实际上是对的。我发现,当令牌被刷新时,它不仅仅是从令牌存储中刷新,它需要调用底层的 UserDetailsService 来再次获取用户详细信息。当我从 Active Directory 获取详细信息时,这需要大量的试验和错误才能解决,但现在可以像宣传的那样工作。我的(丢失的)简单 UserDetailsService bean 已自动连接到配置中,如 注 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 Q 和 A
简而言之:
- 有问题的线索 - 有必要在您的 Zuul/UIApp 端实施 OAuth2RestTemplate。正如 Spring Boot reference 中所说,它不是默认创建的
- 另一部分在该答案中 - 有必要在 OAuth 服务器端进行某些修改
之后,refresh_token 会自动刷新您的 access_token。
P.S。 但是当你 refresh_token 令牌过期时,你仍然会得到同样的错误!要解决这个问题,您可以让您的 refresh_token 在您获得新的 access_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
}
解释更透彻