Authorization Server oauth2-server 学习笔记 001
该文章不讲解详细的过程,只贴代码,该文章的代码实现了通过feign调用资源服务提供的(http)接口的调用
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/ # 一定要和 认证服务的元数据配置一致
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;
}
}
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);
}
}
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);
}
}
}
}
}
我测了 没问题,哈哈哈哈,