fun.fengwk.convention4j.oauth2.server.service.mode.AuthenticationCodeMode Maven / Gradle / Ivy
package fun.fengwk.convention4j.oauth2.server.service.mode;
import fun.fengwk.convention4j.common.web.UriUtils;
import fun.fengwk.convention4j.oauth2.server.manager.OAuth2ClientManager;
import fun.fengwk.convention4j.oauth2.server.manager.OAuth2SubjectManager;
import fun.fengwk.convention4j.oauth2.server.model.AuthenticationCode;
import fun.fengwk.convention4j.oauth2.server.model.OAuth2Client;
import fun.fengwk.convention4j.oauth2.server.model.OAuth2Token;
import fun.fengwk.convention4j.oauth2.server.model.context.AuthenticationCodeTokenContext;
import fun.fengwk.convention4j.oauth2.server.model.context.AuthorizeContext;
import fun.fengwk.convention4j.oauth2.server.model.context.SsoContext;
import fun.fengwk.convention4j.oauth2.server.repo.AuthenticationCodeRepository;
import fun.fengwk.convention4j.oauth2.server.repo.OAuth2TokenRepository;
import fun.fengwk.convention4j.oauth2.share.constant.GrantType;
import fun.fengwk.convention4j.oauth2.share.constant.OAuth2ErrorCodes;
import fun.fengwk.convention4j.oauth2.share.constant.OAuth2Mode;
import fun.fengwk.convention4j.oauth2.share.constant.ResponseType;
import fun.fengwk.convention4j.oauth2.share.model.OAuth2TokenDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.util.Objects;
/**
* @author fengwk
*/
@Slf4j
public class AuthenticationCodeMode
implements OAuth2AuthorizeService, OAuth2TokenService {
private final AuthenticationCodeRepository authenticationCodeRepository;
private final BaseOAuth2AuthorizeService authorizeDelegate;
private final BaseOAuth2TokenService tokenDelegate;
public AuthenticationCodeMode(OAuth2ClientManager clientManager,
OAuth2SubjectManager subjectManager,
OAuth2TokenRepository oauth2TokenRepository,
AuthenticationCodeRepository authenticationCodeRepository) {
this.authenticationCodeRepository = authenticationCodeRepository;
OAuth2Mode mode = OAuth2Mode.AUTHORIZATION_CODE;
this.authorizeDelegate = new BaseOAuth2AuthorizeService<>(
clientManager, subjectManager, oauth2TokenRepository) {
@Override
protected ResponseType getResponseType() {
return mode.getResponseType();
}
@Override
protected URI generateAuthorizeUri(AuthorizeContext context,
OAuth2Client client,
UriComponentsBuilder redirectUriBuilder,
String subjectId) {
String ssoId = getSsoId(context);
boolean ssoAuthenticate = false;
if (context instanceof SsoContext ssoContext) {
ssoAuthenticate = ssoContext.isSsoAuthenticate();
}
String code = generateAuthenticationCode(getResponseType(), client, context.getRedirectUri(),
context.getScope(), subjectId, ssoId, ssoAuthenticate);
return buildAuthorizeUri(redirectUriBuilder, code, context.getState());
}
private URI buildAuthorizeUri(UriComponentsBuilder redirectUriBuilder, String code, String state) {
return redirectUriBuilder
.queryParam("code", code)
.queryParam("state", state)
.build().toUri();
}
};
this.tokenDelegate = new BaseOAuth2TokenService<>(
clientManager, subjectManager, oauth2TokenRepository) {
@Override
protected GrantType getGrantType() {
return mode.getGrantType();
}
@Override
protected OAuth2Token generateOAuth2Token(AuthenticationCodeTokenContext context, OAuth2Client client) {
AuthenticationCode authenticationCode = checkAndGetAuthenticationCode(context.getCode());
if (authenticationCode.isSsoAuthenticate()) {
// 单点登陆的情况,直接返回单点登陆对应的令牌
OAuth2Token oauth2Token = oauth2TokenRepository.getBySsoId(authenticationCode.getSsoId());
log.debug("Sso authenticate, ssoId: {}, oauth2Token: {}",
authenticationCode.getSsoId(), oauth2Token);
// 如果访问令牌已过期则刷新,确保返回的令牌是可用的
if (oauth2Token.accessTokenExpired(client.getAccessTokenExpireSeconds())) {
oauth2Token.refresh();
if (!oauth2TokenRepository.updateById(oauth2Token, client.getAuthorizeExpireSeconds())) {
log.warn("Refresh token failed, clientId: {}, refreshToken: {}",
client.getClientId(), oauth2Token.getRefreshToken());
throw OAuth2ErrorCodes.REFRESH_TOKEN_FAILED.asThrowable();
}
}
return oauth2Token;
} else {
// 走正常的登陆流程
checkRedirectUri(context.getRedirectUri(), authenticationCode);
OAuth2Token oauth2Token = generateToken(authenticationCode.getSubjectId(), authenticationCode.getScope(),
client, authenticationCode.getSsoId());
log.debug("Normal authenticate, oauth2Token: {}", oauth2Token);
return oauth2Token;
}
}
};
}
@Override
public String supportResponseType() {
return authorizeDelegate.supportResponseType();
}
@Override
public URI authorize(AuthorizeContext context) {
return authorizeDelegate.authorize(context);
}
@Override
public String supportGrantType() {
return tokenDelegate.supportGrantType();
}
@Override
public OAuth2TokenDTO token(AuthenticationCodeTokenContext context) {
return tokenDelegate.token(context);
}
private String generateAuthenticationCode(
ResponseType responseType, OAuth2Client client, String redirectUri,
String scope, String subjectId, String ssoId, boolean ssoAuthenticate) {
// 生成授权码
AuthenticationCode authenticationCode = AuthenticationCode.generate(
subjectId,
responseType,
client.getClientId(),
redirectUri,
scope,
ssoId,
ssoAuthenticate);
if (!authenticationCodeRepository.add(authenticationCode, client.getAuthorizationCodeExpireSeconds())) {
log.error("Add authenticationCode failed, authenticationCode: {}, authorizationCodeExpireSeconds: {}",
authenticationCode, client.getAuthorizationCodeExpireSeconds());
throw OAuth2ErrorCodes.GENERATE_AUTHENTICATION_CODE_FAILED.asThrowable();
}
// 返回授权码
return authenticationCode.getCode();
}
private AuthenticationCode checkAndGetAuthenticationCode(String code) {
AuthenticationCode authenticationCode = authenticationCodeRepository.get(code);
if (authenticationCode == null) {
log.warn("Invalid authentication code, code: {}", code);
throw OAuth2ErrorCodes.INVALID_AUTHENTICATION_CODE.asThrowable();
} else {
if (!authenticationCodeRepository.remove(code)) {
log.warn("Remove authentication code failed, code: {}", code);
}
}
return authenticationCode;
}
private void checkRedirectUri(String redirectUri, AuthenticationCode authenticationCode) {
UriComponents curUri = UriComponentsBuilder.fromUriString(
UriUtils.fullDecodeUriComponent(redirectUri)).build();
UriComponents storeUri = UriComponentsBuilder.fromUriString(
UriUtils.fullDecodeUriComponent(authenticationCode.getRedirectUri())).build();
if (!Objects.equals(curUri.getScheme(), storeUri.getScheme())
|| !Objects.equals(curUri.getUserInfo(), storeUri.getUserInfo())
|| !Objects.equals(curUri.getHost(), storeUri.getHost())
|| !Objects.equals(curUri.getPort(), storeUri.getPort())
|| !Objects.equals(curUri.getPath(), storeUri.getPath())
|| !Objects.equals(curUri.getFragment(), storeUri.getFragment())) {
log.warn("RedirectUri not match, currentRedirectUri: {}, storeRedirectUri: {}",
redirectUri, authenticationCode.getRedirectUri());
throw OAuth2ErrorCodes.INVALID_REDIRECT_URI.asThrowable();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy