feat(auth): 新增MK OAuth认证模式支持
This commit is contained in:
+4
@@ -77,4 +77,8 @@ public interface OAuth2GranterConstant {
|
||||
*/
|
||||
String REGISTER = "register";
|
||||
|
||||
/**
|
||||
* mk oauth模式
|
||||
*/
|
||||
String MK = "mk";
|
||||
}
|
||||
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
package org.springblade.core.oauth2.endpoint;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.oauth2.constant.OAuth2TokenConstant;
|
||||
import org.springblade.core.oauth2.exception.ExceptionCode;
|
||||
import org.springblade.core.oauth2.granter.TokenGranter;
|
||||
import org.springblade.core.oauth2.granter.TokenGranterFactory;
|
||||
import org.springblade.core.oauth2.handler.TokenHandler;
|
||||
import org.springblade.core.oauth2.provider.OAuth2Request;
|
||||
import org.springblade.core.oauth2.provider.OAuth2Token;
|
||||
import org.springblade.core.oauth2.service.OAuth2User;
|
||||
import org.springblade.core.oauth2.utils.OAuth2ExceptionUtil;
|
||||
import org.springblade.core.tool.support.Kv;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static org.springblade.core.oauth2.constant.OAuth2GranterConstant.MK;
|
||||
import static org.springblade.core.oauth2.constant.OAuth2ParameterConstant.USERNAME;
|
||||
|
||||
|
||||
/**
|
||||
* mk oauth认证端点
|
||||
* @author bfhuange
|
||||
* @date 2024/9/13
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "跳转mk认证", description = "跳转mk认证端点")
|
||||
public abstract class AbstractOAuth2MKEndpoint {
|
||||
|
||||
private final TokenGranterFactory granterFactory;
|
||||
private final TokenHandler tokenHandler;
|
||||
|
||||
/**
|
||||
* 返回登录页面
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/oauth/mk/login")
|
||||
public ResponseEntity<String> login(HttpServletRequest request, HttpServletResponse response) {
|
||||
String referer = request.getHeader("referer");
|
||||
if (!checkRefererUrl(referer)) {
|
||||
// 校验来源地址
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
||||
.body("非法来源地址");
|
||||
}
|
||||
// 返回登录页面
|
||||
return ResponseEntity.ok(generateLoginUrl(referer));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过mk授权码换取本系统token
|
||||
* @param mkCode
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/oauth/mk/token")
|
||||
public ResponseEntity<Kv> token(String mkCode) {
|
||||
log.info("mk 登录回调 mkCode:{}", mkCode);
|
||||
if (!StringUtils.hasText(mkCode)) {
|
||||
// 根据无效的mk code的错误代码抛出异常
|
||||
OAuth2ExceptionUtil.throwFromCode(ExceptionCode.INVALID_MK_CODE.getCode());
|
||||
}
|
||||
|
||||
// 将mk code 转换成erp的账号/手机号
|
||||
String account = getAccountByMkCode(mkCode);
|
||||
if (account == null) {
|
||||
OAuth2ExceptionUtil.throwFromCode(ExceptionCode.MK_ACCOUNT_NOT_FIND.getCode());
|
||||
}
|
||||
// 创建 OAuth2 请求对象并构建参数
|
||||
OAuth2Request request = OAuth2Request.create();
|
||||
// 默认管理组
|
||||
request.setTenantId(OAuth2TokenConstant.DEFAULT_TENANT_ID);
|
||||
// 只设置账号
|
||||
request.getParameterArgs().set(USERNAME, account);
|
||||
// 根据MK oauth的授权类型创建对应的 TokenGranter
|
||||
TokenGranter tokenGranter = granterFactory.create(MK);
|
||||
// 使用 TokenGranter 获取用户信息
|
||||
OAuth2User user = tokenGranter.user(request);
|
||||
// 创建令牌
|
||||
OAuth2Token token = tokenGranter.token(user, request);
|
||||
|
||||
// 对令牌进行增强处理
|
||||
OAuth2Token enhanceToken = tokenHandler.enhance(user, token, request);
|
||||
|
||||
// 返回增强后的令牌
|
||||
return ResponseEntity.ok(enhanceToken.getArgs());
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出消息
|
||||
* @param response
|
||||
* @param message
|
||||
*/
|
||||
protected void writeMessage(HttpServletResponse response, String message) {
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
|
||||
try(PrintWriter writer = response.getWriter()) {
|
||||
log.warn(message);
|
||||
writer.println(message);
|
||||
} catch (IOException e) {
|
||||
log.error(message + "异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成登录地址
|
||||
* @param refererUrl
|
||||
* @return
|
||||
*/
|
||||
protected abstract String generateLoginUrl(String refererUrl);
|
||||
|
||||
/**
|
||||
* 根据mk授权码获取账号
|
||||
* @param mkCode
|
||||
* @return
|
||||
*/
|
||||
protected abstract String getAccountByMkCode(String mkCode);
|
||||
|
||||
/**
|
||||
* 校验来源url,返回false,不跳转登录页面
|
||||
* @param refererUrl
|
||||
* @return
|
||||
*/
|
||||
protected abstract boolean checkRefererUrl(String refererUrl);
|
||||
}
|
||||
+20
@@ -74,6 +74,11 @@ public enum ExceptionCode implements Oauth2ExceptionCode {
|
||||
*/
|
||||
UNAUTHORIZED_USER(OAuth2ErrorCode.UNAUTHORIZED_USER, "认证信息错误或无效"),
|
||||
|
||||
/**
|
||||
* 用户未授权 - 账号或密码不正确。
|
||||
*/
|
||||
ACCOUNT_OR_PASSWORD_ERROR(OAuth2ErrorCode.ACCOUNT_OR_PASSWORD_ERROR, "账号或密码不正确"),
|
||||
|
||||
/**
|
||||
* 用户租户未授权 - 指定的用户租户未授权。
|
||||
*/
|
||||
@@ -84,6 +89,21 @@ public enum ExceptionCode implements Oauth2ExceptionCode {
|
||||
*/
|
||||
INVALID_REFRESH_TOKEN(OAuth2ErrorCode.INVALID_REFRESH_TOKEN, "令牌刷新错误或无效"),
|
||||
|
||||
/**
|
||||
* 用户未授权 - 无效的mk code
|
||||
*/
|
||||
INVALID_MK_CODE(OAuth2ErrorCode.INVALID_MK_CODE, "无效的mk code"),
|
||||
|
||||
/**
|
||||
* 用户未授权 - 获取mk账号错误
|
||||
*/
|
||||
MK_ACCOUNT_NOT_FIND(OAuth2ErrorCode.MK_ACCOUNT_FETCH_ERROR, "获取MK账号错误"),
|
||||
|
||||
/**
|
||||
* 用户未授权 - mk账号未同步本系统
|
||||
*/
|
||||
MK_ACCOUNT_NOT_SYNC_SYSTEM(OAuth2ErrorCode.MK_ACCOUNT_NOT_SYNC_SYSTEM, "MK账号【%s】未同步到本系统"),
|
||||
|
||||
/**
|
||||
* 客户端不存在 - 指定的客户端ID不存在或无效。
|
||||
*/
|
||||
|
||||
+16
@@ -59,10 +59,26 @@ public interface OAuth2ErrorCode {
|
||||
* 用户租户未授权 - 指定的用户租户未授权。
|
||||
*/
|
||||
int UNAUTHORIZED_USER_TENANT = 2006;
|
||||
/**
|
||||
* 账号或密码不正确
|
||||
*/
|
||||
int ACCOUNT_OR_PASSWORD_ERROR = 2007;
|
||||
/**
|
||||
* 令牌刷新错误或无效 - 刷新令牌认证信息错误或无效。
|
||||
*/
|
||||
int INVALID_REFRESH_TOKEN = 2010;
|
||||
/**
|
||||
* 无效的mk code
|
||||
*/
|
||||
int INVALID_MK_CODE = 2020;
|
||||
/**
|
||||
* 获取mk账号错误
|
||||
*/
|
||||
int MK_ACCOUNT_FETCH_ERROR = 2021;
|
||||
/**
|
||||
* mk账号未同步本系统
|
||||
*/
|
||||
int MK_ACCOUNT_NOT_SYNC_SYSTEM = 2022;
|
||||
/**
|
||||
* 客户端不存在 - 指定的客户端ID不存在或无效。
|
||||
*/
|
||||
|
||||
+3
-1
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
package org.springblade.core.oauth2.granter;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.launch.constant.TokenConstant;
|
||||
@@ -66,8 +67,9 @@ public abstract class AbstractTokenGranter implements TokenGranter {
|
||||
* 认证失败原因:密码匹配失败
|
||||
*/
|
||||
private static final String REASON_PASSWORD_MISMATCH = "密码匹配失败";
|
||||
|
||||
@Getter
|
||||
private final OAuth2ClientService clientService;
|
||||
@Getter
|
||||
private final OAuth2UserService userService;
|
||||
private final PasswordHandler passwordHandler;
|
||||
|
||||
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
package org.springblade.core.oauth2.granter;
|
||||
|
||||
import org.springblade.core.oauth2.exception.ExceptionCode;
|
||||
import org.springblade.core.oauth2.exception.OAuth2ErrorCode;
|
||||
import org.springblade.core.oauth2.exception.OAuth2Exception;
|
||||
import org.springblade.core.oauth2.handler.PasswordHandler;
|
||||
import org.springblade.core.oauth2.provider.OAuth2Request;
|
||||
import org.springblade.core.oauth2.service.OAuth2Client;
|
||||
import org.springblade.core.oauth2.service.OAuth2ClientService;
|
||||
import org.springblade.core.oauth2.service.OAuth2User;
|
||||
import org.springblade.core.oauth2.service.OAuth2UserService;
|
||||
import org.springblade.core.oauth2.utils.OAuth2ExceptionUtil;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* PasswordTokenGranter
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Component
|
||||
public class MKTokenGranter extends AbstractTokenGranter {
|
||||
/**
|
||||
* MK client id
|
||||
*/
|
||||
private static final String MK_CLIENT_ID = "mk-oauth";
|
||||
|
||||
public MKTokenGranter(OAuth2ClientService clientService, OAuth2UserService userService, PasswordHandler passwordHandler) {
|
||||
super(clientService, userService, passwordHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return MK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2User user(OAuth2Request request) {
|
||||
OAuth2User user = this.getUser(request);
|
||||
return Optional.ofNullable(this.enhancer)
|
||||
.map(enhancer -> enhancer.enhance(user, request))
|
||||
.orElse(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2Client client(OAuth2Request request) {
|
||||
// 直接使用固定的client id
|
||||
return getClientService().loadByClientId(MK_CLIENT_ID);
|
||||
}
|
||||
|
||||
private OAuth2User getUser(OAuth2Request request) {
|
||||
// 获取用户信息
|
||||
OAuth2User user = getUserService().loadByUsername(request.getUsername(), request);
|
||||
|
||||
// 用户不存在
|
||||
if (user == null) {
|
||||
String message = String.format(ExceptionCode.MK_ACCOUNT_NOT_SYNC_SYSTEM.getMessage(), request.getUsername());
|
||||
throw new OAuth2Exception(ExceptionCode.MK_ACCOUNT_NOT_SYNC_SYSTEM, message);
|
||||
}
|
||||
|
||||
// 校验用户信息
|
||||
if (!getUserService().validateUser(Objects.requireNonNull(user))) {
|
||||
OAuth2ExceptionUtil.throwFromCode(OAuth2ErrorCode.INVALID_USER);
|
||||
}
|
||||
|
||||
// 无需校验用户密码
|
||||
|
||||
//MK登进来检查是否是默认密码
|
||||
//MK登录跳过强制修改密码
|
||||
// user.setDefaultPwdFlag(false);
|
||||
|
||||
// 设置客户端信息
|
||||
user.setClient(client(request));
|
||||
|
||||
// 返回用户信息
|
||||
return user;
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -115,6 +115,7 @@ public class TokenGranterFactory {
|
||||
case WECHAT_APPLET -> properties.getGranter().getWechatApplet();
|
||||
case SOCIAL -> properties.getGranter().getSocial();
|
||||
case REGISTER -> properties.getGranter().getRegister();
|
||||
case MK -> properties.getGranter().getMk();
|
||||
default -> true;
|
||||
};
|
||||
}
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ public abstract class AbstractAuthorizationHandler implements AuthorizationHandl
|
||||
*/
|
||||
@Override
|
||||
public OAuth2Validation authValidation(OAuth2User user, OAuth2Request request) {
|
||||
if (request.isClientCredentials() || request.isImplicit() || request.isSocial()) {
|
||||
if (request.isClientCredentials() || request.isImplicit() || request.isSocial() || request.isMk()) {
|
||||
return new OAuth2Validation();
|
||||
}
|
||||
if (Func.hasEmpty(user, user.getUserId())) {
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ public class OAuth2AuthorizationHandler extends AbstractAuthorizationHandler {
|
||||
*/
|
||||
@Override
|
||||
public void authFailure(OAuth2User user, OAuth2Request request, OAuth2Validation validation) {
|
||||
|
||||
log.error("用户:{},认证失败,失败原因:{}", user.getAccount(), validation.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+9
@@ -65,6 +65,11 @@ public class OAuth2Properties {
|
||||
*/
|
||||
private String privateKey;
|
||||
|
||||
/**
|
||||
* sm4密钥
|
||||
*/
|
||||
private String sm4SecretKey;
|
||||
|
||||
/**
|
||||
* 授权模式
|
||||
*/
|
||||
@@ -117,6 +122,10 @@ public class OAuth2Properties {
|
||||
* 是否开启注册模式
|
||||
*/
|
||||
private Boolean register = true;
|
||||
/**
|
||||
* 是否开启 mk oauth模式
|
||||
*/
|
||||
private Boolean mk = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
@@ -410,6 +410,14 @@ public class OAuth2Request {
|
||||
return SOCIAL.equals(getGrantType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否mk oauth模式
|
||||
* @return
|
||||
*/
|
||||
public Boolean isMk() {
|
||||
return MK.equals(getGrantType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置租户ID
|
||||
*
|
||||
|
||||
+10
@@ -56,6 +56,9 @@ public class OAuth2ExceptionUtil {
|
||||
OAUTH2_EXCEPTION.put(
|
||||
ExceptionCode.INVALID_USER, () -> new UserInvalidException(ExceptionCode.INVALID_USER.getMessage(), new Throwable())
|
||||
);
|
||||
OAUTH2_EXCEPTION.put(
|
||||
ExceptionCode.ACCOUNT_OR_PASSWORD_ERROR, () -> new UserInvalidException(ExceptionCode.ACCOUNT_OR_PASSWORD_ERROR.getMessage())
|
||||
);
|
||||
OAUTH2_EXCEPTION.put(
|
||||
ExceptionCode.UNAUTHORIZED_USER, () -> new UserUnauthorizedException(ExceptionCode.UNAUTHORIZED_USER.getMessage(), new Throwable())
|
||||
);
|
||||
@@ -95,6 +98,13 @@ public class OAuth2ExceptionUtil {
|
||||
OAUTH2_EXCEPTION.put(
|
||||
ExceptionCode.TEMPORARILY_UNAVAILABLE, () -> new OAuth2Exception(ExceptionCode.TEMPORARILY_UNAVAILABLE, ExceptionCode.TEMPORARILY_UNAVAILABLE.getMessage(), new Throwable())
|
||||
);
|
||||
put(ExceptionCode.INVALID_MK_CODE);
|
||||
put(ExceptionCode.MK_ACCOUNT_NOT_FIND);
|
||||
put(ExceptionCode.MK_ACCOUNT_NOT_SYNC_SYSTEM);
|
||||
}
|
||||
|
||||
private static void put(ExceptionCode exceptionCode) {
|
||||
OAUTH2_EXCEPTION.put(exceptionCode, () -> new OAuth2Exception(exceptionCode, exceptionCode.getMessage()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user