Personalizando solicitações de autorização e token com o cliente Spring Security 5.1
1. Visão geral
Às vezes, as APIs do OAuth2 podem divergir um pouco do padrão; nesse caso, precisamos fazer algumas personalizações nas solicitações do OAuth2 padrão.
Spring Security 5.1 fornece suporte para customizar a autorização OAuth2 e solicitações de token.
Neste tutorial, veremos como personalizar os parâmetros de solicitação e tratamento de resposta.
2. Solicitação de autorização personalizada
Primeiro, personalizaremos a solicitação de autorização OAuth2. Podemos modificar parâmetros padrão e adicionar parâmetros extras à solicitação de autorização, conforme necessário.
Para fazer isso,we need to implement our own OAuth2AuthorizationRequestResolver:
public class CustomAuthorizationRequestResolver
implements OAuth2AuthorizationRequestResolver {
private OAuth2AuthorizationRequestResolver defaultResolver;
public CustomAuthorizationRequestResolver(
ClientRegistrationRepository repo, String authorizationRequestBaseUri) {
defaultResolver = new DefaultOAuth2AuthorizationRequestResolver(repo, authorizationRequestBaseUri);
}
// ...
}
Observe que usamosDefaultOAuth2AuthorizationRequestResolver para fornecer a funcionalidade básica.
Também substituiremos os métodosresolve() para adicionar nossa lógica de personalização:
public class CustomAuthorizationRequestResolver
implements OAuth2AuthorizationRequestResolver {
//...
@Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
OAuth2AuthorizationRequest req = defaultResolver.resolve(request);
if(req != null) {
req = customizeAuthorizationRequest(req);
}
return req;
}
@Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String clientRegistrationId) {
OAuth2AuthorizationRequest req = defaultResolver.resolve(request, clientRegistrationId);
if(req != null) {
req = customizeAuthorizationRequest(req);
}
return req;
}
private OAuth2AuthorizationRequest customizeAuthorizationRequest(
OAuth2AuthorizationRequest req) {
// ...
}
}
Adicionaremos nossas personalizações mais tarde, usando nosso métodocustomizeAuthorizationRequest(), conforme discutiremos nas próximas seções.
Depois de implementar nossoOAuth2AuthorizationRequestResolver personalizado, precisamos adicioná-lo à nossa configuração de segurança:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.oauth2Login()
.authorizationEndpoint()
.authorizationRequestResolver(
new CustomAuthorizationRequestResolver(
clientRegistrationRepository(), "/oauth2/authorize-client"))
//...
}
}
Aqui, usamosoauth2Login().authorizationEndpoint().authorizationRequestResolver() para injetar nossoOAuth2AuthorizationRequestResolver. personalizado
3. Customizando Parâmetros Padrão de Solicitação de Autorização
Agora, vamos discutir a personalização real. Podemos modificarOAuth2AuthorizationRequest o quanto quisermos.
Para começar,we can modify a standard parameter for each authorization request.
Podemos, por exemplo, gerar nosso próprio parâmetro“state”:
private OAuth2AuthorizationRequest customizeAuthorizationRequest(
OAuth2AuthorizationRequest req) {
return OAuth2AuthorizationRequest
.from(req).state("xyz").build();
}
4. Parâmetros extras de solicitação de autorização
We can also add extra parameters to our OAuth2AuthorizationRequest usando o métodoadditionalParameters() deOAuth2AuthorizationRequeste passando em umMap:
private OAuth2AuthorizationRequest customizeAuthorizationRequest(
OAuth2AuthorizationRequest req) {
Map extraParams = new HashMap();
extraParams.putAll(req.getAdditionalParameters());
extraParams.put("test", "extra");
return OAuth2AuthorizationRequest
.from(req)
.additionalParameters(extraParams)
.build();
}
Também temos que nos certificar de que incluímos osadditionalParameters antigos antes de adicionarmos os novos.
Vamos ver um exemplo mais prático personalizando a solicitação de autorização usada com o servidor de autorização Okta.
4.1. Pedido de autorização de Okta personalizado
Okta tem parâmetros opcionais extras para solicitação de autorização para fornecer ao usuário mais funcionalidade. Por exemplo,idp que indica o provedor de identidade.
O provedor de identidade é Okta por padrão, mas podemos personalizá-lo usando o parâmetroidp:
private OAuth2AuthorizationRequest customizeOktaReq(OAuth2AuthorizationRequest req) {
Map extraParams = new HashMap();
extraParams.putAll(req.getAdditionalParameters());
extraParams.put("idp", "https://idprovider.com");
return OAuth2AuthorizationRequest
.from(req)
.additionalParameters(extraParams)
.build();
}
5. Pedido de token personalizado
Agora, veremos como personalizar a solicitação de token OAuth2.
Podemos personalizar a solicitação de token personalizandoOAuth2AccessTokenResponseClient.
A implementação padrão paraOAuth2AccessTokenResponseClient éDefaultAuthorizationCodeTokenResponseClient.
Podemos personalizar a própria solicitação de token fornecendo umRequestEntityConverter personalizado e podemos até personalizar o tratamento da resposta de token personalizandoDefaultAuthorizationCodeTokenResponseClientRestOperations:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.tokenEndpoint()
.accessTokenResponseClient(accessTokenResponseClient())
//...
}
@Bean
public OAuth2AccessTokenResponseClient accessTokenResponseClient(){
DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient =
new DefaultAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.setRequestEntityConverter(new CustomRequestEntityConverter());
OAuth2AccessTokenResponseHttpMessageConverter tokenResponseHttpMessageConverter =
new OAuth2AccessTokenResponseHttpMessageConverter();
tokenResponseHttpMessageConverter.setTokenResponseConverter(new CustomTokenResponseConverter());
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
new FormHttpMessageConverter(), tokenResponseHttpMessageConverter));
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
accessTokenResponseClient.setRestOperations(restTemplate);
return accessTokenResponseClient;
}
}
Podemos injetar nosso próprioOAuth2AccessTokenResponseClient usingtokenEndpoint().accessTokenResponseClient().
Para personalizar os parâmetros de solicitação de token, implementaremosCustomRequestEntityConverter. Da mesma forma, para personalizar o tratamento da resposta do token, implementaremosCustomTokenResponseConverter.
DiscutiremosCustomRequestEntityConverter eCustomTokenResponseConverter nas seções a seguir.
6. Parâmetros extras de solicitação de token
Agora, veremos como adicionar parâmetros extras à nossa solicitação de token criando umConverter personalizado:
public class CustomRequestEntityConverter implements
Converter> {
private OAuth2AuthorizationCodeGrantRequestEntityConverter defaultConverter;
public CustomRequestEntityConverter() {
defaultConverter = new OAuth2AuthorizationCodeGrantRequestEntityConverter();
}
@Override
public RequestEntity> convert(OAuth2AuthorizationCodeGrantRequest req) {
RequestEntity> entity = defaultConverter.convert(req);
MultiValueMap params = (MultiValueMap) entity.getBody();
params.add("test2", "extra2");
return new RequestEntity<>(params, entity.getHeaders(),
entity.getMethod(), entity.getUrl());
}
}
NossoConverter transformaOAuth2AuthorizationCodeGrantRequest em aRequestEntity.
Usamos o conversor padrãoOAuth2AuthorizationCodeGrantRequestEntityConverter para fornecer a funcionalidade básica e adicionamos parâmetros extras ao corpo deRequestEntity.
7. Manuseio de resposta de token personalizado
Agora, vamos personalizar o tratamento da resposta do token.
Podemos usar o conversor de resposta de token padrãoOAuth2AccessTokenResponseHttpMessageConverter como ponto de partida.
ImplementaremosCustomTokenResponseConverter para lidar com o parâmetro“scope” de maneira diferente:
public class CustomTokenResponseConverter implements
Converter
O conversor de resposta de token transformaMap emOAuth2AccessTokenResponse.
Neste exemplo, analisamos o parâmetro“scope” como delimitado por vírgulas em vez deString. delimitado por espaço
Vejamos outro exemplo prático, personalizando a resposta do token usando o LinkedIn como um servidor de autorização.
7.1. Tratamento de resposta de token do LinkedIn
Finalmente, vamos ver como lidar com a resposta de tokenLinkedIn. Contém apenasaccess_tokeneexpires_in,, mas também precisamos detoken_type.
Podemos simplesmente implementar nosso próprio conversor de resposta de token e definirtoken_type manualmente:
public class LinkedinTokenResponseConverter
implements Converter
8. Conclusão
Neste artigo, aprendemos como personalizar solicitações de autorização e token do OAuth2 adicionando ou modificando parâmetros de solicitação.
O código-fonte completo dos exemplos está disponívelover on GitHub.