当前位置: 首页 > 工具软件 > OAuth2client > 使用案例 >

oauth2-client 学习笔记 002

于意智
2023-12-01

Authorization Server oauth2-server 学习笔记 001

该文章不讲解详细的过程,只贴代码,该文章的代码实现了通过feign调用资源服务提供的(http)接口的调用

oauth2 client 配置的yml配置

server:
  port: 6005
  servlet:
    context-path: /client/
spring:
  security:
    oauth2:
      client:
        registration:
          test-client:
            client-id: test-client
            client-name: test-client
            client-secret: secret
            redirect-uri: http://127.0.0.1:6005/client/client/detail # 重定向地址
            # redirect-uri: https://www.baidu.com
            authorization-grant-type: authorization_code
            client-authentication-method: client_secret_basic
            scope: openid,message.read,message.write
        provider:
          test-client:
            issuer-uri: http://127.0.0.1:6004/server/ # 一定要和 认证服务的元数据配置一致

Oauth2ClientConfig


import lombok.extern.slf4j.Slf4j;
import org.apache.http.impl.client.HttpClients;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;
import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.DefaultClientCredentialsTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.DefaultRefreshTokenTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequestEntityConverter;
import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequestEntityConverter;
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
import org.springframework.security.oauth2.client.oidc.authentication.OidcIdTokenDecoderFactory;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.Arrays;

/**
 * @author fusheng.zhang
 * @date 2021-12-04 20:43:18
 */
@Slf4j
@Configuration
public class Oauth2ClientConfig {

    @Bean
    public RestTemplate oauth2ClientRestTemplate(RestTemplateBuilder restTemplateBuilder) {
        log.debug("oauth2 client: init oauth2 client restTemplate");
        return restTemplateBuilder
                .requestFactory(() -> new HttpComponentsClientHttpRequestFactory(HttpClients.custom().build()))
                .messageConverters(Arrays.asList(new FormHttpMessageConverter(), new OAuth2AccessTokenResponseHttpMessageConverter()))
                .errorHandler(new OAuth2ErrorResponseErrorHandler()).build();
    }

    @Bean
    public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeTokenResponseClient(
            RestTemplate oauth2ClientRestTemplate) {
        log.debug("oauth2 client init OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest>");
        DefaultAuthorizationCodeTokenResponseClient defaultAuthorizationCodeTokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();
        defaultAuthorizationCodeTokenResponseClient.setRestOperations(oauth2ClientRestTemplate);
        return defaultAuthorizationCodeTokenResponseClient;
    }

    /**
     * 放开对{@code redirect_uri}的访问,否则会出现{@code 403},授权服务器需要回调该地址
     * @param http
     * @return
     * @throws Exception
     */
    @Bean
    public SecurityFilterChain securityFilterChain(
            HttpSecurity http,
            OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeTokenResponseClient) throws Exception {
        log.debug("oauth2 client: init default security filter chain");
        http.authorizeRequests()
                .mvcMatchers("/client/**").anonymous()
                .anyRequest().authenticated();
        http.oauth2Login(oauth2Login -> oauth2Login.tokenEndpoint(
                        tokenEndpoint -> tokenEndpoint.accessTokenResponseClient(authorizationCodeTokenResponseClient)))
                .oauth2Client(oauth2Client -> oauth2Client.authorizationCodeGrant(
                        authorizationCodeGrant -> authorizationCodeGrant.accessTokenResponseClient(authorizationCodeTokenResponseClient)));
        return http.build();
    }

    @Bean
    public JwtDecoderFactory<ClientRegistration> jwtDecoderFactory() {
        return new OidcIdTokenDecoderFactory();
    }

