Я реализую клиент веб-приложения OAuth2 с использованием Spring Boot 2.1.3 и Spring Security 5.1.3, который получает токены JWT с сервера авторизации через тип предоставления кода авторизации и вызывает защищенный сервер ресурсов.
Вот как выглядит реализация до сих пор:
Конфигурация безопасности и bean-компонент restTemplate, используемый для вызова защищенного ресурса:
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
.oauth2Login()
.and()
.oauth2Client()
.and().logout().logoutSuccessUrl("/");
}
@Bean
public RestTemplate restTemplate(OAuth2AuthorizedClientService clientService) {
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new AuthorizationHeaderInterceptor(clientService));
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
}
Перехватчик, который добавляет заголовок авторизации (из InMemoryOAuth2AuthorizedClientService платформы) в шаблон restTemplate:
public class AuthorizationHeaderInterceptor implements ClientHttpRequestInterceptor {
private OAuth2AuthorizedClientService clientService;
public AuthorizationHeaderInterceptor(OAuth2AuthorizedClientService clientService) {
this.clientService = clientService;
}
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] bytes, ClientHttpRequestExecution execution) throws IOException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String accessToken = null;
if (authentication != null && authentication.getClass().isAssignableFrom(OAuth2AuthenticationToken.class)) {
OAuth2AuthenticationToken auth = (OAuth2AuthenticationToken) authentication;
String clientRegistrationId = auth.getAuthorizedClientRegistrationId();
OAuth2AuthorizedClient client = clientService.loadAuthorizedClient(clientRegistrationId, auth.getName());
accessToken = client.getAccessToken().getTokenValue();
request.getHeaders().add("Authorization", "Bearer " + accessToken);
}
return execution.execute(request, bytes);
}
}
И контроллер, который вызывает защищенный сервер ресурсов:
@Controller
@RequestMapping("/profile")
public class ProfileController {
@Autowired
private RestTemplate restTemplate;
@Value("${oauth.resourceServerBase}")
private String resourceServerBase;
@GetMapping
public String getProfile(Model model) {
Profile profile = restTemplate.getForEntity(resourceServerBase + "/api/profile/", Profile.class).getBody();
model.addAttribute("profile", profile);
return "profile";
}
}
Конфигурация клиента OAuth2 находится непосредственно в application.yml:
spring:
security:
oauth2:
client:
registration:
auth-server:
client-id: webClient
client-secret: clientSecret
scope: read,write
authorization-grant-type: authorization_code
redirect-uri: http://localhost:8081/client/login/oauth2/code/auth-server
provider:
auth-server:
authorization-uri: http://localhost:8080/auth-server/oauth/authorize
token-uri: http://localhost:8080/auth-server/oauth/token
user-info-uri: http://localhost:8082/resource-server/users/info
user-name-attribute: user_name
После некоторой отладки я заметил, что в конце успешного потока аутентификации через OAuth2LoginAuthtenticationFilter платформа сохраняет полученные маркеры доступа и обновления JWT в модели OAuth2AuthorizedClient в памяти через предоставленный InMemoryOAuth2AuthorizedClientService.
Я пытаюсь выяснить, как переопределить это поведение, чтобы токены могли оставаться доступными после перезапуска сервера. И также держите пользователя вошедшим в систему на основе этого.
Должен ли я просто предоставить собственную реализацию OAuth2AuthorizedClientService? Как я могу настроить Spring Security для его использования? И должна ли эта пользовательская реализация хранить токены в куки?