У меня есть локальная оркестрованная среда с использованием облачных компонентов Spring (eureka, zuul и серверы аутентификации). Все эти компоненты реализованы как отдельные автономные службы. Затем у меня появляется растущее число комбинированных сервисов пользовательского интерфейса / ресурсов, в которых все отдельные сервисы имеют собственный пользовательский интерфейс. Пользовательский интерфейс составляется на стороне сервера с использованием шаблонов тимелеафа, но по сути представляет собой одностраничные приложения angularjs, которые запускаются в браузере.
Один сервис Zuul обслуживает все сервисы пользовательского интерфейса / ресурсов. Я аннотировал все службы пользовательского интерфейса / ресурсов @EnableResourceServer
и добавил @EnableOAuth2Sso
на сервер Zuul.
В application.properties для Zuul у меня есть следующие свойства:
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
Обновление: я включил отладку для org.springframework.security.oauth в службе Zuul и получил следующее
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, есть предложения?
Может ли кто-нибудь посоветовать, какую еще информацию мне нужно опубликовать здесь, чтобы выяснить, почему обновление токена не работает. Я новичок в весеннем облаке, и это соглашение о черной магии не очень понятно (я искал и искал примеры того, что, как я думал, будет обычным вариантом использования, но ничего не нашел).
Примечание 2: у меня уже есть следующий компонент на стороне Zuul
@Bean
public OAuth2RestTemplate oauth2RestTemplate(OAuth2ProtectedResourceDetails resource, OAuth2ClientContext context) {
return new OAuth2RestTemplate(resource, context);
}
Следуя совету @AlexK, я также добавил следующий компонент UserDetailsService на стороне Auth.
@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, для решения этой проблемы потребовалось много проб и ошибок, но теперь она работает так, как рекламируется. Мой (отсутствующий) простой bean-компонент UserDetailsService, который был автоматически подключен к конфигурации, как показано в примечании 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;
}