前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用 Spring Security 5.1 客户端自定义授权和令牌请求

使用 Spring Security 5.1 客户端自定义授权和令牌请求

作者头像
用户1516716
发布2018-12-25 16:12:29
4.3K0
发布2018-12-25 16:12:29
举报
文章被收录于专栏:A周立SpringCloudA周立SpringCloud

原文链接:https://www.baeldung.com/spring-security-custom-oauth-requests

作者:baeldung

译者:Darren Luo

1. 概述

有时 OAuth2 API 可能与标准有一些不同,在这种情况家,我们需要对标准 OAuth2 请求进行一些自定义。

Spring Security 5.1 支持自定义 OAuth2 授权和令牌请求。

在本教程,我们将了解人如何自定义请求参数和相应处理。

2. 自定义授权请求

首先,我们自定义 OAuth2 授权请求。我们可以根据需要修改标准参数并添加额外的参数到授权请求中。

为此,我们需要实现我们自己的 OAuth2AuthorizationRequestResolver

代码语言:javascript
复制
public class CustomAuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver {    private OAuth2AuthorizationRequestResolver defaultResolver;    public CustomAuthorizationRequestResolver(
      ClientRegistrationRepository repo, String authorizationRequestBaseUri) {
        defaultResolver = new DefaultOAuth2AuthorizationRequestResolver(repo, authorizationRequestBaseUri);
    }    // ...}

请注意,我们使用 DefaultOAuth2AuthorizationRequestResolver 来提供基本功能。

我们还将覆盖 resolve() 方法来添加我们自定义逻辑:

代码语言:javascript
复制
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) {        // ...
    }

}

我们稍后将使用我们的 customizeAuthorizationRequest() 方法添加我们的自定义,我们将在下一节中讨论。

实现我们的自定义 OAuth2AuthorizationRequestResolver 后,我们需要将其添加到我们的安全配置:

代码语言:javascript
复制
@Configurationpublic class SecurityConfig extends WebSecurityConfigurerAdapter {    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.oauth2Login()
          .authorizationEndpoint()
          .authorizationRequestResolver(            new CustomAuthorizationRequestResolver(
              clientRegistrationRepository(), "/oauth2/authorize-client"))        //...
    }
}

这里我们使用 oauth2Login().authorizationEndpoint().authorizationRequestResolver() 来注入我们的自定义OAuth2AuthorizationRequestResolver

3. 自定义授权请求标准参数

现在,我们来讨论世纪的自定义。我们可以根据需要修改 OAuth2AuthorizationRequest

对于初学者,我们可以修改每个授权请求的标准参数。

例如,我们可以生成我们自己的“state”参数:

代码语言:javascript
复制
private OAuth2AuthorizationRequest customizeAuthorizationRequest(
  OAuth2AuthorizationRequest req) {    return OAuth2AuthorizationRequest
      .from(req).state("xyz").build();
}

4. 授权请求额外的参数

我们也可以添加额外的参数到我们的 OAuth2AuthorizationRequest ,使用 OAuth2AuthorizationRequestadditionalParameters() 方法并传入一个 Map:

代码语言:javascript
复制
private OAuth2AuthorizationRequest customizeAuthorizationRequest(
  OAuth2AuthorizationRequest req) {
    Map<String,Object> extraParams = new HashMap<String,Object>();
    extraParams.putAll(req.getAdditionalParameters()); 
    extraParams.put("test", "extra");    return OAuth2AuthorizationRequest
      .from(req)
      .additionalParameters(extraParams)
      .build();
}

我们添加我们新的参数之前,还必须保证我们包含旧的 additionalParameters

让我们通过为 Okta 授权服务自定义授权请求来查看更实际的示例。

4.1. 自定义 Okta 授权请求

Okta 为授权请求提供了额外的可选参数,以便为用户提供更多功能。例如,idp 表明身份提供者。

默认情况下,身份提供者是 Okta,但我们可以使用 idp 参数自定义它:

代码语言:javascript
复制
private OAuth2AuthorizationRequest customizeOktaReq(OAuth2AuthorizationRequest req) {
    Map<String,Object> extraParams = new HashMap<String,Object>();
    extraParams.putAll(req.getAdditionalParameters()); 
    extraParams.put("idp", "https://idprovider.com");    return OAuth2AuthorizationRequest
      .from(req)
      .additionalParameters(extraParams)
      .build();
}

5. 自定义令牌请求

现在,我们将了解如何自定义 OAuth2 令牌请求。

我们可以通过自定义 OAuth2AccessTokenResponseClient 自定义令牌请求。

OAuth2AccessTokenResponseClient 的默认实现是 DefaultAuthorizationCodeTokenResponseClient

我们可以通过提供一个自定义 RequestEntityConverter 来自定义令牌请求本身,我们甚至可以通过自定义 DefaultAuthorizationCodeTokenResponseClient RestOperations 来自定义令牌响应处理:

代码语言:javascript
复制
@Configurationpublic class SecurityConfig extends WebSecurityConfigurerAdapter {    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.tokenEndpoint()
          .accessTokenResponseClient(accessTokenResponseClient())            //...
    }    @Bean
    public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> 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;
    }
}

我们可以注入我们自己的 OAuth2AccessTokenResponseClient using tokenEndpoint().accessTokenResponseClient()