    @Bean
    @SuppressWarnings("DuplicatedCode")
    public OAuth2AuthorizedClientManager authorizedClientManager(
            RestTemplate oauth2ClientRestTemplate,
            ClientRegistrationRepository clientRegistrationRepository,
            OAuth2AuthorizedClientRepository authorizedClientRepository) {

        OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
                .authorizationCode()
                .refreshToken(refreshToken -> {
                    DefaultRefreshTokenTokenResponseClient
                            refreshTokenTokenResponseClient = new DefaultRefreshTokenTokenResponseClient();
                    refreshTokenTokenResponseClient.setRestOperations(oauth2ClientRestTemplate);

                    OAuth2RefreshTokenGrantRequestEntityConverter
                            refreshTokenGrantRequestEntityConverter = new OAuth2RefreshTokenGrantRequestEntityConverter();
                    refreshTokenGrantRequestEntityConverter.addParametersConverter(authorizationGrantRequest -> {
                        MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(1);
                        parameters.add(OAuth2ParameterNames.CLIENT_ID, authorizationGrantRequest.getClientRegistration().getClientId());
                        return parameters;
                    });
                    refreshTokenTokenResponseClient.setRequestEntityConverter(refreshTokenGrantRequestEntityConverter);

                    refreshToken.accessTokenResponseClient(refreshTokenTokenResponseClient);
                })
                .clientCredentials(clientCredentials -> {
                    DefaultClientCredentialsTokenResponseClient clientCredentialsTokenResponseClient =
                            new DefaultClientCredentialsTokenResponseClient();
                    clientCredentialsTokenResponseClient.setRestOperations(oauth2ClientRestTemplate);

                    OAuth2ClientCredentialsGrantRequestEntityConverter clientCredentialsGrantRequestEntityConverter =
                            new OAuth2ClientCredentialsGrantRequestEntityConverter();
                    clientCredentialsGrantRequestEntityConverter.addParametersConverter(authorizationGrantRequest -> {
                        MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(1);
                        parameters.add(OAuth2ParameterNames.CLIENT_ID, authorizationGrantRequest.getClientRegistration().getClientId());
                        return parameters;
                    });
                    clientCredentialsTokenResponseClient.setRequestEntityConverter(clientCredentialsGrantRequestEntityConverter);

                    clientCredentials.accessTokenResponseClient(clientCredentialsTokenResponseClient);
                })
                .build();
        DefaultOAuth2AuthorizedClientManager authorizedClientManager =
                new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientRepository);
        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

        return authorizedClientManager;
    }
}

OAuth2AuthorizationException 异常处理


import cn.zhangfusheng.base.response.BaseResponse; // 自定义返回值实体,自行补充
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletResponse;

/**
 * @author fusheng.zhang
 * @date 2021-12-13 15:07:41
 */
@Slf4j
@RestControllerAdvice
public class Oauth2ClientExceptionAdvice {

    /**
     * client authorization required exception
     * @param e
     * @param response
     * @return
     */
    @ExceptionHandler(value = {OAuth2AuthorizationException.class})
    public BaseResponse<Object> clientAuthorizationRequiredExceptionHandler(
            OAuth2AuthorizationException e, HttpServletResponse response) {
        log.error(e.getMessage(), e);
        response.setStatus(BaseResponse.STATUS_401);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        return BaseResponse.error(e.getMessage(), BaseResponse.STATUS_401);
    }
}

feign 的 RequestInterceptor,实现feign调用资源接口

package cn.zhangfusheng.oauth2.client.config;

import cn.zhangfusheng.base.server.core.annotation.bean.Autowired;
import cn.zhangfusheng.base.server.core.constant.BaseConstant;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Objects;

/**
 * @author fusheng.zhang
 * @date 2021-12-12 16:07:08
 */
@Slf4j
@Component
public class OAuth2FeignRequestInterceptor implements RequestInterceptor {

    @Autowired
    private OAuth2AuthorizedClientManager authorizedClientManager;

    @Override
    public void apply(RequestTemplate template) {
        OAuth2AuthorizeRequest oAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("test-client")
                .principal(SecurityContextHolder.getContext().getAuthentication()).build();
        OAuth2AuthorizedClient oAuth2AuthorizedClient = authorizedClientManager.authorize(oAuth2AuthorizeRequest);
        if (Objects.nonNull(oAuth2AuthorizedClient)) {
            OAuth2AccessToken accessToken = oAuth2AuthorizedClient.getAccessToken();
            String authorization = String.format("%s %s", accessToken.getTokenType().getValue(), accessToken.getTokenValue());
            template.header(BaseConstant.GLOBAL_AUTHORIZATION, authorization);
            Instant issuedAt = accessToken.getIssuedAt(), expiresAt = accessToken.getExpiresAt();
            if (Objects.nonNull(expiresAt) &&
                    Instant.now().minus(Duration.of(60, ChronoUnit.SECONDS)).isAfter(expiresAt)) {
                OAuth2RefreshToken refreshToken = oAuth2AuthorizedClient.getRefreshToken();
                if (Objects.nonNull(refreshToken)) {
                    authorization = String.format("%s %s", accessToken.getTokenType().getValue(), refreshToken.getTokenValue());
                    template.header(BaseConstant.GLOBAL_AUTHORIZATION, authorization);
                }
            }
        }
    }
}

测试

我测了 没问题,哈哈哈哈,

 类似资料: