package com.livk.auth.common.provider;
import com.livk.auth.common.core.exception.BadCaptchaException;
import com.livk.auth.common.token.OAuth2BaseAuthenticationToken;
import com.livk.auth.common.util.MessageSourceUtils;
import com.livk.auth.common.util.OAuth2AuthenticationProviderUtils;
import com.livk.auth.common.util.OAuth2ErrorCodesExpand;
import lombok.extern.slf4j.Slf4j;
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.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.JwtEncodingContext;
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;
/**
* <p>处理自定义授权</p>
*
* @author livk
*/
@Slf4j
public abstract class OAuth2BaseAuthenticationProvider<T extends OAuth2BaseAuthenticationToken> implements AuthenticationProvider {
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;
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 OAuth2BaseAuthenticationProvider(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;
// 国际化配置
this.messages = new MessageSourceAccessor(MessageSourceUtils.get(), Locale.CHINA);
}
@Deprecated
public void setRefreshTokenGenerator(Supplier<String> refreshTokenGenerator) {
Assert.notNull(refreshTokenGenerator, "refreshTokenGenerator cannot be null");
this.refreshTokenGenerator = refreshTokenGenerator;
}
/**
* 封装简易principal
*
* @param reqParameters
* @return
*/
public abstract UsernamePasswordAuthenticationToken assemble(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
*
* @param authentication the authentication request object.
* @throws AuthenticationException if authentication fails.
* @see org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationProvider#authenticate(Authentication)
* @see org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationProvider#authenticate(Authentication)
* {@link AuthenticationManager#authenticate(Authentication)} .
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
OAuth2BaseAuthenticationToken baseAuthentication = (OAuth2BaseAuthenticationToken) authentication;
OAuth2ClientAuthenticationToken clientPrincipal = OAuth2AuthenticationProviderUtils.getAuthenticatedClientElseThrowInvalidClient(baseAuthentication);
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
checkClient(registeredClient);
// Default to configured scopes
Set<String> authorizedScopes = Objects.requireNonNull(registeredClient).getScopes();
if (!CollectionUtils.isEmpty(baseAuthentication.getScopes())) {
if (baseAuthentication.getScopes().stream().noneMatch(scope -> registeredClient.getScopes().contains(scope))) {
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_SCOPE);
}
authorizedScopes = new LinkedHashSet<>(baseAuthentication.getScopes());
}
Map<String, Object> reqParameters = baseAuthentication.getAdditionalParameters();
try {
/* 自行构造UsernamePasswordAuthenticationToken,用来去数据库进行校验 */
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = assemble(reqParameters);
Authentication principal = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
// @formatter:off
DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()
.registeredClient(registeredClient)
.principal(principal)
.providerContext(ProviderContextHolder.getProviderContext())
.authorizedScopes(authorizedScopes)
.authorizationGrantType(baseAuthentication.getAuthorizationGrantType())
.authorizationGrant(baseAuthentication);
// @formatter:on
/* ACCESS TOKEN CONTEXT */
OAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build();
// -------------------- Access token start --------------------
OAuth2Token generatedAccessToken = Optional.ofNullable(this.tokenGenerator.generate(tokenContext)).orElseThrow(() ->
new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, "The token generator failed to generate the access token.", ERROR_URI)));
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(),
generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes());
// -------------------- Access token end --------------------
/**
* 封装OAuth2Authorization,给OAuth2AuthorizationService保存信息
评论0