feat(auth): 新增MK OAuth认证模式支持
This commit is contained in:
+4
@@ -77,4 +77,8 @@ public interface OAuth2GranterConstant {
|
|||||||
*/
|
*/
|
||||||
String REGISTER = "register";
|
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, "认证信息错误或无效"),
|
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, "令牌刷新错误或无效"),
|
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不存在或无效。
|
* 客户端不存在 - 指定的客户端ID不存在或无效。
|
||||||
*/
|
*/
|
||||||
|
|||||||
+16
@@ -59,10 +59,26 @@ public interface OAuth2ErrorCode {
|
|||||||
* 用户租户未授权 - 指定的用户租户未授权。
|
* 用户租户未授权 - 指定的用户租户未授权。
|
||||||
*/
|
*/
|
||||||
int UNAUTHORIZED_USER_TENANT = 2006;
|
int UNAUTHORIZED_USER_TENANT = 2006;
|
||||||
|
/**
|
||||||
|
* 账号或密码不正确
|
||||||
|
*/
|
||||||
|
int ACCOUNT_OR_PASSWORD_ERROR = 2007;
|
||||||
/**
|
/**
|
||||||
* 令牌刷新错误或无效 - 刷新令牌认证信息错误或无效。
|
* 令牌刷新错误或无效 - 刷新令牌认证信息错误或无效。
|
||||||
*/
|
*/
|
||||||
int INVALID_REFRESH_TOKEN = 2010;
|
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不存在或无效。
|
* 客户端不存在 - 指定的客户端ID不存在或无效。
|
||||||
*/
|
*/
|
||||||
|
|||||||
+3
-1
@@ -25,6 +25,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.springblade.core.oauth2.granter;
|
package org.springblade.core.oauth2.granter;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springblade.core.launch.constant.TokenConstant;
|
import org.springblade.core.launch.constant.TokenConstant;
|
||||||
@@ -66,8 +67,9 @@ public abstract class AbstractTokenGranter implements TokenGranter {
|
|||||||
* 认证失败原因:密码匹配失败
|
* 认证失败原因:密码匹配失败
|
||||||
*/
|
*/
|
||||||
private static final String REASON_PASSWORD_MISMATCH = "密码匹配失败";
|
private static final String REASON_PASSWORD_MISMATCH = "密码匹配失败";
|
||||||
|
@Getter
|
||||||
private final OAuth2ClientService clientService;
|
private final OAuth2ClientService clientService;
|
||||||
|
@Getter
|
||||||
private final OAuth2UserService userService;
|
private final OAuth2UserService userService;
|
||||||
private final PasswordHandler passwordHandler;
|
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 WECHAT_APPLET -> properties.getGranter().getWechatApplet();
|
||||||
case SOCIAL -> properties.getGranter().getSocial();
|
case SOCIAL -> properties.getGranter().getSocial();
|
||||||
case REGISTER -> properties.getGranter().getRegister();
|
case REGISTER -> properties.getGranter().getRegister();
|
||||||
|
case MK -> properties.getGranter().getMk();
|
||||||
default -> true;
|
default -> true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -76,7 +76,7 @@ public abstract class AbstractAuthorizationHandler implements AuthorizationHandl
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public OAuth2Validation authValidation(OAuth2User user, OAuth2Request request) {
|
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();
|
return new OAuth2Validation();
|
||||||
}
|
}
|
||||||
if (Func.hasEmpty(user, user.getUserId())) {
|
if (Func.hasEmpty(user, user.getUserId())) {
|
||||||
|
|||||||
+1
-1
@@ -61,7 +61,7 @@ public class OAuth2AuthorizationHandler extends AbstractAuthorizationHandler {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void authFailure(OAuth2User user, OAuth2Request request, OAuth2Validation validation) {
|
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;
|
private String privateKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm4密钥
|
||||||
|
*/
|
||||||
|
private String sm4SecretKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 授权模式
|
* 授权模式
|
||||||
*/
|
*/
|
||||||
@@ -117,6 +122,10 @@ public class OAuth2Properties {
|
|||||||
* 是否开启注册模式
|
* 是否开启注册模式
|
||||||
*/
|
*/
|
||||||
private Boolean register = true;
|
private Boolean register = true;
|
||||||
|
/**
|
||||||
|
* 是否开启 mk oauth模式
|
||||||
|
*/
|
||||||
|
private Boolean mk = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+8
@@ -410,6 +410,14 @@ public class OAuth2Request {
|
|||||||
return SOCIAL.equals(getGrantType());
|
return SOCIAL.equals(getGrantType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否mk oauth模式
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Boolean isMk() {
|
||||||
|
return MK.equals(getGrantType());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置租户ID
|
* 设置租户ID
|
||||||
*
|
*
|
||||||
|
|||||||
+10
@@ -56,6 +56,9 @@ public class OAuth2ExceptionUtil {
|
|||||||
OAUTH2_EXCEPTION.put(
|
OAUTH2_EXCEPTION.put(
|
||||||
ExceptionCode.INVALID_USER, () -> new UserInvalidException(ExceptionCode.INVALID_USER.getMessage(), new Throwable())
|
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(
|
OAUTH2_EXCEPTION.put(
|
||||||
ExceptionCode.UNAUTHORIZED_USER, () -> new UserUnauthorizedException(ExceptionCode.UNAUTHORIZED_USER.getMessage(), new Throwable())
|
ExceptionCode.UNAUTHORIZED_USER, () -> new UserUnauthorizedException(ExceptionCode.UNAUTHORIZED_USER.getMessage(), new Throwable())
|
||||||
);
|
);
|
||||||
@@ -95,6 +98,13 @@ public class OAuth2ExceptionUtil {
|
|||||||
OAUTH2_EXCEPTION.put(
|
OAUTH2_EXCEPTION.put(
|
||||||
ExceptionCode.TEMPORARILY_UNAVAILABLE, () -> new OAuth2Exception(ExceptionCode.TEMPORARILY_UNAVAILABLE, ExceptionCode.TEMPORARILY_UNAVAILABLE.getMessage(), new Throwable())
|
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