要自定义令牌请求参数,我们将实现 CustomRequestEntityConverter。同样,为了自定义处理令牌响应,我们将实现 CustomTokenResponseConverter

我们将在接下来的部分讨论 CustomRequestEntityConverterCustomTokenResponseConverter

6. 令牌请求额外参数

现在,我们将看到如何通过构建自定义 Converter 来添加额外的参数到我们的令牌请求:

代码语言:javascript
复制
public class CustomRequestEntityConverter implements Converter<OAuth2AuthorizationCodeGrantRequest, RequestEntity<?>> {    private OAuth2AuthorizationCodeGrantRequestEntityConverter defaultConverter;    public CustomRequestEntityConverter() {
        defaultConverter = new OAuth2AuthorizationCodeGrantRequestEntityConverter();
    }    @Override
    public RequestEntity<?> convert(OAuth2AuthorizationCodeGrantRequest req) {
        RequestEntity<?> entity = defaultConverter.convert(req);
        MultiValueMap<String, String> params = (MultiValueMap<String,String>) entity.getBody();
        params.add("test2", "extra2");        return new RequestEntity<>(params, entity.getHeaders(), 
          entity.getMethod(), entity.getUrl());
    }

}

我们的 ConverterOAuth2AuthorizationCodeGrantRequest 转换为 RequestEntity

我们使用默认转换器 OAuth2AuthorizationCodeGrantRequestEntityConverter 来提供基本功能,并向 RequestEntity 添加了额外的参数。

7. 自定义令牌响应处理

现在,我们将自定义处理令牌响应。

我们可以使用默认令牌响应转换器 OAuth2AccessTokenResponseHttpMessageConverter 作为起点。

我们将实现 CustomTokenResponseConverter 以不同方式处理“scope”参数:

代码语言:javascript
复制
public class CustomTokenResponseConverter implements Converter<Map<String, String>, OAuth2AccessTokenResponse> {    private static final Set<String> TOKEN_RESPONSE_PARAMETER_NAMES = Stream.of(
        OAuth2ParameterNames.ACCESS_TOKEN, 
        OAuth2ParameterNames.TOKEN_TYPE, 
        OAuth2ParameterNames.EXPIRES_IN, 
        OAuth2ParameterNames.REFRESH_TOKEN, 
        OAuth2ParameterNames.SCOPE).collect(Collectors.toSet());    @Override
    public OAuth2AccessTokenResponse convert(Map<String, String> tokenResponseParameters) {
        String accessToken = tokenResponseParameters.get(OAuth2ParameterNames.ACCESS_TOKEN);

        Set<String> scopes = Collections.emptySet();        if (tokenResponseParameters.containsKey(OAuth2ParameterNames.SCOPE)) {
            String scope = tokenResponseParameters.get(OAuth2ParameterNames.SCOPE);
            scopes = Arrays.stream(StringUtils.delimitedListToStringArray(scope, ","))
                .collect(Collectors.toSet());
        }        //...
        return OAuth2AccessTokenResponse.withToken(accessToken)
          .tokenType(accessTokenType)
          .expiresIn(expiresIn)
          .scopes(scopes)
          .refreshToken(refreshToken)
          .additionalParameters(additionalParameters)
          .build();
    }

}

令牌响应转换器将 Map 转换为 OAuth2AccessTokenResponse

在此示例中,我们将“scope”参数解析为逗号分割而不是空格风格的 String

让我们查看另一个通过使用 LinkedIn 作为授权服务器自定义令牌响应的示例。

7.1. LinkedIn 令牌响应处理

最后,让我们看看如何处理 LinkedIn 令牌响应。它只包含了 access_tokenexpires_in,但我们还需要 token_type

我们可以简单的实现我们的令牌响应转换器并手动设置 token_type

代码语言:javascript
复制
public class LinkedinTokenResponseConverter 
  implements Converter<Map<String, String>, OAuth2AccessTokenResponse> {    @Override
    public OAuth2AccessTokenResponse convert(Map<String, String> tokenResponseParameters) {
        String accessToken = tokenResponseParameters.get(OAuth2ParameterNames.ACCESS_TOKEN);        long expiresIn = Long.valueOf(tokenResponseParameters.get(OAuth2ParameterNames.EXPIRES_IN));

        OAuth2AccessToken.TokenType accessTokenType = OAuth2AccessToken.TokenType.BEARER;        return OAuth2AccessTokenResponse.withToken(accessToken)
          .tokenType(accessTokenType)
          .expiresIn(expiresIn)
          .build();
    }
}

8. 结论

在本文,我们学习了如何通过添加或修改请求参数来自定义 OAuth2 授权和令牌请求。

GitHub(https://github.com/eugenp/tutorials/tree/master/spring-5-security-oauth) 上提供了这些示例的完整源代码。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-12-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 A周立SpringCloud 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 概述
  • 2. 自定义授权请求
  • 3. 自定义授权请求标准参数
  • 4. 授权请求额外的参数
    • 4.1. 自定义 Okta 授权请求
    • 5. 自定义令牌请求
    • 6. 令牌请求额外参数
    • 7. 自定义令牌响应处理
      • 7.1. LinkedIn 令牌响应处理
      • 8. 结论
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档