mirror of
https://gitee.com/log4j/pig.git
synced 2024-12-22 12:48:58 +08:00
♻️ Refactoring code. 重写token 生成规则
This commit is contained in:
commit
f846556527
@ -16,9 +16,13 @@
|
||||
|
||||
package com.pig4cloud.pig.auth.config;
|
||||
|
||||
import com.nimbusds.jose.jwk.source.JWKSource;
|
||||
import cn.hutool.core.lang.UUID;
|
||||
import com.pig4cloud.pig.auth.support.CustomeOAuth2AccessTokenGenerator;
|
||||
import com.pig4cloud.pig.auth.support.OAuth2ResourceOwnerPasswordAuthenticationConverter;
|
||||
import com.pig4cloud.pig.auth.support.OAuth2ResourceOwnerPasswordAuthenticationProvider;
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pig.common.core.util.WebUtils;
|
||||
import com.pig4cloud.pig.common.security.component.PigDaoAuthenticationProvider;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
@ -26,20 +30,12 @@ import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||
import org.springframework.security.oauth2.core.OAuth2TokenFormat;
|
||||
import org.springframework.security.oauth2.jwt.JwtEncoder;
|
||||
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
||||
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
|
||||
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
|
||||
import org.springframework.security.oauth2.server.authorization.config.TokenSettings;
|
||||
import org.springframework.security.oauth2.server.authorization.token.*;
|
||||
import org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator;
|
||||
import org.springframework.security.oauth2.server.authorization.token.OAuth2RefreshTokenGenerator;
|
||||
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
|
||||
import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter;
|
||||
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeAuthenticationConverter;
|
||||
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ClientCredentialsAuthenticationConverter;
|
||||
@ -51,21 +47,25 @@ import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2022/5/27 认证服务器配置
|
||||
* @date 2022/5/27
|
||||
*
|
||||
* 认证服务器配置
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class AuthorizationServerConfiguration {
|
||||
|
||||
private static final String CUSTOM_CONSENT_PAGE_URI = "/oauth2/login";
|
||||
|
||||
|
||||
/**
|
||||
* 定义 Spring Security 的拦截器链,比如我们的 授权url、获取token的url 需要由那个过滤器来处理,此处配置这个。 1.开放oauth2
|
||||
* 相关地址 2.增加密码模式的扩展 方法 addCustomOAuth2ResourceOwnerPasswordAuthenticationProvider
|
||||
* 定义 Spring Security 的拦截器链,比如我们的 授权url、获取token的url 需要由那个过滤器来处理,此处配置这个。
|
||||
*
|
||||
* 1.开放oauth2 相关地址</br>
|
||||
* 2.增加密码模式的扩展 方法 addCustomOAuth2ResourceOwnerPasswordAuthenticationProvider
|
||||
*/
|
||||
@Bean
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
|
||||
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http,
|
||||
OAuth2AuthorizationService authorizationService) throws Exception {
|
||||
OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer<>();
|
||||
|
||||
http.apply(authorizationServerConfigurer.tokenEndpoint(
|
||||
@ -77,6 +77,8 @@ public class AuthorizationServerConfiguration {
|
||||
authorizationServerConfigurer.authorizationEndpoint(
|
||||
authorizationEndpoint -> authorizationEndpoint.consentPage(CUSTOM_CONSENT_PAGE_URI));
|
||||
|
||||
authorizationServerConfigurer.authorizationService(authorizationService);
|
||||
|
||||
RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();
|
||||
|
||||
http.requestMatcher(endpointsMatcher)
|
||||
@ -85,31 +87,29 @@ public class AuthorizationServerConfiguration {
|
||||
|
||||
SecurityFilterChain securityFilterChain = http.formLogin(Customizer.withDefaults()).build();
|
||||
|
||||
// Custom configuration for Resource Owner Password grant type. Current
|
||||
// implementation has no support for Resource Owner
|
||||
// Password grant type
|
||||
addCustomOAuth2PasswordAuthenticationProvider(http);
|
||||
|
||||
return securityFilterChain;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OAuth2TokenGenerator tokenGenerator(JWKSource jwkSource) {
|
||||
JwtEncoder jwtEncoder = new NimbusJwtEncoder(jwkSource);
|
||||
JwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder);
|
||||
OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();
|
||||
OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();
|
||||
return new DelegatingOAuth2TokenGenerator(jwtGenerator, accessTokenGenerator, refreshTokenGenerator);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ProviderSettings providerSettings() {
|
||||
return ProviderSettings.builder().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 令牌生成实现 username:uuid
|
||||
* @return OAuth2TokenGenerator
|
||||
*/
|
||||
@Bean
|
||||
public OAuth2TokenGenerator oAuth2TokenGenerator() {
|
||||
CustomeOAuth2AccessTokenGenerator tokenGenerator = new CustomeOAuth2AccessTokenGenerator();
|
||||
tokenGenerator.setAccessTokenGenerator(() -> String.format("%s:%s:%s", SecurityConstants.PROJECT_PREFIX,
|
||||
WebUtils.getRequest().get().getParameter(SecurityConstants.USERNAME), UUID.fastUUID()));
|
||||
return new DelegatingOAuth2TokenGenerator(tokenGenerator, new OAuth2RefreshTokenGenerator());
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩展密码模式
|
||||
* 扩展密码模式
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
private void addCustomOAuth2PasswordAuthenticationProvider(HttpSecurity http) {
|
||||
@ -118,10 +118,12 @@ public class AuthorizationServerConfiguration {
|
||||
OAuth2TokenGenerator oAuth2TokenGenerator = http.getSharedObject(OAuth2TokenGenerator.class);
|
||||
OAuth2AuthorizationService authorizationService = http.getSharedObject(OAuth2AuthorizationService.class);
|
||||
|
||||
OAuth2ResourceOwnerPasswordAuthenticationProvider resourceOwnerPasswordAuthenticationProvider =
|
||||
new OAuth2ResourceOwnerPasswordAuthenticationProvider(authenticationManager,authorizationService,oAuth2TokenGenerator);
|
||||
OAuth2ResourceOwnerPasswordAuthenticationProvider resourceOwnerPasswordAuthenticationProvider = new OAuth2ResourceOwnerPasswordAuthenticationProvider(
|
||||
authenticationManager, authorizationService, oAuth2TokenGenerator);
|
||||
|
||||
// This will add new authentication provider in the list of existing authentication providers.
|
||||
// 处理 UsernamePasswordAuthenticationToken
|
||||
http.authenticationProvider(new PigDaoAuthenticationProvider());
|
||||
// 处理 OAuth2ResourceOwnerPasswordAuthenticationToken
|
||||
http.authenticationProvider(resourceOwnerPasswordAuthenticationProvider);
|
||||
|
||||
}
|
||||
|
@ -20,29 +20,32 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
/**
|
||||
* 服务安全相关配置
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2022/1/12 认证相关配置
|
||||
* @date 2022/1/12
|
||||
*/
|
||||
@EnableWebSecurity
|
||||
public class WebSecurityConfiguration {
|
||||
|
||||
// @formatter:off
|
||||
/**
|
||||
* spring security 默认的安全策略
|
||||
* @param http security注入点
|
||||
* @return SecurityFilterChain
|
||||
* @throws Exception
|
||||
*/
|
||||
@Bean
|
||||
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests(authorizeRequests -> authorizeRequests
|
||||
.antMatchers("/oauth/*").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.csrf().disable()
|
||||
.formLogin(Customizer.withDefaults());
|
||||
http.authorizeRequests(
|
||||
// 暴露自定义 的 password 等端点
|
||||
authorizeRequests -> authorizeRequests.antMatchers("/oauth/*").permitAll().anyRequest().authenticated())
|
||||
// 个性化 formLogin
|
||||
.csrf().disable().formLogin(Customizer.withDefaults());
|
||||
|
||||
// http.authenticationProvider(new PigDaoAuthenticationProvider());
|
||||
return http.build();
|
||||
}
|
||||
|
||||
|
@ -1,165 +0,0 @@
|
||||
//package com.pig4cloud.pig.auth.endpoint;
|
||||
//
|
||||
//import cn.hutool.core.util.StrUtil;
|
||||
//import cn.hutool.extra.spring.SpringUtil;
|
||||
//import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
//import com.pig4cloud.pig.common.core.util.R;
|
||||
//import com.pig4cloud.pig.common.security.service.PigUserDetailsService;
|
||||
//import lombok.RequiredArgsConstructor;
|
||||
//import lombok.SneakyThrows;
|
||||
//import org.springframework.core.Ordered;
|
||||
//import org.springframework.http.HttpHeaders;
|
||||
//import org.springframework.http.converter.HttpMessageConverter;
|
||||
//import org.springframework.http.server.ServletServerHttpResponse;
|
||||
//import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
//import org.springframework.security.core.Authentication;
|
||||
//import org.springframework.security.core.userdetails.UserDetails;
|
||||
//import org.springframework.security.oauth2.core.*;
|
||||
//import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
|
||||
//import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
|
||||
//import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
||||
//import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
||||
//import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;
|
||||
//import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
||||
//import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
|
||||
//import org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;
|
||||
//import org.springframework.security.oauth2.server.authorization.token.OAuth2RefreshTokenGenerator;
|
||||
//import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
|
||||
//import org.springframework.security.web.authentication.www.BasicAuthenticationConverter;
|
||||
//import org.springframework.util.CollectionUtils;
|
||||
//import org.springframework.web.bind.annotation.*;
|
||||
//
|
||||
//import javax.servlet.http.HttpServletRequest;
|
||||
//import javax.servlet.http.HttpServletResponse;
|
||||
//import java.io.IOException;
|
||||
//import java.time.temporal.ChronoUnit;
|
||||
//import java.util.Comparator;
|
||||
//import java.util.HashMap;
|
||||
//import java.util.Map;
|
||||
//import java.util.Optional;
|
||||
//
|
||||
///**
|
||||
// * 登录端点
|
||||
// *
|
||||
// * @author lengleng
|
||||
// * @date 2022/5/27
|
||||
// */
|
||||
//@RestController
|
||||
//@RequiredArgsConstructor
|
||||
//@RequestMapping("/oauth")
|
||||
//public class LoginEndpoint {
|
||||
//
|
||||
// private final OAuth2AuthorizationService tokenService;
|
||||
//
|
||||
// private final RegisteredClientRepository registeredClientRepository;
|
||||
//
|
||||
// private final OAuth2TokenGenerator tokenGenerator;
|
||||
//
|
||||
// private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
|
||||
//
|
||||
// private final OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();
|
||||
//
|
||||
// private BasicAuthenticationConverter authenticationConverter = new BasicAuthenticationConverter();
|
||||
//
|
||||
// @SneakyThrows
|
||||
// @PostMapping("/token")
|
||||
// public void login(HttpServletRequest request, HttpServletResponse response, String username, String password) {
|
||||
//
|
||||
// // 获取请求header 中的basic 信息
|
||||
// UsernamePasswordAuthenticationToken clientAuthentication = authenticationConverter.convert(request);
|
||||
// RegisteredClient client = registeredClientRepository.findByClientId(clientAuthentication.getName());
|
||||
//
|
||||
// // 根据用户名查询用户
|
||||
// Map<String, PigUserDetailsService> userDetailsServiceMap = SpringUtil
|
||||
// .getBeansOfType(PigUserDetailsService.class);
|
||||
// Optional<PigUserDetailsService> optional = userDetailsServiceMap.values().stream()
|
||||
// .filter(service -> service.support(client.getClientId(), AuthorizationGrantType.PASSWORD.getValue()))
|
||||
// .max(Comparator.comparingInt(Ordered::getOrder));
|
||||
// UserDetails userDetails = optional.get().loadUserByUsername(username);
|
||||
//
|
||||
// // 生成accessToken
|
||||
// UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails,
|
||||
// null);
|
||||
// DefaultOAuth2TokenContext.Builder builder = DefaultOAuth2TokenContext.builder().registeredClient(client)
|
||||
// .principal(authenticationToken).tokenType(OAuth2TokenType.ACCESS_TOKEN)
|
||||
// .authorizationGrantType(AuthorizationGrantType.PASSWORD);
|
||||
// OAuth2Token generatedAccessToken = this.tokenGenerator.generate(builder.build());
|
||||
// OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
|
||||
// generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(),
|
||||
// generatedAccessToken.getExpiresAt(), builder.build().getAuthorizedScopes());
|
||||
//
|
||||
// OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(client)
|
||||
// .principalName(authenticationToken.getName()).authorizationGrantType(AuthorizationGrantType.PASSWORD);
|
||||
// if (generatedAccessToken instanceof ClaimAccessor) {
|
||||
// authorizationBuilder.token(accessToken,
|
||||
// (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME,
|
||||
// ((ClaimAccessor) generatedAccessToken).getClaims()));
|
||||
// }
|
||||
// else {
|
||||
// authorizationBuilder.accessToken(accessToken);
|
||||
// }
|
||||
//
|
||||
// // 创建刷新令牌
|
||||
// OAuth2Token generatedRefreshToken = this.refreshTokenGenerator
|
||||
// .generate(builder.tokenType(OAuth2TokenType.REFRESH_TOKEN)
|
||||
// .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN).build());
|
||||
// authorizationBuilder.refreshToken((OAuth2RefreshToken) generatedRefreshToken);
|
||||
// OAuth2Authorization authorization = authorizationBuilder.build();
|
||||
//
|
||||
// // 保存认证信息
|
||||
// tokenService.save(authorization);
|
||||
//
|
||||
// // 对外输出
|
||||
// Map<String, Object> additionalParameters = new HashMap<>();
|
||||
// additionalParameters.put("license", "pig");
|
||||
// additionalParameters.put(SecurityConstants.DETAILS_USER, userDetails);
|
||||
// OAuth2AccessTokenAuthenticationToken oAuth2AccessTokenAuthenticationToken = new OAuth2AccessTokenAuthenticationToken(
|
||||
// client, authenticationToken, accessToken, (OAuth2RefreshToken) generatedRefreshToken,
|
||||
// additionalParameters);
|
||||
// sendAccessTokenResponse(response, oAuth2AccessTokenAuthenticationToken);
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @GetMapping("/info")
|
||||
// public OAuth2Authorization info(String token) {
|
||||
// return tokenService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
|
||||
// }
|
||||
//
|
||||
// private void sendAccessTokenResponse(HttpServletResponse response, Authentication authentication)
|
||||
// throws IOException {
|
||||
//
|
||||
// OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) authentication;
|
||||
//
|
||||
// OAuth2AccessToken accessToken = accessTokenAuthentication.getAccessToken();
|
||||
// OAuth2RefreshToken refreshToken = accessTokenAuthentication.getRefreshToken();
|
||||
// Map<String, Object> additionalParameters = accessTokenAuthentication.getAdditionalParameters();
|
||||
//
|
||||
// OAuth2AccessTokenResponse.Builder builder = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())
|
||||
// .tokenType(accessToken.getTokenType()).scopes(accessToken.getScopes());
|
||||
// if (accessToken.getIssuedAt() != null && accessToken.getExpiresAt() != null) {
|
||||
// builder.expiresIn(ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()));
|
||||
// }
|
||||
// if (refreshToken != null) {
|
||||
// builder.refreshToken(refreshToken.getTokenValue());
|
||||
// }
|
||||
// if (!CollectionUtils.isEmpty(additionalParameters)) {
|
||||
// builder.additionalParameters(additionalParameters);
|
||||
// }
|
||||
// OAuth2AccessTokenResponse accessTokenResponse = builder.build();
|
||||
// ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
|
||||
// this.accessTokenHttpResponseConverter.write(accessTokenResponse, null, httpResponse);
|
||||
// }
|
||||
//
|
||||
// @DeleteMapping("/logout")
|
||||
// public R<Boolean> logout(@RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authHeader) {
|
||||
// if (StrUtil.isBlank(authHeader)) {
|
||||
// return R.ok();
|
||||
// }
|
||||
//
|
||||
// String tokenValue = authHeader.replace(OAuth2AccessToken.TokenType.BEARER.getValue(), StrUtil.EMPTY).trim();
|
||||
// OAuth2Authorization oAuth2Authorization = tokenService.findByToken(tokenValue, OAuth2TokenType.ACCESS_TOKEN);
|
||||
// tokenService.remove(oAuth2Authorization);
|
||||
// return R.ok();
|
||||
// }
|
||||
//
|
||||
//}
|
@ -0,0 +1,127 @@
|
||||
package com.pig4cloud.pig.auth.support;
|
||||
|
||||
import lombok.Setter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
|
||||
import org.springframework.security.crypto.keygen.StringKeyGenerator;
|
||||
import org.springframework.security.oauth2.core.ClaimAccessor;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2TokenFormat;
|
||||
import org.springframework.security.oauth2.core.OAuth2TokenType;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
||||
import org.springframework.security.oauth2.server.authorization.token.*;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2022/5/29
|
||||
*/
|
||||
public class CustomeOAuth2AccessTokenGenerator implements OAuth2TokenGenerator<OAuth2AccessToken> {
|
||||
|
||||
@Setter
|
||||
private StringKeyGenerator accessTokenGenerator = new Base64StringKeyGenerator(
|
||||
Base64.getUrlEncoder().withoutPadding(), 96);
|
||||
|
||||
private OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public OAuth2AccessToken generate(OAuth2TokenContext context) {
|
||||
if (!OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType()) || !OAuth2TokenFormat.REFERENCE
|
||||
.equals(context.getRegisteredClient().getTokenSettings().getAccessTokenFormat())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String issuer = null;
|
||||
if (context.getProviderContext() != null) {
|
||||
issuer = context.getProviderContext().getIssuer();
|
||||
}
|
||||
RegisteredClient registeredClient = context.getRegisteredClient();
|
||||
|
||||
Instant issuedAt = Instant.now();
|
||||
Instant expiresAt = issuedAt.plus(registeredClient.getTokenSettings().getAccessTokenTimeToLive());
|
||||
|
||||
// @formatter:off
|
||||
OAuth2TokenClaimsSet.Builder claimsBuilder = OAuth2TokenClaimsSet.builder();
|
||||
if (StringUtils.hasText(issuer)) {
|
||||
claimsBuilder.issuer(issuer);
|
||||
}
|
||||
claimsBuilder
|
||||
.subject(context.getPrincipal().getName())
|
||||
.audience(Collections.singletonList(registeredClient.getClientId()))
|
||||
.issuedAt(issuedAt)
|
||||
.expiresAt(expiresAt)
|
||||
.notBefore(issuedAt)
|
||||
.id(UUID.randomUUID().toString());
|
||||
if (!CollectionUtils.isEmpty(context.getAuthorizedScopes())) {
|
||||
claimsBuilder.claim(OAuth2ParameterNames.SCOPE, context.getAuthorizedScopes());
|
||||
}
|
||||
// @formatter:on
|
||||
|
||||
if (this.accessTokenCustomizer != null) {
|
||||
// @formatter:off
|
||||
OAuth2TokenClaimsContext.Builder accessTokenContextBuilder = OAuth2TokenClaimsContext.with(claimsBuilder)
|
||||
.registeredClient(context.getRegisteredClient())
|
||||
.principal(context.getPrincipal())
|
||||
.providerContext(context.getProviderContext())
|
||||
.authorizedScopes(context.getAuthorizedScopes())
|
||||
.tokenType(context.getTokenType())
|
||||
.authorizationGrantType(context.getAuthorizationGrantType());
|
||||
if (context.getAuthorization() != null) {
|
||||
accessTokenContextBuilder.authorization(context.getAuthorization());
|
||||
}
|
||||
if (context.getAuthorizationGrant() != null) {
|
||||
accessTokenContextBuilder.authorizationGrant(context.getAuthorizationGrant());
|
||||
}
|
||||
// @formatter:on
|
||||
|
||||
OAuth2TokenClaimsContext accessTokenContext = accessTokenContextBuilder.build();
|
||||
this.accessTokenCustomizer.customize(accessTokenContext);
|
||||
}
|
||||
|
||||
OAuth2TokenClaimsSet accessTokenClaimsSet = claimsBuilder.build();
|
||||
|
||||
OAuth2AccessToken accessToken = new CustomeOAuth2AccessTokenGenerator.OAuth2AccessTokenClaims(
|
||||
OAuth2AccessToken.TokenType.BEARER, this.accessTokenGenerator.generateKey(),
|
||||
accessTokenClaimsSet.getIssuedAt(), accessTokenClaimsSet.getExpiresAt(), context.getAuthorizedScopes(),
|
||||
accessTokenClaimsSet.getClaims());
|
||||
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link OAuth2TokenCustomizer} that customizes the
|
||||
* {@link OAuth2TokenClaimsContext#getClaims() claims} for the
|
||||
* {@link OAuth2AccessToken}.
|
||||
* @param accessTokenCustomizer the {@link OAuth2TokenCustomizer} that customizes the
|
||||
* claims for the {@code OAuth2AccessToken}
|
||||
*/
|
||||
public void setAccessTokenCustomizer(OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer) {
|
||||
Assert.notNull(accessTokenCustomizer, "accessTokenCustomizer cannot be null");
|
||||
this.accessTokenCustomizer = accessTokenCustomizer;
|
||||
}
|
||||
|
||||
private static final class OAuth2AccessTokenClaims extends OAuth2AccessToken implements ClaimAccessor {
|
||||
|
||||
private final Map<String, Object> claims;
|
||||
|
||||
private OAuth2AccessTokenClaims(TokenType tokenType, String tokenValue, Instant issuedAt, Instant expiresAt,
|
||||
Set<String> scopes, Map<String, Object> claims) {
|
||||
super(tokenType, tokenValue, issuedAt, expiresAt, scopes);
|
||||
this.claims = claims;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getClaims() {
|
||||
return this.claims;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package com.pig4cloud.pig.auth.support;
|
||||
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
@ -70,6 +71,9 @@ public class OAuth2ResourceOwnerPasswordAuthenticationConverter implements Authe
|
||||
&& !e.getKey().equals(OAuth2ParameterNames.SCOPE))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0)));
|
||||
|
||||
// 注入license
|
||||
additionalParameters.put(SecurityConstants.DETAILS_LICENSE, SecurityConstants.PROJECT_LICENSE);
|
||||
|
||||
return new OAuth2ResourceOwnerPasswordAuthenticationToken(AuthorizationGrantType.PASSWORD, clientPrincipal,
|
||||
requestedScopes, additionalParameters);
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.pig4cloud.pig.auth.support;
|
||||
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
@ -74,8 +75,7 @@ public class OAuth2ResourceOwnerPasswordAuthenticationProvider implements Authen
|
||||
|
||||
/**
|
||||
* Performs authentication with the same contract as
|
||||
* {@link AuthenticationManager#authenticate(Authentication)}
|
||||
* .
|
||||
* {@link AuthenticationManager#authenticate(Authentication)} .
|
||||
* @param authentication the authentication request object.
|
||||
* @return a fully authenticated object including credentials. May return
|
||||
* <code>null</code> if the <code>AuthenticationProvider</code> is unable to support
|
||||
@ -99,7 +99,7 @@ public class OAuth2ResourceOwnerPasswordAuthenticationProvider implements Authen
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);
|
||||
}
|
||||
|
||||
Set<String> authorizedScopes = registeredClient.getScopes();
|
||||
Set<String> authorizedScopes;
|
||||
// Default to configured scopes
|
||||
if (!CollectionUtils.isEmpty(resouceOwnerPasswordAuthentication.getScopes())) {
|
||||
for (String requestedScope : resouceOwnerPasswordAuthentication.getScopes()) {
|
||||
@ -113,9 +113,11 @@ public class OAuth2ResourceOwnerPasswordAuthenticationProvider implements Authen
|
||||
throw new ScopeException(OAuth2ErrorCodesExpand.SCOPE_IS_EMPTY);
|
||||
}
|
||||
|
||||
Map<String, Object> additionalParameters = resouceOwnerPasswordAuthentication.getAdditionalParameters();
|
||||
String username = (String) additionalParameters.get(OAuth2ParameterNames.USERNAME);
|
||||
String password = (String) additionalParameters.get(OAuth2ParameterNames.PASSWORD);
|
||||
Map<String, Object> reqParameters = resouceOwnerPasswordAuthentication.getAdditionalParameters();
|
||||
String username = (String) reqParameters.get(OAuth2ParameterNames.USERNAME);
|
||||
String password = (String) reqParameters.get(OAuth2ParameterNames.PASSWORD);
|
||||
|
||||
Map<String, Object> additionalParameters = new HashMap<>(4);
|
||||
|
||||
try {
|
||||
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
|
||||
@ -125,6 +127,9 @@ public class OAuth2ResourceOwnerPasswordAuthenticationProvider implements Authen
|
||||
Authentication usernamePasswordAuthentication = authenticationManager
|
||||
.authenticate(usernamePasswordAuthenticationToken);
|
||||
|
||||
// ----- 输出扩展 暴露信息 -----
|
||||
additionalParameters.put(SecurityConstants.DETAILS_USER, usernamePasswordAuthentication.getPrincipal());
|
||||
|
||||
// @formatter:off
|
||||
DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()
|
||||
.registeredClient(registeredClient)
|
||||
@ -193,12 +198,9 @@ public class OAuth2ResourceOwnerPasswordAuthenticationProvider implements Authen
|
||||
|
||||
LOGGER.debug("returning OAuth2AccessTokenAuthenticationToken");
|
||||
|
||||
// ----- 扩展 暴露信息 -----
|
||||
Map<String, Object> objectMap = new HashMap<>();
|
||||
objectMap.put(OAuth2ParameterNames.USERNAME, username);
|
||||
|
||||
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken,
|
||||
refreshToken, objectMap);
|
||||
refreshToken, additionalParameters);
|
||||
|
||||
}
|
||||
catch (Exception ex) {
|
||||
|
@ -30,7 +30,7 @@ public interface SecurityConstants {
|
||||
/**
|
||||
* 前缀
|
||||
*/
|
||||
String PROJECT_PREFIX = "pig_";
|
||||
String PROJECT_PREFIX = "pig";
|
||||
|
||||
/**
|
||||
* 项目的license
|
||||
@ -50,7 +50,7 @@ public interface SecurityConstants {
|
||||
/**
|
||||
* 默认登录URL
|
||||
*/
|
||||
String OAUTH_TOKEN_URL = "/oauth/token";
|
||||
String OAUTH_TOKEN_URL = "/oauth2/token";
|
||||
|
||||
/**
|
||||
* grant_type
|
||||
@ -68,32 +68,20 @@ public interface SecurityConstants {
|
||||
String BCRYPT = "{bcrypt}";
|
||||
|
||||
/**
|
||||
* sys_oauth_client_details 表的字段,不包括client_id、client_secret
|
||||
* {noop} 加密的特征码
|
||||
*/
|
||||
String CLIENT_FIELDS = "client_id, CONCAT('{noop}',client_secret) as client_secret, resource_ids, scope, "
|
||||
+ "authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, "
|
||||
+ "refresh_token_validity, additional_information, autoapprove";
|
||||
|
||||
/**
|
||||
* JdbcClientDetailsService 查询语句
|
||||
*/
|
||||
String BASE_FIND_STATEMENT = "select " + CLIENT_FIELDS + " from sys_oauth_client_details";
|
||||
|
||||
/**
|
||||
* 默认的查询语句
|
||||
*/
|
||||
String DEFAULT_FIND_STATEMENT = BASE_FIND_STATEMENT + " order by client_id";
|
||||
|
||||
/**
|
||||
* 按条件client_id 查询
|
||||
*/
|
||||
String DEFAULT_SELECT_STATEMENT = BASE_FIND_STATEMENT + " where client_id = ?";
|
||||
String NOOP = "{noop}";
|
||||
|
||||
/***
|
||||
* 资源服务器默认bean名称
|
||||
*/
|
||||
String RESOURCE_SERVER_CONFIGURER = "resourceServerConfigurerAdapter";
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
String USERNAME = "username";
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
|
@ -9,9 +9,6 @@ import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
|
||||
import org.springframework.security.oauth2.core.OAuth2TokenType;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.security.oauth2.jwt.JwtClaimNames;
|
||||
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
|
||||
@ -31,15 +28,11 @@ public class CustomOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
|
||||
|
||||
@Override
|
||||
public OAuth2AuthenticatedPrincipal introspect(String token) {
|
||||
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri("http://localhost:8080/auth/oauth2/jwks").build();
|
||||
|
||||
Jwt jwt = jwtDecoder.decode(token);
|
||||
String principalClaimValue = jwt.getClaimAsString(JwtClaimNames.SUB);
|
||||
|
||||
OAuth2Authorization oldAuthorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
|
||||
|
||||
Map<String, PigUserDetailsService> userDetailsServiceMap = SpringUtil
|
||||
.getBeansOfType(PigUserDetailsService.class);
|
||||
|
||||
Optional<PigUserDetailsService> optional = userDetailsServiceMap.values().stream()
|
||||
.filter(service -> service.support(oldAuthorization.getRegisteredClientId(),
|
||||
oldAuthorization.getAuthorizationGrantType().getValue()))
|
||||
@ -47,7 +40,7 @@ public class CustomOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
|
||||
|
||||
UserDetails userDetails = null;
|
||||
try {
|
||||
userDetails = optional.get().loadUserByUsername(principalClaimValue);
|
||||
userDetails = optional.get().loadUserByUsername(oldAuthorization.getPrincipalName());
|
||||
}
|
||||
catch (UsernameNotFoundException notFoundException) {
|
||||
}
|
||||
|
@ -16,32 +16,17 @@
|
||||
|
||||
package com.pig4cloud.pig.common.security.component;
|
||||
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.pig4cloud.pig.admin.api.feign.RemoteUserService;
|
||||
import com.pig4cloud.pig.common.security.service.PigUser;
|
||||
import com.pig4cloud.pig.common.security.service.PigUserDetailsServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.jackson2.SecurityJackson2Modules;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
||||
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService;
|
||||
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
||||
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
|
||||
import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2020-06-23
|
||||
@ -50,54 +35,10 @@ import java.util.List;
|
||||
@RequiredArgsConstructor
|
||||
public class PigResourceServerAutoConfiguration {
|
||||
|
||||
|
||||
private final RemoteUserService remoteUserService;
|
||||
|
||||
private final CacheManager cacheManager;
|
||||
|
||||
/**
|
||||
* 配置授权服务器连接数据库,增加自定义序列化数据
|
||||
*/
|
||||
@Bean
|
||||
public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate,
|
||||
RegisteredClientRepository registeredClientRepository) {
|
||||
JdbcOAuth2AuthorizationService service = new JdbcOAuth2AuthorizationService(jdbcTemplate,
|
||||
registeredClientRepository);
|
||||
JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper rowMapper = new JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper(
|
||||
registeredClientRepository);
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
ClassLoader classLoader = JdbcOAuth2AuthorizationService.class.getClassLoader();
|
||||
List<Module> securityModules = SecurityJackson2Modules.getModules(classLoader);
|
||||
objectMapper.registerModules(securityModules);
|
||||
objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
|
||||
|
||||
// You will need to write the Mixin for your class so Jackson can marshall it.
|
||||
objectMapper.addMixIn(LinkedHashSet.class, LinkedHashSet.class);
|
||||
objectMapper.addMixIn(PigUser.class, PigUser.class);
|
||||
|
||||
rowMapper.setObjectMapper(objectMapper);
|
||||
service.setAuthorizationRowMapper(rowMapper);
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
|
||||
return new JdbcRegisteredClientRepository(jdbcTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果是授权码的流程,可能客户端申请了多个权限,比如:获取用户信息,修改用户信息, 此Service处理的是用户给这个客户端哪些权限,比如只给获取用户信息的权限
|
||||
*/
|
||||
@Bean
|
||||
public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate,
|
||||
RegisteredClientRepository registeredClientRepository) {
|
||||
return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
|
||||
}
|
||||
|
||||
|
||||
@Bean("pms")
|
||||
public PermissionService permissionService() {
|
||||
return new PermissionService();
|
||||
@ -119,17 +60,8 @@ public class PigResourceServerAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public UserDetailsService userDetailsService(){
|
||||
return new PigUserDetailsServiceImpl(remoteUserService ,cacheManager);
|
||||
public UserDetailsService userDetailsService() {
|
||||
return new PigUserDetailsServiceImpl(remoteUserService, cacheManager);
|
||||
}
|
||||
|
||||
// @Bean
|
||||
public JwtDecoder jwtDecoder() {
|
||||
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri("http://localhost:8080/auth/oauth2/jwks").build();
|
||||
return jwtDecoder;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -4,8 +4,12 @@ import com.nimbusds.jose.jwk.JWKSet;
|
||||
import com.nimbusds.jose.jwk.RSAKey;
|
||||
import com.nimbusds.jose.jwk.source.JWKSource;
|
||||
import com.nimbusds.jose.proc.SecurityContext;
|
||||
import com.pig4cloud.pig.common.security.service.PigRedisOAuth2AuthorizationService;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
@ -18,11 +22,11 @@ import java.util.UUID;
|
||||
*/
|
||||
public class PigTokenStoreAutoConfiguration {
|
||||
|
||||
//todo 暂时屏蔽redis 权限配置,存在不兼容的问题
|
||||
// @Bean
|
||||
// public OAuth2AuthorizationService authorizationService(RedisTemplate redisTemplate) {
|
||||
// return new PigRedisOAuth2AuthorizationService(redisTemplate);
|
||||
// }
|
||||
// todo 暂时屏蔽redis 权限配置,存在不兼容的问题
|
||||
@Bean
|
||||
public OAuth2AuthorizationService authorizationService(RedisTemplate redisTemplate) {
|
||||
return new PigRedisOAuth2AuthorizationService(redisTemplate);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SneakyThrows
|
||||
|
@ -16,13 +16,20 @@
|
||||
|
||||
package com.pig4cloud.pig.common.security.feign;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import feign.RequestInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2019/2/1 feign 拦截器传递 header 中oauth token, 使用hystrix 的信号量模式
|
||||
*/
|
||||
@ConditionalOnProperty("security.oauth2.client.client-id")
|
||||
public class PigFeignClientConfiguration {
|
||||
|
||||
/**
|
||||
* 注入 oauth2 feign token 增强
|
||||
* @param tokenResolver token获取处理器
|
||||
* @return 拦截器
|
||||
*/
|
||||
@Bean
|
||||
public RequestInterceptor oauthRequestInterceptor(BearerTokenResolver tokenResolver) {
|
||||
return new PigOAuthRequestInterceptor(tokenResolver);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,73 @@
|
||||
package com.pig4cloud.pig.common.security.feign;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pig.common.core.util.WebUtils;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* oauth2 feign token传递
|
||||
*
|
||||
* 重新 OAuth2FeignRequestInterceptor ,官方实现部分常见不适用
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2022/5/29
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class PigOAuthRequestInterceptor implements RequestInterceptor {
|
||||
|
||||
private final BearerTokenResolver tokenResolver;
|
||||
|
||||
/**
|
||||
* Create a template with the header of provided name and extracted extract </br>
|
||||
*
|
||||
* 1. 如果使用 非web 请求,header 区别 </br>
|
||||
*
|
||||
* 2. 根据authentication 还原请求token
|
||||
* @param template
|
||||
*/
|
||||
@Override
|
||||
public void apply(RequestTemplate template) {
|
||||
Collection<String> fromHeader = template.headers().get(SecurityConstants.FROM);
|
||||
// 带from 请求直接跳过
|
||||
if (CollUtil.isNotEmpty(fromHeader) && fromHeader.contains(SecurityConstants.FROM_IN)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 非web 请求直接跳过
|
||||
if (!WebUtils.getRequest().isPresent()) {
|
||||
return;
|
||||
}
|
||||
HttpServletRequest request = WebUtils.getRequest().get();
|
||||
Enumeration<String> headerNames = request.getHeaderNames();
|
||||
if (headerNames != null) {
|
||||
while (headerNames.hasMoreElements()) {
|
||||
String name = headerNames.nextElement();
|
||||
String values = request.getHeader(name);
|
||||
template.header(name, values);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 避免请求参数的 query token 无法传递
|
||||
String token = tokenResolver.resolve(request);
|
||||
if (StrUtil.isBlank(token)) {
|
||||
return;
|
||||
}
|
||||
template.header(HttpHeaders.AUTHORIZATION, String.format("%s %s", OAuth2AccessToken.TokenType.BEARER, token));
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
package com.pig4cloud.pig.common.security.service;
|
||||
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import com.pig4cloud.pig.admin.api.entity.SysOauthClientDetails;
|
||||
import com.pig4cloud.pig.admin.api.feign.RemoteClientDetailsService;
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pig.common.core.util.R;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||
import org.springframework.security.oauth2.core.OAuth2TokenFormat;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
|
||||
import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
|
||||
import org.springframework.security.oauth2.server.authorization.config.TokenSettings;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 查询客户端相关信息实现
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2022/5/29
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class PigRemoteRegisteredClientRepository implements RegisteredClientRepository {
|
||||
|
||||
/**
|
||||
* 刷新令牌有效期默认 30 填
|
||||
*/
|
||||
private final static int refreshTokenValiditySeconds = 60 * 60 * 24 * 30;
|
||||
|
||||
/**
|
||||
* 请求令牌有效期默认 12 小时
|
||||
*/
|
||||
private final static int accessTokenValiditySeconds = 60 * 60 * 12;
|
||||
|
||||
private final RemoteClientDetailsService clientDetailsService;
|
||||
|
||||
/**
|
||||
* Saves the registered client.
|
||||
*
|
||||
* <p>
|
||||
* IMPORTANT: Sensitive information should be encoded externally from the
|
||||
* implementation, e.g. {@link RegisteredClient#getClientSecret()}
|
||||
* @param registeredClient the {@link RegisteredClient}
|
||||
*/
|
||||
@Override
|
||||
public void save(RegisteredClient registeredClient) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the registered client identified by the provided {@code id}, or
|
||||
* {@code null} if not found.
|
||||
* @param id the registration identifier
|
||||
* @return the {@link RegisteredClient} if found, otherwise {@code null}
|
||||
*/
|
||||
@Override
|
||||
public RegisteredClient findById(String id) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the registered client identified by the provided {@code clientId}, or
|
||||
* {@code null} if not found.
|
||||
* @param clientId the client identifier
|
||||
* @return the {@link RegisteredClient} if found, otherwise {@code null}
|
||||
*/
|
||||
|
||||
/**
|
||||
* 重写原生方法支持redis缓存
|
||||
* @param clientId
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
@SneakyThrows
|
||||
// @Cacheable(value = CacheConstants.CLIENT_DETAILS_KEY, key = "#clientId", unless =
|
||||
// "#result == null")
|
||||
public RegisteredClient findByClientId(String clientId) {
|
||||
R<SysOauthClientDetails> detailsR = clientDetailsService.getClientDetailsById(clientId,
|
||||
SecurityConstants.FROM_IN);
|
||||
SysOauthClientDetails clientDetails = detailsR.getData();
|
||||
|
||||
RegisteredClient.Builder builder = RegisteredClient.withId(clientDetails.getClientId())
|
||||
.clientId(clientDetails.getClientSecret())
|
||||
.clientSecret(SecurityConstants.NOOP + clientDetails.getClientSecret())
|
||||
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
|
||||
|
||||
// 授权模式
|
||||
Optional.ofNullable(clientDetails.getAuthorizedGrantTypes())
|
||||
.ifPresent(grants -> StringUtils.commaDelimitedListToSet(grants)
|
||||
.forEach(s -> builder.authorizationGrantType(new AuthorizationGrantType(s))));
|
||||
// 回调地址
|
||||
Optional.ofNullable(clientDetails.getWebServerRedirectUri()).ifPresent(builder::redirectUri);
|
||||
|
||||
// scope
|
||||
Optional.ofNullable(clientDetails.getScope()).ifPresent(builder::scope);
|
||||
return builder
|
||||
.tokenSettings(TokenSettings.builder().accessTokenFormat(OAuth2TokenFormat.REFERENCE)
|
||||
.accessTokenTimeToLive(Duration.ofSeconds(Optional
|
||||
.ofNullable(clientDetails.getAccessTokenValidity()).orElse(accessTokenValiditySeconds)))
|
||||
.refreshTokenTimeToLive(
|
||||
Duration.ofSeconds(Optional.ofNullable(clientDetails.getRefreshTokenValidity())
|
||||
.orElse(refreshTokenValiditySeconds)))
|
||||
.build())
|
||||
.clientSettings(ClientSettings.builder()
|
||||
.requireAuthorizationConsent(BooleanUtil.toBoolean(clientDetails.getAutoapprove())).build())
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -67,9 +67,7 @@ public class PigUser extends User implements OAuth2AuthenticatedPrincipal {
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> getAttributes() {
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put("a", "v");
|
||||
return attributes;
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -3,3 +3,4 @@ com.pig4cloud.pig.common.security.service.PigAppUserDetailsServiceImpl
|
||||
com.pig4cloud.pig.common.security.component.PigSecurityInnerAspect
|
||||
com.pig4cloud.pig.common.security.component.PigTokenStoreAutoConfiguration
|
||||
com.pig4cloud.pig.common.security.component.PigSecurityMessageSourceConfiguration
|
||||
com.pig4cloud.pig.common.security.service.PigRemoteRegisteredClientRepository
|
||||
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pig.admin.api.feign;
|
||||
|
||||
import com.pig4cloud.pig.admin.api.entity.SysOauthClientDetails;
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pig.common.core.constant.ServiceNameConstants;
|
||||
import com.pig4cloud.pig.common.core.util.R;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2020/12/05
|
||||
*/
|
||||
@FeignClient(contextId = "remoteClientDetailsService", value = ServiceNameConstants.UMPS_SERVICE)
|
||||
public interface RemoteClientDetailsService {
|
||||
|
||||
/**
|
||||
* 通过clientId 查询客户端信息
|
||||
* @param clientId 用户名
|
||||
* @param from 调用标志
|
||||
* @return R
|
||||
*/
|
||||
@GetMapping("/client/getClientDetailsById/{clientId}")
|
||||
R<SysOauthClientDetails> getClientDetailsById(@PathVariable("clientId") String clientId,
|
||||
@RequestHeader(SecurityConstants.FROM) String from);
|
||||
|
||||
/**
|
||||
* 查询全部客户端
|
||||
* @param from 调用标识
|
||||
* @return R
|
||||
*/
|
||||
@GetMapping("/client/list")
|
||||
R<List<SysOauthClientDetails>> listClientDetails(@RequestHeader(SecurityConstants.FROM) String from);
|
||||
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
com.pig4cloud.pig.admin.api.feign.RemoteClientDetailsService
|
||||
com.pig4cloud.pig.admin.api.feign.RemoteDictService
|
||||
com.pig4cloud.pig.admin.api.feign.RemoteDeptService
|
||||
com.pig4cloud.pig.admin.api.feign.RemoteLogService
|
||||
|
@ -23,6 +23,7 @@ import com.pig4cloud.pig.admin.api.entity.SysOauthClientDetails;
|
||||
import com.pig4cloud.pig.admin.service.SysOauthClientDetailsService;
|
||||
import com.pig4cloud.pig.common.core.util.R;
|
||||
import com.pig4cloud.pig.common.log.annotation.SysLog;
|
||||
import com.pig4cloud.pig.common.security.annotation.Inner;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@ -117,4 +118,11 @@ public class OauthClientDetailsController {
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Inner(false)
|
||||
@GetMapping("/getClientDetailsById/{clientId}")
|
||||
public R getClientDetailsById(@PathVariable String clientId) {
|
||||
return R.ok(sysOauthClientDetailsService.getOne(
|
||||
Wrappers.<SysOauthClientDetails>lambdaQuery().eq(SysOauthClientDetails::getClientId, clientId), false));
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user