mirror of
https://gitee.com/log4j/pig.git
synced 2024-12-22 12:48:58 +08:00
♻️ Refactoring code. 添加自定义类型抽象
This commit is contained in:
parent
cb91df9a33
commit
6cf6c339ae
@ -17,6 +17,7 @@
|
||||
package com.pig4cloud.pig.auth.config;
|
||||
|
||||
import com.pig4cloud.pig.auth.support.CustomeOAuth2AccessTokenGenerator;
|
||||
import com.pig4cloud.pig.auth.support.PigDaoAuthenticationProvider;
|
||||
import com.pig4cloud.pig.auth.support.handler.PigAuthenticationFailureEventHandler;
|
||||
import com.pig4cloud.pig.auth.support.handler.PigAuthenticationSuccessEventHandler;
|
||||
import com.pig4cloud.pig.auth.support.password.OAuth2ResourceOwnerPasswordAuthenticationConverter;
|
||||
@ -24,7 +25,6 @@ import com.pig4cloud.pig.auth.support.password.OAuth2ResourceOwnerPasswordAuthen
|
||||
import com.pig4cloud.pig.auth.support.sms.OAuth2ResourceOwnerSmsAuthenticationConverter;
|
||||
import com.pig4cloud.pig.auth.support.sms.OAuth2ResourceOwnerSmsAuthenticationProvider;
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pig.common.security.component.PigDaoAuthenticationProvider;
|
||||
import com.pig4cloud.pig.common.security.service.PigUser;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -16,9 +16,9 @@
|
||||
|
||||
package com.pig4cloud.pig.auth.config;
|
||||
|
||||
import com.pig4cloud.pig.auth.support.PigDaoAuthenticationProvider;
|
||||
import com.pig4cloud.pig.auth.support.handler.FormAuthenticationFailureHandler;
|
||||
import com.pig4cloud.pig.auth.support.handler.SsoLogoutSuccessHandler;
|
||||
import com.pig4cloud.pig.common.security.component.PigDaoAuthenticationProvider;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
|
@ -22,14 +22,14 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.pig4cloud.pig.admin.api.entity.SysOauthClientDetails;
|
||||
import com.pig4cloud.pig.admin.api.feign.RemoteClientDetailsService;
|
||||
import com.pig4cloud.pig.admin.api.vo.TokenVo;
|
||||
import com.pig4cloud.pig.common.security.util.OAuth2EndpointUtils;
|
||||
import com.pig4cloud.pig.common.security.util.OAuth2ErrorCodesExpand;
|
||||
import com.pig4cloud.pig.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pig.common.core.constant.CommonConstants;
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pig.common.core.util.R;
|
||||
import com.pig4cloud.pig.common.core.util.SpringContextHolder;
|
||||
import com.pig4cloud.pig.common.security.annotation.Inner;
|
||||
import com.pig4cloud.pig.common.security.util.OAuth2EndpointUtils;
|
||||
import com.pig4cloud.pig.common.security.util.OAuth2ErrorCodesExpand;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -1,6 +1,8 @@
|
||||
package com.pig4cloud.pig.common.security.component;
|
||||
package com.pig4cloud.pig.auth.support;
|
||||
|
||||
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.WebUtils;
|
||||
import com.pig4cloud.pig.common.security.service.PigUserDetailsService;
|
||||
import org.springframework.core.Ordered;
|
||||
@ -23,7 +25,6 @@ import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
@ -62,8 +63,8 @@ public class PigDaoAuthenticationProvider extends AbstractUserDetailsAuthenticat
|
||||
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
|
||||
|
||||
// app 模式不用校验密码
|
||||
Object attribute = WebUtils.getRequest().get().getAttribute(OAuth2ParameterNames.GRANT_TYPE);
|
||||
if (Objects.nonNull(attribute)) {
|
||||
String grantType = WebUtils.getRequest().get().getParameter(OAuth2ParameterNames.GRANT_TYPE);
|
||||
if (StrUtil.equals(SecurityConstants.APP, grantType)) {
|
||||
return;
|
||||
}
|
||||
|
@ -0,0 +1,100 @@
|
||||
package com.pig4cloud.pig.auth.support.base;
|
||||
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pig.common.security.util.OAuth2EndpointUtils;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author jumuning
|
||||
* @date 2022-06-02
|
||||
*
|
||||
* 自定义模式认证转换器
|
||||
*/
|
||||
public abstract class OAuth2ResourceOwnerBaseAuthenticationConverter<T extends OAuth2ResourceOwnerBaseAuthenticationToken>
|
||||
implements AuthenticationConverter {
|
||||
|
||||
/**
|
||||
* 是否支持此convert
|
||||
* @param grantType 授权类型
|
||||
* @return
|
||||
*/
|
||||
public abstract boolean support(String grantType);
|
||||
|
||||
/**
|
||||
* 校验参数
|
||||
* @param request 请求
|
||||
*/
|
||||
public void checkParams(HttpServletRequest request) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建具体类型的token
|
||||
* @param clientPrincipal
|
||||
* @param requestedScopes
|
||||
* @param additionalParameters
|
||||
* @return
|
||||
*/
|
||||
public abstract T buildToken(Authentication clientPrincipal, Set<String> requestedScopes,
|
||||
Map<String, Object> additionalParameters);
|
||||
|
||||
@Override
|
||||
public Authentication convert(HttpServletRequest request) {
|
||||
|
||||
// grant_type (REQUIRED)
|
||||
String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);
|
||||
if (!support(grantType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MultiValueMap<String, String> parameters = OAuth2EndpointUtils.getParameters(request);
|
||||
// scope (OPTIONAL)
|
||||
String scope = parameters.getFirst(OAuth2ParameterNames.SCOPE);
|
||||
if (StringUtils.hasText(scope) && parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) {
|
||||
OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SCOPE,
|
||||
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
|
||||
}
|
||||
|
||||
Set<String> requestedScopes = null;
|
||||
if (StringUtils.hasText(scope)) {
|
||||
requestedScopes = new HashSet<>(Arrays.asList(StringUtils.delimitedListToStringArray(scope, " ")));
|
||||
}
|
||||
|
||||
// 校验个性化参数
|
||||
checkParams(request);
|
||||
|
||||
// 获取当前已经认证的客户端信息
|
||||
Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (clientPrincipal == null) {
|
||||
OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ErrorCodes.INVALID_CLIENT,
|
||||
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
|
||||
}
|
||||
|
||||
// 扩展信息
|
||||
Map<String, Object> additionalParameters = parameters.entrySet().stream()
|
||||
.filter(e -> !e.getKey().equals(OAuth2ParameterNames.GRANT_TYPE)
|
||||
&& !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);
|
||||
|
||||
// 创建token
|
||||
return buildToken(clientPrincipal, requestedScopes, additionalParameters);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,281 @@
|
||||
package com.pig4cloud.pig.auth.support.base;
|
||||
|
||||
import com.pig4cloud.pig.auth.support.ScopeException;
|
||||
import com.pig4cloud.pig.common.security.util.OAuth2ErrorCodesExpand;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.security.authentication.*;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.oauth2.core.*;
|
||||
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.authentication.OAuth2ClientAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
||||
import org.springframework.security.oauth2.server.authorization.context.ProviderContextHolder;
|
||||
import org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;
|
||||
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;
|
||||
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author jumuning
|
||||
* @description
|
||||
*
|
||||
* 处理自定义授权
|
||||
*/
|
||||
public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OAuth2ResourceOwnerBaseAuthenticationToken>
|
||||
implements AuthenticationProvider {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(OAuth2ResourceOwnerBaseAuthenticationProvider.class);
|
||||
|
||||
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1";
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;
|
||||
|
||||
private final AuthenticationManager authenticationManager;
|
||||
|
||||
private final MessageSourceAccessor messages = new MessageSourceAccessor(new SpringSecurityMessageSource(),
|
||||
Locale.CHINA);
|
||||
|
||||
@Deprecated
|
||||
private Supplier<String> refreshTokenGenerator;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the
|
||||
* provided parameters.
|
||||
* @param authorizationService the authorization service
|
||||
* @param tokenGenerator the token generator
|
||||
* @since 0.2.3
|
||||
*/
|
||||
public OAuth2ResourceOwnerBaseAuthenticationProvider(AuthenticationManager authenticationManager,
|
||||
OAuth2AuthorizationService authorizationService,
|
||||
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {
|
||||
Assert.notNull(authorizationService, "authorizationService cannot be null");
|
||||
Assert.notNull(tokenGenerator, "tokenGenerator cannot be null");
|
||||
this.authenticationManager = authenticationManager;
|
||||
this.authorizationService = authorizationService;
|
||||
this.tokenGenerator = tokenGenerator;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setRefreshTokenGenerator(Supplier<String> refreshTokenGenerator) {
|
||||
Assert.notNull(refreshTokenGenerator, "refreshTokenGenerator cannot be null");
|
||||
this.refreshTokenGenerator = refreshTokenGenerator;
|
||||
}
|
||||
|
||||
public abstract UsernamePasswordAuthenticationToken buildToken(Map<String, Object> reqParameters);
|
||||
|
||||
/**
|
||||
* 当前provider是否支持此令牌类型
|
||||
* @param authentication
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public abstract boolean supports(Class<?> authentication);
|
||||
|
||||
/**
|
||||
* 当前的请求客户端是否支持此模式
|
||||
* @param registeredClient
|
||||
*/
|
||||
public abstract void checkClient(RegisteredClient registeredClient);
|
||||
|
||||
/**
|
||||
* Performs authentication with the same contract as
|
||||
* {@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
|
||||
* authentication of the passed <code>Authentication</code> object. In such a case,
|
||||
* the next <code>AuthenticationProvider</code> that supports the presented
|
||||
* <code>Authentication</code> class will be tried.
|
||||
* @throws AuthenticationException if authentication fails.
|
||||
*/
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
|
||||
T resouceOwnerBaseAuthentication = (T) authentication;
|
||||
|
||||
OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(
|
||||
resouceOwnerBaseAuthentication);
|
||||
|
||||
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
|
||||
checkClient(registeredClient);
|
||||
|
||||
Set<String> authorizedScopes;
|
||||
// Default to configured scopes
|
||||
if (!CollectionUtils.isEmpty(resouceOwnerBaseAuthentication.getScopes())) {
|
||||
for (String requestedScope : resouceOwnerBaseAuthentication.getScopes()) {
|
||||
if (!registeredClient.getScopes().contains(requestedScope)) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_SCOPE);
|
||||
}
|
||||
}
|
||||
authorizedScopes = new LinkedHashSet<>(resouceOwnerBaseAuthentication.getScopes());
|
||||
}
|
||||
else {
|
||||
throw new ScopeException(OAuth2ErrorCodesExpand.SCOPE_IS_EMPTY);
|
||||
}
|
||||
|
||||
Map<String, Object> reqParameters = resouceOwnerBaseAuthentication.getAdditionalParameters();
|
||||
try {
|
||||
|
||||
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = buildToken(reqParameters);
|
||||
|
||||
LOGGER.debug("got usernamePasswordAuthenticationToken=" + usernamePasswordAuthenticationToken);
|
||||
|
||||
Authentication usernamePasswordAuthentication = authenticationManager
|
||||
.authenticate(usernamePasswordAuthenticationToken);
|
||||
|
||||
// @formatter:off
|
||||
DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()
|
||||
.registeredClient(registeredClient)
|
||||
.principal(usernamePasswordAuthentication)
|
||||
.providerContext(ProviderContextHolder.getProviderContext())
|
||||
.authorizedScopes(authorizedScopes)
|
||||
.authorizationGrantType(AuthorizationGrantType.PASSWORD)
|
||||
.authorizationGrant(resouceOwnerBaseAuthentication);
|
||||
// @formatter:on
|
||||
|
||||
OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization
|
||||
.withRegisteredClient(registeredClient).principalName(usernamePasswordAuthentication.getName())
|
||||
.authorizationGrantType(AuthorizationGrantType.PASSWORD)
|
||||
.attribute(OAuth2Authorization.AUTHORIZED_SCOPE_ATTRIBUTE_NAME, authorizedScopes);
|
||||
|
||||
// ----- Access token -----
|
||||
OAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build();
|
||||
OAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext);
|
||||
if (generatedAccessToken == null) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
|
||||
"The token generator failed to generate the access token.", ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
|
||||
generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(),
|
||||
generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes());
|
||||
if (generatedAccessToken instanceof ClaimAccessor) {
|
||||
authorizationBuilder.id(accessToken.getTokenValue())
|
||||
.token(accessToken,
|
||||
(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME,
|
||||
((ClaimAccessor) generatedAccessToken).getClaims()))
|
||||
.attribute(OAuth2Authorization.AUTHORIZED_SCOPE_ATTRIBUTE_NAME, authorizedScopes)
|
||||
.attribute(Principal.class.getName(), usernamePasswordAuthentication);
|
||||
}
|
||||
else {
|
||||
authorizationBuilder.id(accessToken.getTokenValue()).accessToken(accessToken);
|
||||
}
|
||||
|
||||
// ----- Refresh token -----
|
||||
OAuth2RefreshToken refreshToken = null;
|
||||
if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN) &&
|
||||
// Do not issue refresh token to public client
|
||||
!clientPrincipal.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.NONE)) {
|
||||
|
||||
if (this.refreshTokenGenerator != null) {
|
||||
Instant issuedAt = Instant.now();
|
||||
Instant expiresAt = issuedAt.plus(registeredClient.getTokenSettings().getRefreshTokenTimeToLive());
|
||||
refreshToken = new OAuth2RefreshToken(this.refreshTokenGenerator.get(), issuedAt, expiresAt);
|
||||
}
|
||||
else {
|
||||
tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();
|
||||
OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);
|
||||
if (!(generatedRefreshToken instanceof OAuth2RefreshToken)) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
|
||||
"The token generator failed to generate the refresh token.", ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
refreshToken = (OAuth2RefreshToken) generatedRefreshToken;
|
||||
}
|
||||
authorizationBuilder.refreshToken(refreshToken);
|
||||
}
|
||||
|
||||
OAuth2Authorization authorization = authorizationBuilder.build();
|
||||
|
||||
this.authorizationService.save(authorization);
|
||||
|
||||
LOGGER.debug("returning OAuth2AccessTokenAuthenticationToken");
|
||||
|
||||
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken,
|
||||
refreshToken, Objects.requireNonNull(authorization.getAccessToken().getClaims()));
|
||||
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOGGER.error("problem in authenticate", ex);
|
||||
throw oAuth2AuthenticationException(authentication, (AuthenticationException) ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录异常转换为oauth2异常
|
||||
* @param authentication 身份验证
|
||||
* @param authenticationException 身份验证异常
|
||||
* @return {@link OAuth2AuthenticationException}
|
||||
*/
|
||||
private OAuth2AuthenticationException oAuth2AuthenticationException(Authentication authentication,
|
||||
AuthenticationException authenticationException) {
|
||||
if (authenticationException instanceof UsernameNotFoundException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USERNAME_NOT_FOUND,
|
||||
this.messages.getMessage("JdbcDaoImpl.notFound", new Object[] { authentication.getName() },
|
||||
"Username {0} not found"),
|
||||
""));
|
||||
}
|
||||
if (authenticationException instanceof BadCredentialsException) {
|
||||
return new OAuth2AuthenticationException(
|
||||
new OAuth2Error(OAuth2ErrorCodesExpand.BAD_CREDENTIALS, this.messages.getMessage(
|
||||
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), ""));
|
||||
}
|
||||
if (authenticationException instanceof LockedException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USER_LOCKED, this.messages
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.locked", "User account is locked"), ""));
|
||||
}
|
||||
if (authenticationException instanceof DisabledException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USER_DISABLE,
|
||||
this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.disabled", "User is disabled"),
|
||||
""));
|
||||
}
|
||||
if (authenticationException instanceof AccountExpiredException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USER_EXPIRED, this.messages
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.expired", "User account has expired"), ""));
|
||||
}
|
||||
if (authenticationException instanceof CredentialsExpiredException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.CREDENTIALS_EXPIRED,
|
||||
this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.credentialsExpired",
|
||||
"User credentials have expired"),
|
||||
""));
|
||||
}
|
||||
if (authenticationException instanceof ScopeException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_SCOPE,
|
||||
this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "invalid_scope"), ""));
|
||||
}
|
||||
return new OAuth2AuthenticationException(OAuth2ErrorCodesExpand.UN_KNOW_LOGIN_ERROR);
|
||||
}
|
||||
|
||||
private OAuth2ClientAuthenticationToken getAuthenticatedClientElseThrowInvalidClient(
|
||||
Authentication authentication) {
|
||||
|
||||
OAuth2ClientAuthenticationToken clientPrincipal = null;
|
||||
|
||||
if (OAuth2ClientAuthenticationToken.class.isAssignableFrom(authentication.getPrincipal().getClass())) {
|
||||
clientPrincipal = (OAuth2ClientAuthenticationToken) authentication.getPrincipal();
|
||||
}
|
||||
|
||||
if (clientPrincipal != null && clientPrincipal.isAuthenticated()) {
|
||||
return clientPrincipal;
|
||||
}
|
||||
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package com.pig4cloud.pig.auth.support.base;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2022/6/2
|
||||
*
|
||||
* 自定义授权模式抽象
|
||||
*/
|
||||
public abstract class OAuth2ResourceOwnerBaseAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
@Getter
|
||||
private final AuthorizationGrantType authorizationGrantType;
|
||||
|
||||
@Getter
|
||||
private final Authentication clientPrincipal;
|
||||
|
||||
@Getter
|
||||
private final Set<String> scopes;
|
||||
|
||||
@Getter
|
||||
private final Map<String, Object> additionalParameters;
|
||||
|
||||
public OAuth2ResourceOwnerBaseAuthenticationToken(AuthorizationGrantType authorizationGrantType,
|
||||
Authentication clientPrincipal, @Nullable Set<String> scopes,
|
||||
@Nullable Map<String, Object> additionalParameters) {
|
||||
super(Collections.emptyList());
|
||||
Assert.notNull(authorizationGrantType, "authorizationGrantType cannot be null");
|
||||
Assert.notNull(clientPrincipal, "clientPrincipal cannot be null");
|
||||
this.authorizationGrantType = authorizationGrantType;
|
||||
this.clientPrincipal = clientPrincipal;
|
||||
this.scopes = Collections.unmodifiableSet(scopes != null ? new HashSet<>(scopes) : Collections.emptySet());
|
||||
this.additionalParameters = Collections.unmodifiableMap(
|
||||
additionalParameters != null ? new HashMap<>(additionalParameters) : Collections.emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩展模式一般不需要密码
|
||||
*/
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户名
|
||||
*/
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return this.clientPrincipal;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 自定义认证模式接入的抽象实现
|
||||
*/
|
||||
package com.pig4cloud.pig.auth.support.base;
|
@ -1,52 +1,50 @@
|
||||
package com.pig4cloud.pig.auth.support.password;
|
||||
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pig.auth.support.base.OAuth2ResourceOwnerBaseAuthenticationConverter;
|
||||
import com.pig4cloud.pig.common.security.util.OAuth2EndpointUtils;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author jumuning
|
||||
* @date 2021/9/14 OAuth2 资源所有者密码认证转换器
|
||||
* @date 2022-06-02
|
||||
*
|
||||
* 密码认证转换器
|
||||
*/
|
||||
public class OAuth2ResourceOwnerPasswordAuthenticationConverter implements AuthenticationConverter {
|
||||
public class OAuth2ResourceOwnerPasswordAuthenticationConverter
|
||||
extends OAuth2ResourceOwnerBaseAuthenticationConverter<OAuth2ResourceOwnerPasswordAuthenticationToken> {
|
||||
|
||||
/**
|
||||
* 支持密码模式
|
||||
* @param grantType 授权类型
|
||||
*/
|
||||
@Override
|
||||
public boolean support(String grantType) {
|
||||
return AuthorizationGrantType.PASSWORD.getValue().equals(grantType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication convert(HttpServletRequest request) {
|
||||
|
||||
// grant_type (REQUIRED)
|
||||
String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);
|
||||
if (!AuthorizationGrantType.PASSWORD.getValue().equals(grantType)) {
|
||||
return null;
|
||||
}
|
||||
public OAuth2ResourceOwnerPasswordAuthenticationToken buildToken(Authentication clientPrincipal,
|
||||
Set requestedScopes, Map additionalParameters) {
|
||||
return new OAuth2ResourceOwnerPasswordAuthenticationToken(AuthorizationGrantType.PASSWORD, clientPrincipal,
|
||||
requestedScopes, additionalParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验扩展参数 密码模式密码必须不为空
|
||||
* @param request 参数列表
|
||||
*/
|
||||
@Override
|
||||
public void checkParams(HttpServletRequest request) {
|
||||
MultiValueMap<String, String> parameters = OAuth2EndpointUtils.getParameters(request);
|
||||
|
||||
// scope (OPTIONAL)
|
||||
String scope = parameters.getFirst(OAuth2ParameterNames.SCOPE);
|
||||
if (StringUtils.hasText(scope) && parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) {
|
||||
OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SCOPE,
|
||||
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
|
||||
}
|
||||
|
||||
Set<String> requestedScopes = null;
|
||||
if (StringUtils.hasText(scope)) {
|
||||
requestedScopes = new HashSet<>(Arrays.asList(StringUtils.delimitedListToStringArray(scope, " ")));
|
||||
}
|
||||
|
||||
// username (REQUIRED)
|
||||
String username = parameters.getFirst(OAuth2ParameterNames.USERNAME);
|
||||
if (!StringUtils.hasText(username) || parameters.get(OAuth2ParameterNames.USERNAME).size() != 1) {
|
||||
@ -60,24 +58,6 @@ public class OAuth2ResourceOwnerPasswordAuthenticationConverter implements Authe
|
||||
OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.PASSWORD,
|
||||
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
|
||||
}
|
||||
|
||||
Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (clientPrincipal == null) {
|
||||
OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ErrorCodes.INVALID_CLIENT,
|
||||
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
|
||||
}
|
||||
|
||||
Map<String, Object> additionalParameters = parameters.entrySet().stream()
|
||||
.filter(e -> !e.getKey().equals(OAuth2ParameterNames.GRANT_TYPE)
|
||||
&& !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,59 +1,34 @@
|
||||
package com.pig4cloud.pig.auth.support.password;
|
||||
|
||||
import com.pig4cloud.pig.auth.support.ScopeException;
|
||||
import com.pig4cloud.pig.common.security.util.OAuth2ErrorCodesExpand;
|
||||
import com.pig4cloud.pig.auth.support.base.OAuth2ResourceOwnerBaseAuthenticationProvider;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.security.authentication.*;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.oauth2.core.*;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.OAuth2Token;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
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.authentication.OAuth2ClientAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
||||
import org.springframework.security.oauth2.server.authorization.context.ProviderContextHolder;
|
||||
import org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;
|
||||
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;
|
||||
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author jumuning
|
||||
* @description 处理用户名密码授权
|
||||
*/
|
||||
public class OAuth2ResourceOwnerPasswordAuthenticationProvider implements AuthenticationProvider {
|
||||
public class OAuth2ResourceOwnerPasswordAuthenticationProvider
|
||||
extends OAuth2ResourceOwnerBaseAuthenticationProvider<OAuth2ResourceOwnerPasswordAuthenticationToken> {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(OAuth2ResourceOwnerPasswordAuthenticationProvider.class);
|
||||
|
||||
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1";
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;
|
||||
|
||||
private final AuthenticationManager authenticationManager;
|
||||
|
||||
private final MessageSourceAccessor messages = new MessageSourceAccessor(new SpringSecurityMessageSource(),
|
||||
Locale.CHINA);
|
||||
|
||||
@Deprecated
|
||||
private Supplier<String> refreshTokenGenerator;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the
|
||||
* provided parameters.
|
||||
* @param authenticationManager
|
||||
* @param authorizationService the authorization service
|
||||
* @param tokenGenerator the token generator
|
||||
* @since 0.2.3
|
||||
@ -61,148 +36,14 @@ public class OAuth2ResourceOwnerPasswordAuthenticationProvider implements Authen
|
||||
public OAuth2ResourceOwnerPasswordAuthenticationProvider(AuthenticationManager authenticationManager,
|
||||
OAuth2AuthorizationService authorizationService,
|
||||
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {
|
||||
Assert.notNull(authorizationService, "authorizationService cannot be null");
|
||||
Assert.notNull(tokenGenerator, "tokenGenerator cannot be null");
|
||||
this.authenticationManager = authenticationManager;
|
||||
this.authorizationService = authorizationService;
|
||||
this.tokenGenerator = tokenGenerator;
|
||||
super(authenticationManager, authorizationService, tokenGenerator);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setRefreshTokenGenerator(Supplier<String> refreshTokenGenerator) {
|
||||
Assert.notNull(refreshTokenGenerator, "refreshTokenGenerator cannot be null");
|
||||
this.refreshTokenGenerator = refreshTokenGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs authentication with the same contract as
|
||||
* {@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
|
||||
* authentication of the passed <code>Authentication</code> object. In such a case,
|
||||
* the next <code>AuthenticationProvider</code> that supports the presented
|
||||
* <code>Authentication</code> class will be tried.
|
||||
* @throws AuthenticationException if authentication fails.
|
||||
*/
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
|
||||
OAuth2ResourceOwnerPasswordAuthenticationToken resouceOwnerPasswordAuthentication = (OAuth2ResourceOwnerPasswordAuthenticationToken) authentication;
|
||||
|
||||
OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(
|
||||
resouceOwnerPasswordAuthentication);
|
||||
|
||||
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
|
||||
|
||||
assert registeredClient != null;
|
||||
if (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.PASSWORD)) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);
|
||||
}
|
||||
|
||||
Set<String> authorizedScopes;
|
||||
// Default to configured scopes
|
||||
if (!CollectionUtils.isEmpty(resouceOwnerPasswordAuthentication.getScopes())) {
|
||||
for (String requestedScope : resouceOwnerPasswordAuthentication.getScopes()) {
|
||||
if (!registeredClient.getScopes().contains(requestedScope)) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_SCOPE);
|
||||
}
|
||||
}
|
||||
authorizedScopes = new LinkedHashSet<>(resouceOwnerPasswordAuthentication.getScopes());
|
||||
}
|
||||
else {
|
||||
throw new ScopeException(OAuth2ErrorCodesExpand.SCOPE_IS_EMPTY);
|
||||
}
|
||||
|
||||
Map<String, Object> reqParameters = resouceOwnerPasswordAuthentication.getAdditionalParameters();
|
||||
public UsernamePasswordAuthenticationToken buildToken(Map<String, Object> reqParameters) {
|
||||
String username = (String) reqParameters.get(OAuth2ParameterNames.USERNAME);
|
||||
String password = (String) reqParameters.get(OAuth2ParameterNames.PASSWORD);
|
||||
|
||||
try {
|
||||
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
|
||||
username, password);
|
||||
LOGGER.debug("got usernamePasswordAuthenticationToken=" + usernamePasswordAuthenticationToken);
|
||||
|
||||
Authentication usernamePasswordAuthentication = authenticationManager
|
||||
.authenticate(usernamePasswordAuthenticationToken);
|
||||
|
||||
// @formatter:off
|
||||
DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()
|
||||
.registeredClient(registeredClient)
|
||||
.principal(usernamePasswordAuthentication)
|
||||
.providerContext(ProviderContextHolder.getProviderContext())
|
||||
.authorizedScopes(authorizedScopes)
|
||||
.authorizationGrantType(AuthorizationGrantType.PASSWORD)
|
||||
.authorizationGrant(resouceOwnerPasswordAuthentication);
|
||||
// @formatter:on
|
||||
|
||||
OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization
|
||||
.withRegisteredClient(registeredClient).principalName(usernamePasswordAuthentication.getName())
|
||||
.authorizationGrantType(AuthorizationGrantType.PASSWORD)
|
||||
.attribute(OAuth2Authorization.AUTHORIZED_SCOPE_ATTRIBUTE_NAME, authorizedScopes);
|
||||
|
||||
// ----- Access token -----
|
||||
OAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build();
|
||||
OAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext);
|
||||
if (generatedAccessToken == null) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
|
||||
"The token generator failed to generate the access token.", ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
|
||||
generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(),
|
||||
generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes());
|
||||
if (generatedAccessToken instanceof ClaimAccessor) {
|
||||
authorizationBuilder.id(accessToken.getTokenValue())
|
||||
.token(accessToken,
|
||||
(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME,
|
||||
((ClaimAccessor) generatedAccessToken).getClaims()))
|
||||
.attribute(OAuth2Authorization.AUTHORIZED_SCOPE_ATTRIBUTE_NAME, authorizedScopes)
|
||||
.attribute(Principal.class.getName(), usernamePasswordAuthentication);
|
||||
}
|
||||
else {
|
||||
authorizationBuilder.id(accessToken.getTokenValue()).accessToken(accessToken);
|
||||
}
|
||||
|
||||
// ----- Refresh token -----
|
||||
OAuth2RefreshToken refreshToken = null;
|
||||
if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN) &&
|
||||
// Do not issue refresh token to public client
|
||||
!clientPrincipal.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.NONE)) {
|
||||
|
||||
if (this.refreshTokenGenerator != null) {
|
||||
Instant issuedAt = Instant.now();
|
||||
Instant expiresAt = issuedAt.plus(registeredClient.getTokenSettings().getRefreshTokenTimeToLive());
|
||||
refreshToken = new OAuth2RefreshToken(this.refreshTokenGenerator.get(), issuedAt, expiresAt);
|
||||
}
|
||||
else {
|
||||
tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();
|
||||
OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);
|
||||
if (!(generatedRefreshToken instanceof OAuth2RefreshToken)) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
|
||||
"The token generator failed to generate the refresh token.", ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
refreshToken = (OAuth2RefreshToken) generatedRefreshToken;
|
||||
}
|
||||
authorizationBuilder.refreshToken(refreshToken);
|
||||
}
|
||||
|
||||
OAuth2Authorization authorization = authorizationBuilder.build();
|
||||
|
||||
this.authorizationService.save(authorization);
|
||||
|
||||
LOGGER.debug("returning OAuth2AccessTokenAuthenticationToken");
|
||||
|
||||
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken,
|
||||
refreshToken, Objects.requireNonNull(authorization.getAccessToken().getClaims()));
|
||||
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOGGER.error("problem in authenticate", ex);
|
||||
throw oAuth2AuthenticationException(authentication, (AuthenticationException) ex);
|
||||
}
|
||||
|
||||
return new UsernamePasswordAuthenticationToken(username, password);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -212,65 +53,12 @@ public class OAuth2ResourceOwnerPasswordAuthenticationProvider implements Authen
|
||||
return supports;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录异常转换为oauth2异常
|
||||
* @param authentication 身份验证
|
||||
* @param authenticationException 身份验证异常
|
||||
* @return {@link OAuth2AuthenticationException}
|
||||
*/
|
||||
private OAuth2AuthenticationException oAuth2AuthenticationException(Authentication authentication,
|
||||
AuthenticationException authenticationException) {
|
||||
if (authenticationException instanceof UsernameNotFoundException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USERNAME_NOT_FOUND,
|
||||
this.messages.getMessage("JdbcDaoImpl.notFound", new Object[] { authentication.getName() },
|
||||
"Username {0} not found"),
|
||||
""));
|
||||
@Override
|
||||
public void checkClient(RegisteredClient registeredClient) {
|
||||
assert registeredClient != null;
|
||||
if (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.PASSWORD)) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);
|
||||
}
|
||||
if (authenticationException instanceof BadCredentialsException) {
|
||||
return new OAuth2AuthenticationException(
|
||||
new OAuth2Error(OAuth2ErrorCodesExpand.BAD_CREDENTIALS, this.messages.getMessage(
|
||||
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), ""));
|
||||
}
|
||||
if (authenticationException instanceof LockedException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USER_LOCKED, this.messages
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.locked", "User account is locked"), ""));
|
||||
}
|
||||
if (authenticationException instanceof DisabledException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USER_DISABLE,
|
||||
this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.disabled", "User is disabled"),
|
||||
""));
|
||||
}
|
||||
if (authenticationException instanceof AccountExpiredException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USER_EXPIRED, this.messages
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.expired", "User account has expired"), ""));
|
||||
}
|
||||
if (authenticationException instanceof CredentialsExpiredException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.CREDENTIALS_EXPIRED,
|
||||
this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.credentialsExpired",
|
||||
"User credentials have expired"),
|
||||
""));
|
||||
}
|
||||
if (authenticationException instanceof ScopeException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_SCOPE,
|
||||
this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "invalid_scope"), ""));
|
||||
}
|
||||
return new OAuth2AuthenticationException(OAuth2ErrorCodesExpand.UN_KNOW_LOGIN_ERROR);
|
||||
}
|
||||
|
||||
private OAuth2ClientAuthenticationToken getAuthenticatedClientElseThrowInvalidClient(
|
||||
Authentication authentication) {
|
||||
|
||||
OAuth2ClientAuthenticationToken clientPrincipal = null;
|
||||
|
||||
if (OAuth2ClientAuthenticationToken.class.isAssignableFrom(authentication.getPrincipal().getClass())) {
|
||||
clientPrincipal = (OAuth2ClientAuthenticationToken) authentication.getPrincipal();
|
||||
}
|
||||
|
||||
if (clientPrincipal != null && clientPrincipal.isAuthenticated()) {
|
||||
return clientPrincipal;
|
||||
}
|
||||
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,80 +1,21 @@
|
||||
package com.pig4cloud.pig.auth.support.password;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import com.pig4cloud.pig.auth.support.base.OAuth2ResourceOwnerBaseAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author jumuning
|
||||
* @description 密码授权token信息
|
||||
*/
|
||||
public class OAuth2ResourceOwnerPasswordAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = -6067207202119450764L;
|
||||
|
||||
private final AuthorizationGrantType authorizationGrantType;
|
||||
|
||||
private final Authentication clientPrincipal;
|
||||
|
||||
private final Set<String> scopes;
|
||||
|
||||
private final Map<String, Object> additionalParameters;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2ClientCredentialsAuthenticationToken} using the provided
|
||||
* parameters.
|
||||
* @param clientPrincipal the authenticated client principal
|
||||
*/
|
||||
public class OAuth2ResourceOwnerPasswordAuthenticationToken extends OAuth2ResourceOwnerBaseAuthenticationToken {
|
||||
|
||||
public OAuth2ResourceOwnerPasswordAuthenticationToken(AuthorizationGrantType authorizationGrantType,
|
||||
Authentication clientPrincipal, @Nullable Set<String> scopes,
|
||||
@Nullable Map<String, Object> additionalParameters) {
|
||||
super(Collections.emptyList());
|
||||
Assert.notNull(authorizationGrantType, "authorizationGrantType cannot be null");
|
||||
Assert.notNull(clientPrincipal, "clientPrincipal cannot be null");
|
||||
this.authorizationGrantType = authorizationGrantType;
|
||||
this.clientPrincipal = clientPrincipal;
|
||||
this.scopes = Collections.unmodifiableSet(scopes != null ? new HashSet<>(scopes) : Collections.emptySet());
|
||||
this.additionalParameters = Collections.unmodifiableMap(
|
||||
additionalParameters != null ? new HashMap<>(additionalParameters) : Collections.emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authorization grant type.
|
||||
* @return the authorization grant type
|
||||
*/
|
||||
public AuthorizationGrantType getGrantType() {
|
||||
return this.authorizationGrantType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return this.clientPrincipal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the requested scope(s).
|
||||
* @return the requested scope(s), or an empty {@code Set} if not available
|
||||
*/
|
||||
public Set<String> getScopes() {
|
||||
return this.scopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the additional parameters.
|
||||
* @return the additional parameters
|
||||
*/
|
||||
public Map<String, Object> getAdditionalParameters() {
|
||||
return this.additionalParameters;
|
||||
Authentication clientPrincipal, Set<String> scopes, Map<String, Object> additionalParameters) {
|
||||
super(authorizationGrantType, clientPrincipal, scopes, additionalParameters);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 密码模式
|
||||
*/
|
||||
package com.pig4cloud.pig.auth.support.password;
|
@ -1,22 +1,17 @@
|
||||
package com.pig4cloud.pig.auth.support.sms;
|
||||
|
||||
import com.pig4cloud.pig.auth.support.base.OAuth2ResourceOwnerBaseAuthenticationConverter;
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pig.common.security.util.OAuth2EndpointUtils;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
@ -24,57 +19,39 @@ import java.util.stream.Collectors;
|
||||
*
|
||||
* 短信登录转换器
|
||||
*/
|
||||
public class OAuth2ResourceOwnerSmsAuthenticationConverter implements AuthenticationConverter {
|
||||
public class OAuth2ResourceOwnerSmsAuthenticationConverter
|
||||
extends OAuth2ResourceOwnerBaseAuthenticationConverter<OAuth2ResourceOwnerSmsAuthenticationToken> {
|
||||
|
||||
/**
|
||||
* 是否支持此convert
|
||||
* @param grantType 授权类型
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean support(String grantType) {
|
||||
return SecurityConstants.APP.equals(grantType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication convert(HttpServletRequest request) {
|
||||
|
||||
// grant_type (REQUIRED)
|
||||
String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);
|
||||
if (!SecurityConstants.APP.equals(grantType)) {
|
||||
return null;
|
||||
}
|
||||
public OAuth2ResourceOwnerSmsAuthenticationToken buildToken(Authentication clientPrincipal, Set requestedScopes,
|
||||
Map additionalParameters) {
|
||||
return new OAuth2ResourceOwnerSmsAuthenticationToken(new AuthorizationGrantType(SecurityConstants.APP),
|
||||
clientPrincipal, requestedScopes, additionalParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验扩展参数 密码模式密码必须不为空
|
||||
* @param request 参数列表
|
||||
*/
|
||||
@Override
|
||||
public void checkParams(HttpServletRequest request) {
|
||||
MultiValueMap<String, String> parameters = OAuth2EndpointUtils.getParameters(request);
|
||||
|
||||
// scope (OPTIONAL)
|
||||
String scope = parameters.getFirst(OAuth2ParameterNames.SCOPE);
|
||||
if (StringUtils.hasText(scope) && parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) {
|
||||
OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SCOPE,
|
||||
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
|
||||
}
|
||||
|
||||
Set<String> requestedScopes = null;
|
||||
if (StringUtils.hasText(scope)) {
|
||||
requestedScopes = new HashSet<>(Arrays.asList(StringUtils.delimitedListToStringArray(scope, " ")));
|
||||
}
|
||||
|
||||
// phone (REQUIRED)
|
||||
// PHONE (REQUIRED)
|
||||
String phone = parameters.getFirst(SecurityConstants.SMS_PARAMETER_NAME);
|
||||
if (!StringUtils.hasText(phone) || parameters.get(SecurityConstants.SMS_PARAMETER_NAME).size() != 1) {
|
||||
OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, SecurityConstants.SMS_PARAMETER_NAME,
|
||||
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
|
||||
}
|
||||
|
||||
Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (clientPrincipal == null) {
|
||||
OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ErrorCodes.INVALID_CLIENT,
|
||||
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
|
||||
}
|
||||
|
||||
Map<String, Object> additionalParameters = parameters.entrySet().stream()
|
||||
.filter(e -> !e.getKey().equals(OAuth2ParameterNames.GRANT_TYPE)
|
||||
&& !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);
|
||||
|
||||
// 这个扩展参数非常重要, PigDaoAuthenticationProvider 是否校验密码
|
||||
request.setAttribute(OAuth2ParameterNames.GRANT_TYPE, SecurityConstants.APP);
|
||||
return new OAuth2ResourceOwnerSmsAuthenticationToken(new AuthorizationGrantType(SecurityConstants.APP),
|
||||
clientPrincipal, requestedScopes, additionalParameters);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,36 +1,18 @@
|
||||
package com.pig4cloud.pig.auth.support.sms;
|
||||
|
||||
import com.pig4cloud.pig.auth.support.ScopeException;
|
||||
import com.pig4cloud.pig.auth.support.base.OAuth2ResourceOwnerBaseAuthenticationProvider;
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pig.common.security.util.OAuth2ErrorCodesExpand;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.security.authentication.*;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.oauth2.core.*;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.OAuth2Token;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
||||
import org.springframework.security.oauth2.server.authorization.context.ProviderContextHolder;
|
||||
import org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;
|
||||
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;
|
||||
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.time.Instant;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
@ -38,27 +20,13 @@ import java.util.function.Supplier;
|
||||
*
|
||||
* 短信登录的核心处理
|
||||
*/
|
||||
public class OAuth2ResourceOwnerSmsAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(OAuth2ResourceOwnerSmsAuthenticationProvider.class);
|
||||
|
||||
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1";
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;
|
||||
|
||||
private final AuthenticationManager authenticationManager;
|
||||
|
||||
private final MessageSourceAccessor messages = new MessageSourceAccessor(new SpringSecurityMessageSource(),
|
||||
Locale.CHINA);
|
||||
|
||||
@Deprecated
|
||||
private Supplier<String> refreshTokenGenerator;
|
||||
public class OAuth2ResourceOwnerSmsAuthenticationProvider
|
||||
extends OAuth2ResourceOwnerBaseAuthenticationProvider<OAuth2ResourceOwnerSmsAuthenticationToken> {
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the
|
||||
* provided parameters.
|
||||
* @param authenticationManager
|
||||
* @param authorizationService the authorization service
|
||||
* @param tokenGenerator the token generator
|
||||
* @since 0.2.3
|
||||
@ -66,216 +34,28 @@ public class OAuth2ResourceOwnerSmsAuthenticationProvider implements Authenticat
|
||||
public OAuth2ResourceOwnerSmsAuthenticationProvider(AuthenticationManager authenticationManager,
|
||||
OAuth2AuthorizationService authorizationService,
|
||||
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {
|
||||
Assert.notNull(authorizationService, "authorizationService cannot be null");
|
||||
Assert.notNull(tokenGenerator, "tokenGenerator cannot be null");
|
||||
this.authenticationManager = authenticationManager;
|
||||
this.authorizationService = authorizationService;
|
||||
this.tokenGenerator = tokenGenerator;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setRefreshTokenGenerator(Supplier<String> refreshTokenGenerator) {
|
||||
Assert.notNull(refreshTokenGenerator, "refreshTokenGenerator cannot be null");
|
||||
this.refreshTokenGenerator = refreshTokenGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs authentication with the same contract as
|
||||
* {@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
|
||||
* authentication of the passed <code>Authentication</code> object. In such a case,
|
||||
* the next <code>AuthenticationProvider</code> that supports the presented
|
||||
* <code>Authentication</code> class will be tried.
|
||||
* @throws AuthenticationException if authentication fails.
|
||||
*/
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
|
||||
OAuth2ResourceOwnerSmsAuthenticationToken resouceOwnerSmsAuthentication = (OAuth2ResourceOwnerSmsAuthenticationToken) authentication;
|
||||
|
||||
OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(
|
||||
resouceOwnerSmsAuthentication);
|
||||
|
||||
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
|
||||
|
||||
assert registeredClient != null;
|
||||
if (!registeredClient.getAuthorizationGrantTypes()
|
||||
.contains(new AuthorizationGrantType(SecurityConstants.APP))) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);
|
||||
}
|
||||
|
||||
Set<String> authorizedScopes;
|
||||
// Default to configured scopes
|
||||
if (!CollectionUtils.isEmpty(resouceOwnerSmsAuthentication.getScopes())) {
|
||||
for (String requestedScope : resouceOwnerSmsAuthentication.getScopes()) {
|
||||
if (!registeredClient.getScopes().contains(requestedScope)) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_SCOPE);
|
||||
}
|
||||
}
|
||||
authorizedScopes = new LinkedHashSet<>(resouceOwnerSmsAuthentication.getScopes());
|
||||
}
|
||||
else {
|
||||
throw new ScopeException(OAuth2ErrorCodesExpand.SCOPE_IS_EMPTY);
|
||||
}
|
||||
|
||||
Map<String, Object> reqParameters = resouceOwnerSmsAuthentication.getAdditionalParameters();
|
||||
String username = (String) reqParameters.get(SecurityConstants.SMS_PARAMETER_NAME);
|
||||
|
||||
try {
|
||||
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
|
||||
username, null);
|
||||
LOGGER.debug("got usernamePasswordAuthenticationToken=" + usernamePasswordAuthenticationToken);
|
||||
|
||||
Authentication usernamePasswordAuthentication = authenticationManager
|
||||
.authenticate(usernamePasswordAuthenticationToken);
|
||||
|
||||
// @formatter:off
|
||||
DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()
|
||||
.registeredClient(registeredClient)
|
||||
.principal(usernamePasswordAuthentication)
|
||||
.providerContext(ProviderContextHolder.getProviderContext())
|
||||
.authorizedScopes(authorizedScopes)
|
||||
.authorizationGrantType(new AuthorizationGrantType(SecurityConstants.APP))
|
||||
.authorizationGrant(resouceOwnerSmsAuthentication);
|
||||
// @formatter:on
|
||||
|
||||
OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization
|
||||
.withRegisteredClient(registeredClient).principalName(usernamePasswordAuthentication.getName())
|
||||
.authorizationGrantType(new AuthorizationGrantType(SecurityConstants.APP))
|
||||
.attribute(OAuth2Authorization.AUTHORIZED_SCOPE_ATTRIBUTE_NAME, authorizedScopes);
|
||||
|
||||
// ----- Access token -----
|
||||
OAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build();
|
||||
OAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext);
|
||||
if (generatedAccessToken == null) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
|
||||
"The token generator failed to generate the access token.", ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
|
||||
generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(),
|
||||
generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes());
|
||||
if (generatedAccessToken instanceof ClaimAccessor) {
|
||||
authorizationBuilder.id(accessToken.getTokenValue())
|
||||
.token(accessToken,
|
||||
(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME,
|
||||
((ClaimAccessor) generatedAccessToken).getClaims()))
|
||||
.attribute(OAuth2Authorization.AUTHORIZED_SCOPE_ATTRIBUTE_NAME, authorizedScopes)
|
||||
.attribute(Principal.class.getName(), usernamePasswordAuthentication);
|
||||
}
|
||||
else {
|
||||
authorizationBuilder.id(accessToken.getTokenValue()).accessToken(accessToken);
|
||||
}
|
||||
|
||||
// ----- Refresh token -----
|
||||
OAuth2RefreshToken refreshToken = null;
|
||||
if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN) &&
|
||||
// Do not issue refresh token to public client
|
||||
!clientPrincipal.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.NONE)) {
|
||||
|
||||
if (this.refreshTokenGenerator != null) {
|
||||
Instant issuedAt = Instant.now();
|
||||
Instant expiresAt = issuedAt.plus(registeredClient.getTokenSettings().getRefreshTokenTimeToLive());
|
||||
refreshToken = new OAuth2RefreshToken(this.refreshTokenGenerator.get(), issuedAt, expiresAt);
|
||||
}
|
||||
else {
|
||||
tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();
|
||||
OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);
|
||||
if (!(generatedRefreshToken instanceof OAuth2RefreshToken)) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
|
||||
"The token generator failed to generate the refresh token.", ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
refreshToken = (OAuth2RefreshToken) generatedRefreshToken;
|
||||
}
|
||||
authorizationBuilder.refreshToken(refreshToken);
|
||||
}
|
||||
|
||||
OAuth2Authorization authorization = authorizationBuilder.build();
|
||||
|
||||
this.authorizationService.save(authorization);
|
||||
|
||||
LOGGER.debug("returning OAuth2AccessTokenAuthenticationToken");
|
||||
|
||||
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken,
|
||||
refreshToken, authorization.getAccessToken().getClaims());
|
||||
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOGGER.error("problem in authenticate", ex);
|
||||
throw oAuth2AuthenticationException(authentication, (AuthenticationException) ex);
|
||||
}
|
||||
|
||||
super(authenticationManager, authorizationService, tokenGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> authentication) {
|
||||
boolean supports = OAuth2ResourceOwnerSmsAuthenticationToken.class.isAssignableFrom(authentication);
|
||||
LOGGER.debug("supports authentication=" + authentication + " returning " + supports);
|
||||
return supports;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录异常转换为oauth2异常
|
||||
* @param authentication 身份验证
|
||||
* @param authenticationException 身份验证异常
|
||||
* @return {@link OAuth2AuthenticationException}
|
||||
*/
|
||||
private OAuth2AuthenticationException oAuth2AuthenticationException(Authentication authentication,
|
||||
AuthenticationException authenticationException) {
|
||||
if (authenticationException instanceof UsernameNotFoundException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USERNAME_NOT_FOUND,
|
||||
this.messages.getMessage("JdbcDaoImpl.notFound", new Object[] { authentication.getName() },
|
||||
"Username {0} not found"),
|
||||
""));
|
||||
@Override
|
||||
public void checkClient(RegisteredClient registeredClient) {
|
||||
assert registeredClient != null;
|
||||
if (!registeredClient.getAuthorizationGrantTypes()
|
||||
.contains(new AuthorizationGrantType(SecurityConstants.APP))) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);
|
||||
}
|
||||
if (authenticationException instanceof BadCredentialsException) {
|
||||
return new OAuth2AuthenticationException(
|
||||
new OAuth2Error(OAuth2ErrorCodesExpand.BAD_CREDENTIALS, this.messages.getMessage(
|
||||
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), ""));
|
||||
}
|
||||
if (authenticationException instanceof LockedException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USER_LOCKED, this.messages
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.locked", "User account is locked"), ""));
|
||||
}
|
||||
if (authenticationException instanceof DisabledException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USER_DISABLE,
|
||||
this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.disabled", "User is disabled"),
|
||||
""));
|
||||
}
|
||||
if (authenticationException instanceof AccountExpiredException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USER_EXPIRED, this.messages
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.expired", "User account has expired"), ""));
|
||||
}
|
||||
if (authenticationException instanceof CredentialsExpiredException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.CREDENTIALS_EXPIRED,
|
||||
this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.credentialsExpired",
|
||||
"User credentials have expired"),
|
||||
""));
|
||||
}
|
||||
if (authenticationException instanceof ScopeException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_SCOPE,
|
||||
this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "invalid_scope"), ""));
|
||||
}
|
||||
return new OAuth2AuthenticationException(OAuth2ErrorCodesExpand.UN_KNOW_LOGIN_ERROR);
|
||||
}
|
||||
|
||||
private OAuth2ClientAuthenticationToken getAuthenticatedClientElseThrowInvalidClient(
|
||||
Authentication authentication) {
|
||||
|
||||
OAuth2ClientAuthenticationToken clientPrincipal = null;
|
||||
|
||||
if (OAuth2ClientAuthenticationToken.class.isAssignableFrom(authentication.getPrincipal().getClass())) {
|
||||
clientPrincipal = (OAuth2ClientAuthenticationToken) authentication.getPrincipal();
|
||||
}
|
||||
|
||||
if (clientPrincipal != null && clientPrincipal.isAuthenticated()) {
|
||||
return clientPrincipal;
|
||||
}
|
||||
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);
|
||||
@Override
|
||||
public UsernamePasswordAuthenticationToken buildToken(Map<String, Object> reqParameters) {
|
||||
String phone = (String) reqParameters.get(SecurityConstants.SMS_PARAMETER_NAME);
|
||||
return new UsernamePasswordAuthenticationToken(phone, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,80 +1,21 @@
|
||||
package com.pig4cloud.pig.auth.support.sms;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import com.pig4cloud.pig.auth.support.base.OAuth2ResourceOwnerBaseAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @description 短信登录token信息
|
||||
*/
|
||||
public class OAuth2ResourceOwnerSmsAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = -6067207202119450764L;
|
||||
|
||||
private final AuthorizationGrantType authorizationGrantType;
|
||||
|
||||
private final Authentication clientPrincipal;
|
||||
|
||||
private final Set<String> scopes;
|
||||
|
||||
private final Map<String, Object> additionalParameters;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2ClientCredentialsAuthenticationToken} using the provided
|
||||
* parameters.
|
||||
* @param clientPrincipal the authenticated client principal
|
||||
*/
|
||||
public class OAuth2ResourceOwnerSmsAuthenticationToken extends OAuth2ResourceOwnerBaseAuthenticationToken {
|
||||
|
||||
public OAuth2ResourceOwnerSmsAuthenticationToken(AuthorizationGrantType authorizationGrantType,
|
||||
Authentication clientPrincipal, @Nullable Set<String> scopes,
|
||||
@Nullable Map<String, Object> additionalParameters) {
|
||||
super(Collections.emptyList());
|
||||
Assert.notNull(authorizationGrantType, "authorizationGrantType cannot be null");
|
||||
Assert.notNull(clientPrincipal, "clientPrincipal cannot be null");
|
||||
this.authorizationGrantType = authorizationGrantType;
|
||||
this.clientPrincipal = clientPrincipal;
|
||||
this.scopes = Collections.unmodifiableSet(scopes != null ? new HashSet<>(scopes) : Collections.emptySet());
|
||||
this.additionalParameters = Collections.unmodifiableMap(
|
||||
additionalParameters != null ? new HashMap<>(additionalParameters) : Collections.emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authorization grant type.
|
||||
* @return the authorization grant type
|
||||
*/
|
||||
public AuthorizationGrantType getGrantType() {
|
||||
return this.authorizationGrantType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return this.clientPrincipal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the requested scope(s).
|
||||
* @return the requested scope(s), or an empty {@code Set} if not available
|
||||
*/
|
||||
public Set<String> getScopes() {
|
||||
return this.scopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the additional parameters.
|
||||
* @return the additional parameters
|
||||
*/
|
||||
public Map<String, Object> getAdditionalParameters() {
|
||||
return this.additionalParameters;
|
||||
Authentication clientPrincipal, Set<String> scopes, Map<String, Object> additionalParameters) {
|
||||
super(authorizationGrantType, clientPrincipal, scopes, additionalParameters);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 短信模式
|
||||
*/
|
||||
package com.pig4cloud.pig.auth.support.sms;
|
@ -22,7 +22,7 @@ import java.util.Optional;
|
||||
* @date 2022/5/28
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class CustomOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
|
||||
public class PigCustomOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
@ -29,7 +29,7 @@ import org.springframework.security.oauth2.server.resource.introspection.OpaqueT
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2020-06-23
|
||||
* @date 2022-06-02
|
||||
*/
|
||||
@EnableConfigurationProperties(PermitAllUrlProperties.class)
|
||||
@RequiredArgsConstructor
|
||||
@ -56,7 +56,7 @@ public class PigResourceServerAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public OpaqueTokenIntrospector opaqueTokenIntrospector(OAuth2AuthorizationService authorizationService) {
|
||||
return new CustomOpaqueTokenIntrospector(authorizationService);
|
||||
return new PigCustomOpaqueTokenIntrospector(authorizationService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
@ -9,7 +9,7 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2021/10/16
|
||||
* @date 2022-06-02
|
||||
*/
|
||||
public class PigTokenStoreAutoConfiguration {
|
||||
|
||||
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pig.common.security.util;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2019/2/1 认证授权相关工具类
|
||||
*/
|
||||
@Slf4j
|
||||
@UtilityClass
|
||||
public class AuthUtils {
|
||||
|
||||
private final String BASIC_ = "Basic ";
|
||||
|
||||
/**
|
||||
* 从header 请求中的clientId/clientsecect
|
||||
* @param header header中的参数
|
||||
*/
|
||||
@SneakyThrows
|
||||
public String[] extractAndDecodeHeader(String header) {
|
||||
|
||||
byte[] base64Token = header.substring(6).getBytes("UTF-8");
|
||||
byte[] decoded;
|
||||
try {
|
||||
decoded = Base64.decode(base64Token);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
throw new RuntimeException("Failed to decode basic authentication token");
|
||||
}
|
||||
|
||||
String token = new String(decoded, CharsetUtil.UTF_8);
|
||||
|
||||
int delim = token.indexOf(":");
|
||||
|
||||
if (delim == -1) {
|
||||
throw new RuntimeException("Invalid basic authentication token");
|
||||
}
|
||||
return new String[] { token.substring(0, delim), token.substring(delim + 1) };
|
||||
}
|
||||
|
||||
/**
|
||||
* *从header 请求中的clientId/clientsecect
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
@SneakyThrows
|
||||
public String[] extractAndDecodeHeader(HttpServletRequest request) {
|
||||
String header = request.getHeader(HttpHeaders.AUTHORIZATION);
|
||||
|
||||
if (header == null || !header.startsWith(BASIC_)) {
|
||||
throw new RuntimeException("请求头中client信息为空");
|
||||
}
|
||||
|
||||
return extractAndDecodeHeader(header);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.pig4cloud.pig.common.security.util;
|
||||
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
/**
|
||||
* @author jumuning
|
||||
* @description ScopeException 异常信息
|
||||
*/
|
||||
public class ScopeException extends AuthenticationException {
|
||||
|
||||
/**
|
||||
* Constructs a <code>ScopeException</code> with the specified message.
|
||||
* @param msg the detail message.
|
||||
*/
|
||||
public ScopeException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code ScopeException} with the specified message and root cause.
|
||||
* @param msg the detail message.
|
||||
* @param cause root cause
|
||||
*/
|
||||
public ScopeException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user