refactor: 优化认证及客户端相关代码

This commit is contained in:
2024-12-27 20:29:36 +08:00
parent c870014730
commit 438615f87c
27 changed files with 160 additions and 226 deletions

View File

@@ -23,7 +23,7 @@ import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import top.continew.admin.auth.model.req.AuthReq;
import top.continew.admin.auth.model.req.LoginReq;
import top.continew.admin.common.context.RoleContext;
import top.continew.admin.common.context.UserContext;
import top.continew.admin.common.context.UserContextHolder;
@@ -37,7 +37,7 @@ import top.continew.admin.system.service.OptionService;
import top.continew.admin.system.service.RoleService;
import top.continew.admin.system.service.UserService;
import top.continew.starter.core.validation.CheckUtils;
import top.continew.starter.core.validation.ValidationUtils;
import top.continew.starter.core.validation.Validator;
import top.continew.starter.web.util.SpringWebUtils;
import java.util.Set;
@@ -46,14 +46,14 @@ import java.util.concurrent.CompletableFuture;
import static top.continew.admin.system.enums.PasswordPolicyEnum.PASSWORD_EXPIRATION_DAYS;
/**
* 认证处理器基类
* 登录处理器基类
*
* @author KAI
* @author Charles7c
* @since 2024/12/22 14:52
*/
@Component
public abstract class AbstractAuthHandler<T extends AuthReq> implements AuthHandler<T> {
public abstract class AbstractLoginHandler<T extends LoginReq> implements LoginHandler<T> {
@Resource
protected OptionService optionService;
@@ -73,7 +73,7 @@ public abstract class AbstractAuthHandler<T extends AuthReq> implements AuthHand
@Override
public void preLogin(T req, ClientResp client, HttpServletRequest request) {
// 参数校验
ValidationUtils.validate(req);
Validator.validate(req);
}
@Override
@@ -100,18 +100,15 @@ public abstract class AbstractAuthHandler<T extends AuthReq> implements AuthHand
UserContext userContext = new UserContext(permissionFuture.join(), roleFuture
.join(), passwordExpirationDaysFuture.join());
BeanUtil.copyProperties(user, userContext);
// 登录并缓存用户信息
// 设置登录配置参数
SaLoginModel model = new SaLoginModel();
// 指定此次登录 token 最低活跃频率单位如未指定则使用全局配置的 activeTimeout
model.setActiveTimeout(client.getActiveTimeout());
// 指定此次登录 token 有效期单位 如未指定自动取全局配置的 timeout
model.setTimeout(client.getTimeout());
// 客户端类型
model.setDevice(client.getClientType());
userContext.setClientType(client.getClientType());
// 客户端 ID
model.setExtra(CLIENT_ID, client.getClientId());
userContext.setClientId(client.getClientId());
// 登录并缓存用户信息
StpUtil.login(userContext.getId(), model.setExtraData(BeanUtil.beanToMap(new UserExtraContext(SpringWebUtils
.getRequest()))));
UserContextHolder.setContext(userContext);

View File

@@ -18,18 +18,18 @@ package top.continew.admin.auth;
import jakarta.servlet.http.HttpServletRequest;
import top.continew.admin.auth.enums.AuthTypeEnum;
import top.continew.admin.auth.model.req.AuthReq;
import top.continew.admin.auth.model.req.LoginReq;
import top.continew.admin.auth.model.resp.LoginResp;
import top.continew.admin.system.model.resp.ClientResp;
/**
* 认证处理器
* 登录处理器
*
* @author KAI
* @author Charles7c
* @since 2024/12/22 14:52
*/
public interface AuthHandler<T extends AuthReq> {
public interface LoginHandler<T extends LoginReq> {
/**
* 登录

View File

@@ -19,27 +19,27 @@ package top.continew.admin.auth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import top.continew.admin.auth.enums.AuthTypeEnum;
import top.continew.admin.auth.model.req.AuthReq;
import top.continew.admin.auth.model.req.LoginReq;
import java.util.HashMap;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
/**
* 认证策略
* 登录处理器工厂
*
* @author KAI
* @author Charles7c
* @since 2024/12/20 15:16
*/
@Component
public class AuthStrategy {
public class LoginHandlerFactory {
private final Map<AuthTypeEnum, AuthHandler<? extends AuthReq>> handlerMap = new HashMap<>();
private final Map<AuthTypeEnum, LoginHandler<? extends LoginReq>> handlerMap = new EnumMap<>(AuthTypeEnum.class);
@Autowired
public AuthStrategy(List<AuthHandler<? extends AuthReq>> handlers) {
for (AuthHandler<? extends AuthReq> handler : handlers) {
public LoginHandlerFactory(List<LoginHandler<? extends LoginReq>> handlers) {
for (LoginHandler<? extends LoginReq> handler : handlers) {
handlerMap.put(handler.getAuthType(), handler);
}
}
@@ -50,7 +50,7 @@ public class AuthStrategy {
* @param authType 认证类型
* @return 认证处理器
*/
public AuthHandler<AuthReq> getHandler(AuthTypeEnum authType) {
return (AuthHandler<AuthReq>)handlerMap.get(authType);
public LoginHandler<LoginReq> getHandler(AuthTypeEnum authType) {
return (LoginHandler<LoginReq>)handlerMap.get(authType);
}
}

View File

@@ -22,9 +22,9 @@ import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import top.continew.admin.auth.AbstractAuthHandler;
import top.continew.admin.auth.AbstractLoginHandler;
import top.continew.admin.auth.enums.AuthTypeEnum;
import top.continew.admin.auth.model.req.AccountAuthReq;
import top.continew.admin.auth.model.req.AccountLoginReq;
import top.continew.admin.auth.model.resp.LoginResp;
import top.continew.admin.common.constant.CacheConstants;
import top.continew.admin.common.constant.SysConstants;
@@ -40,7 +40,7 @@ import top.continew.starter.core.validation.ValidationUtils;
import java.time.Duration;
/**
* 账号认证处理器
* 账号登录处理器
*
* @author KAI
* @author Charles7c
@@ -48,12 +48,12 @@ import java.time.Duration;
*/
@Component
@RequiredArgsConstructor
public class AccountAuthHandler extends AbstractAuthHandler<AccountAuthReq> {
public class AccountLoginHandler extends AbstractLoginHandler<AccountLoginReq> {
private final PasswordEncoder passwordEncoder;
@Override
public LoginResp login(AccountAuthReq req, ClientResp client, HttpServletRequest request) {
public LoginResp login(AccountLoginReq req, ClientResp client, HttpServletRequest request) {
// 解密密码
String rawPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(req.getPassword()));
ValidationUtils.throwIfBlank(rawPassword, "密码解密失败");
@@ -72,7 +72,7 @@ public class AccountAuthHandler extends AbstractAuthHandler<AccountAuthReq> {
}
@Override
public void preLogin(AccountAuthReq req, ClientResp client, HttpServletRequest request) {
public void preLogin(AccountLoginReq req, ClientResp client, HttpServletRequest request) {
super.preLogin(req, client, request);
// 校验验证码
int loginCaptchaEnabled = optionService.getValueByCode2Int("LOGIN_CAPTCHA_ENABLED");

View File

@@ -18,9 +18,9 @@ package top.continew.admin.auth.handler;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import top.continew.admin.auth.AbstractAuthHandler;
import top.continew.admin.auth.AbstractLoginHandler;
import top.continew.admin.auth.enums.AuthTypeEnum;
import top.continew.admin.auth.model.req.EmailAuthReq;
import top.continew.admin.auth.model.req.EmailLoginReq;
import top.continew.admin.auth.model.resp.LoginResp;
import top.continew.admin.common.constant.CacheConstants;
import top.continew.admin.system.model.entity.UserDO;
@@ -29,17 +29,17 @@ import top.continew.starter.cache.redisson.util.RedisUtils;
import top.continew.starter.core.validation.ValidationUtils;
/**
* 邮箱认证处理器
* 邮箱登录处理器
*
* @author KAI
* @author Charles7c
* @since 2024/12/22 14:58
*/
@Component
public class EmailAuthHandler extends AbstractAuthHandler<EmailAuthReq> {
public class EmailLoginHandler extends AbstractLoginHandler<EmailLoginReq> {
@Override
public LoginResp login(EmailAuthReq req, ClientResp client, HttpServletRequest request) {
public LoginResp login(EmailLoginReq req, ClientResp client, HttpServletRequest request) {
// 验证邮箱
UserDO user = userService.getByEmail(req.getEmail());
ValidationUtils.throwIfNull(user, "此邮箱未绑定本系统账号");
@@ -51,7 +51,7 @@ public class EmailAuthHandler extends AbstractAuthHandler<EmailAuthReq> {
}
@Override
public void preLogin(EmailAuthReq req, ClientResp client, HttpServletRequest request) {
public void preLogin(EmailLoginReq req, ClientResp client, HttpServletRequest request) {
String email = req.getEmail();
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + email;
String captcha = RedisUtils.get(captchaKey);

View File

@@ -18,9 +18,9 @@ package top.continew.admin.auth.handler;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import top.continew.admin.auth.AbstractAuthHandler;
import top.continew.admin.auth.AbstractLoginHandler;
import top.continew.admin.auth.enums.AuthTypeEnum;
import top.continew.admin.auth.model.req.PhoneAuthReq;
import top.continew.admin.auth.model.req.PhoneLoginReq;
import top.continew.admin.auth.model.resp.LoginResp;
import top.continew.admin.common.constant.CacheConstants;
import top.continew.admin.system.model.entity.UserDO;
@@ -29,17 +29,17 @@ import top.continew.starter.cache.redisson.util.RedisUtils;
import top.continew.starter.core.validation.ValidationUtils;
/**
* 手机号认证处理器
* 手机号登录处理器
*
* @author KAI
* @author Charles7c
* @since 2024/12/22 14:59
*/
@Component
public class PhoneAuthHandler extends AbstractAuthHandler<PhoneAuthReq> {
public class PhoneLoginHandler extends AbstractLoginHandler<PhoneLoginReq> {
@Override
public LoginResp login(PhoneAuthReq req, ClientResp client, HttpServletRequest request) {
public LoginResp login(PhoneLoginReq req, ClientResp client, HttpServletRequest request) {
// 验证手机号
UserDO user = userService.getByPhone(req.getPhone());
ValidationUtils.throwIfNull(user, "此手机号未绑定本系统账号");
@@ -51,7 +51,7 @@ public class PhoneAuthHandler extends AbstractAuthHandler<PhoneAuthReq> {
}
@Override
public void preLogin(PhoneAuthReq req, ClientResp client, HttpServletRequest request) {
public void preLogin(PhoneLoginReq req, ClientResp client, HttpServletRequest request) {
String phone = req.getPhone();
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + phone;
String captcha = RedisUtils.get(captchaKey);

View File

@@ -31,12 +31,13 @@ import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthRequest;
import org.springframework.stereotype.Component;
import top.continew.admin.auth.AbstractAuthHandler;
import top.continew.admin.auth.AbstractLoginHandler;
import top.continew.admin.auth.enums.AuthTypeEnum;
import top.continew.admin.auth.model.req.SocialAuthReq;
import top.continew.admin.auth.model.req.SocialLoginReq;
import top.continew.admin.auth.model.resp.LoginResp;
import top.continew.admin.common.constant.RegexConstants;
import top.continew.admin.common.constant.SysConstants;
import top.continew.admin.common.enums.DisEnableStatusEnum;
import top.continew.admin.common.enums.GenderEnum;
import top.continew.admin.system.enums.MessageTemplateEnum;
import top.continew.admin.system.enums.MessageTypeEnum;
@@ -58,14 +59,15 @@ import java.util.Collections;
import java.util.List;
/**
* 第三方账号认证处理器
* 第三方账号登录处理器
*
* @author KAI
* @author Charles7c
* @since 2024/12/25 14:21
*/
@Component
@RequiredArgsConstructor
public class SocialAuthHandler extends AbstractAuthHandler<SocialAuthReq> {
public class SocialLoginHandler extends AbstractLoginHandler<SocialLoginReq> {
private final AuthRequestFactory authRequestFactory;
private final UserSocialService userSocialService;
@@ -74,7 +76,7 @@ public class SocialAuthHandler extends AbstractAuthHandler<SocialAuthReq> {
private final ProjectProperties projectProperties;
@Override
public LoginResp login(SocialAuthReq req, ClientResp client, HttpServletRequest request) {
public LoginResp login(SocialLoginReq req, ClientResp client, HttpServletRequest request) {
// 获取第三方登录信息
AuthRequest authRequest = this.getAuthRequest(req.getSource());
AuthCallback callback = new AuthCallback();
@@ -105,7 +107,9 @@ public class SocialAuthHandler extends AbstractAuthHandler<SocialAuthReq> {
user.setGender(GenderEnum.valueOf(authUser.getGender().name()));
user.setAvatar(authUser.getAvatar());
user.setDeptId(SysConstants.SUPER_DEPT_ID);
Long userId = userService.add(user);
user.setStatus(DisEnableStatusEnum.ENABLE);
userService.save(user);
Long userId = user.getId();
RoleDO role = roleService.getByCode(SysConstants.SUPER_ROLE_CODE);
userRoleService.assignRolesToUser(Collections.singletonList(role.getId()), userId);
userSocial = new UserSocialDO();
@@ -127,7 +131,7 @@ public class SocialAuthHandler extends AbstractAuthHandler<SocialAuthReq> {
}
@Override
public void preLogin(SocialAuthReq req, ClientResp client, HttpServletRequest request) {
public void preLogin(SocialLoginReq req, ClientResp client, HttpServletRequest request) {
super.preLogin(req, client, request);
if (StpUtil.isLogin()) {
StpUtil.logout();

View File

@@ -23,14 +23,14 @@ import lombok.Data;
import java.io.Serial;
/**
* 账号认证参数
* 账号登录参数
*
* @author Charles7c
* @since 2022/12/21 20:43
*/
@Data
@Schema(description = "账号认证参数")
public class AccountAuthReq extends AuthReq {
@Schema(description = "账号登录参数")
public class AccountLoginReq extends LoginReq {
@Serial
private static final long serialVersionUID = 1L;

View File

@@ -26,14 +26,14 @@ import org.hibernate.validator.constraints.Length;
import java.io.Serial;
/**
* 邮箱认证参数
* 邮箱登录参数
*
* @author Charles7c
* @since 2023/10/23 20:15
*/
@Data
@Schema(description = "邮箱认证参数")
public class EmailAuthReq extends AuthReq {
@Schema(description = "邮箱登录参数")
public class EmailLoginReq extends LoginReq {
@Serial
private static final long serialVersionUID = 1L;

View File

@@ -28,7 +28,7 @@ import java.io.Serial;
import java.io.Serializable;
/**
* 认证参数基类
* 登录参数基类
*
* @author KAI
* @author Charles7c
@@ -36,11 +36,12 @@ import java.io.Serializable;
*/
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "authType", visible = true)
@JsonSubTypes({@JsonSubTypes.Type(value = AccountAuthReq.class, name = "ACCOUNT"),
@JsonSubTypes.Type(value = EmailAuthReq.class, name = "EMAIL"),
@JsonSubTypes.Type(value = PhoneAuthReq.class, name = "PHONE"),
@JsonSubTypes.Type(value = SocialAuthReq.class, name = "SOCIAL")})
public class AuthReq implements Serializable {
@JsonSubTypes({@JsonSubTypes.Type(value = AccountLoginReq.class, name = "ACCOUNT"),
@JsonSubTypes.Type(value = EmailLoginReq.class, name = "EMAIL"),
@JsonSubTypes.Type(value = PhoneLoginReq.class, name = "PHONE"),
@JsonSubTypes.Type(value = SocialLoginReq.class, name = "SOCIAL")})
@Schema(description = "基础登录参数")
public class LoginReq implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@@ -48,14 +49,14 @@ public class AuthReq implements Serializable {
/**
* 客户端 ID
*/
@Schema(description = "客户端 ID")
@Schema(description = "客户端 ID", example = "ef51c9a3e9046c4f2ea45142c8a8344a")
@NotBlank(message = "客户端ID不能为空")
private String clientId;
/**
* 认证类型
*/
@Schema(description = "认证类型")
@Schema(description = "认证类型", example = "ACCOUNT")
@NotNull(message = "认证类型非法")
private AuthTypeEnum authType;
}

View File

@@ -26,14 +26,14 @@ import org.hibernate.validator.constraints.Length;
import java.io.Serial;
/**
* 手机号认证参数
* 手机号登录参数
*
* @author Charles7c
* @since 2023/10/26 22:37
*/
@Data
@Schema(description = "手机号认证参数")
public class PhoneAuthReq extends AuthReq {
@Schema(description = "手机号登录参数")
public class PhoneLoginReq extends LoginReq {
@Serial
private static final long serialVersionUID = 1L;

View File

@@ -23,15 +23,15 @@ import lombok.Data;
import java.io.Serial;
/**
* 第三方账号认证参数
* 第三方账号登录参数
*
* @author KAI
* @author Charles7c
* @since 2024/12/25 15:43
*/
@Data
@Schema(description = "第三方账号认证参数")
public class SocialAuthReq extends AuthReq {
@Schema(description = "第三方账号登录参数")
public class SocialLoginReq extends LoginReq {
@Serial
private static final long serialVersionUID = 1L;

View File

@@ -17,7 +17,7 @@
package top.continew.admin.auth.service;
import jakarta.servlet.http.HttpServletRequest;
import top.continew.admin.auth.model.req.AuthReq;
import top.continew.admin.auth.model.req.LoginReq;
import top.continew.admin.auth.model.resp.LoginResp;
import top.continew.admin.auth.model.resp.RouteResp;
@@ -38,7 +38,7 @@ public interface AuthService {
* @param request 请求对象
* @return 登录响应参数
*/
LoginResp login(AuthReq req, HttpServletRequest request);
LoginResp login(LoginReq req, HttpServletRequest request);
/**
* 构建路由树

View File

@@ -24,10 +24,10 @@ import cn.hutool.core.lang.tree.TreeUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import top.continew.admin.auth.AuthHandler;
import top.continew.admin.auth.AuthStrategy;
import top.continew.admin.auth.LoginHandler;
import top.continew.admin.auth.LoginHandlerFactory;
import top.continew.admin.auth.enums.AuthTypeEnum;
import top.continew.admin.auth.model.req.AuthReq;
import top.continew.admin.auth.model.req.LoginReq;
import top.continew.admin.auth.model.resp.LoginResp;
import top.continew.admin.auth.model.resp.RouteResp;
import top.continew.admin.auth.service.AuthService;
@@ -58,14 +58,14 @@ import java.util.Set;
@RequiredArgsConstructor
public class AuthServiceImpl implements AuthService {
private final AuthStrategy authStrategy;
private final LoginHandlerFactory loginHandlerFactory;
private final ClientService clientService;
private final RoleService roleService;
private final MenuService menuService;
private final CrudProperties crudProperties;
@Override
public LoginResp login(AuthReq req, HttpServletRequest request) {
public LoginResp login(LoginReq req, HttpServletRequest request) {
AuthTypeEnum authType = req.getAuthType();
// 校验客户端
ClientResp client = clientService.getByClientId(req.getClientId());
@@ -74,13 +74,13 @@ public class AuthServiceImpl implements AuthService {
ValidationUtils.throwIf(!client.getAuthType().contains(authType.getValue()), "该客户端暂未授权 [{}] 认证", authType
.getDescription());
// 获取处理器
AuthHandler<AuthReq> authHandler = authStrategy.getHandler(authType);
LoginHandler<LoginReq> loginHandler = loginHandlerFactory.getHandler(authType);
// 登录前置处理
authHandler.preLogin(req, client, request);
loginHandler.preLogin(req, client, request);
// 登录
LoginResp loginResp = authHandler.login(req, client, request);
LoginResp loginResp = loginHandler.login(req, client, request);
// 登录后置处理
authHandler.postLogin(req, client, request);
loginHandler.postLogin(req, client, request);
return loginResp;
}

View File

@@ -30,6 +30,7 @@ import java.util.List;
* 客户端实体
*
* @author KAI
* @author Charles7c
* @since 2024/12/03 16:04
*/
@Data
@@ -66,17 +67,17 @@ public class ClientDO extends BaseDO {
private String clientType;
/**
* Token最低活跃频率-1不限制)
* Token 最低活跃频率(单位:秒,-1不限制,永不冻结
*/
private Integer activeTimeout;
private Long activeTimeout;
/**
* Token有效期默认30天单位:秒)
* Token 有效期(单位:秒-1永不过期
*/
private Integer timeout;
private Long timeout;
/**
* 状态1启用2禁用
* 状态
*/
private DisEnableStatusEnum status;
}

View File

@@ -30,6 +30,7 @@ import java.util.List;
* 客户端查询条件
*
* @author KAI
* @author Charles7c
* @since 2024/12/03 16:04
*/
@Data
@@ -40,28 +41,28 @@ public class ClientQuery implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 客户端Key
* 客户端 Key
*/
@Schema(description = "客户端Key")
@Schema(description = "客户端 Key", example = "PC")
private String clientKey;
/**
* 客户端秘钥
*/
@Schema(description = "客户端秘钥")
@Schema(description = "客户端秘钥", example = "dd77ab1e353a027e0d60ce3b151e8642")
private String clientSecret;
/**
* 认证类型
*/
@Schema(description = "认证类型")
@Schema(description = "认证类型", example = "ACCOUNT")
@Query(type = QueryType.IN)
private List<String> authType;
/**
* 客户端类型
*/
@Schema(description = "客户端类型")
@Schema(description = "客户端类型", example = "PC")
private String clientType;
/**

View File

@@ -18,7 +18,7 @@ package top.continew.admin.system.model.req;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import top.continew.admin.common.enums.DisEnableStatusEnum;
@@ -31,6 +31,7 @@ import java.util.List;
* 创建或修改客户端参数
*
* @author KAI
* @author Charles7c
* @since 2024/12/03 16:04
*/
@Data
@@ -41,15 +42,9 @@ public class ClientReq extends BaseReq {
private static final long serialVersionUID = 1L;
/**
* 客户端ID
* 客户端 Key
*/
@Schema(description = "客户端ID")
private String clientId;
/**
* 客户端Key
*/
@Schema(description = "客户端Key")
@Schema(description = "客户端 Key", example = "PC")
@NotBlank(message = "客户端Key不能为空")
@Length(max = 32, message = "客户端Key长度不能超过 {max} 个字符")
private String clientKey;
@@ -57,7 +52,7 @@ public class ClientReq extends BaseReq {
/**
* 客户端秘钥
*/
@Schema(description = "客户端秘钥")
@Schema(description = "客户端秘钥", example = "dd77ab1e353a027e0d60ce3b151e8642")
@NotBlank(message = "客户端秘钥不能为空")
@Length(max = 255, message = "客户端秘钥长度不能超过 {max} 个字符")
private String clientSecret;
@@ -65,33 +60,39 @@ public class ClientReq extends BaseReq {
/**
* 认证类型
*/
@Schema(description = "认证类型")
@NotNull(message = "认证类型不能为空")
@Schema(description = "认证类型", example = "ACCOUNT")
@NotEmpty(message = "认证类型不能为空")
private List<String> authType;
/**
* 客户端类型
*/
@Schema(description = "客户端类型")
@Schema(description = "客户端类型", example = "PC")
@NotBlank(message = "客户端类型不能为空")
@Length(max = 32, message = "客户端类型长度不能超过 {max} 个字符")
private String clientType;
/**
* Token最低活跃频率-1不限制)
* Token 最低活跃频率(单位:秒,-1不限制,永不冻结
*/
@Schema(description = "Token最低活跃频率-1不限制")
private Integer activeTimeout;
@Schema(description = "Token 最低活跃频率(单位:秒,-1不限制,永不冻结)", example = "1800")
private Long activeTimeout;
/**
* Token有效期默认30天单位:秒)
* Token 有效期(单位:秒-1永不过期
*/
@Schema(description = "Token有效期默认30天单位")
private Integer timeout;
@Schema(description = "Token 有效期(单位:秒,-1永不过期", example = "86400")
private Long timeout;
/**
* 状态
*/
@Schema(description = "状态", example = "1")
private DisEnableStatusEnum status;
/**
* 客户端 ID
*/
@Schema(hidden = true)
private String clientId;
}

View File

@@ -19,7 +19,6 @@ package top.continew.admin.system.model.req.user;
import cn.hutool.core.lang.RegexPool;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import lombok.Data;
import org.hibernate.validator.constraints.Length;

View File

@@ -16,13 +16,10 @@
package top.continew.admin.system.model.resp;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import top.continew.admin.common.enums.DisEnableStatusEnum;
import top.continew.starter.extension.crud.model.resp.BaseDetailResp;
import top.continew.starter.file.excel.converter.ExcelBaseEnumConverter;
import java.io.Serial;
import java.util.List;
@@ -34,7 +31,6 @@ import java.util.List;
* @since 2024/12/03 16:04
*/
@Data
@ExcelIgnoreUnannotated
@Schema(description = "客户端详情信息")
public class ClientDetailResp extends BaseDetailResp {
@@ -45,55 +41,47 @@ public class ClientDetailResp extends BaseDetailResp {
* 客户端ID
*/
@Schema(description = "客户端ID")
@ExcelProperty(value = "客户端ID", order = 2)
private String clientId;
/**
* 客户端Key
*/
@Schema(description = "客户端Key")
@ExcelProperty(value = "客户端Key", order = 3)
private String clientKey;
/**
* 客户端秘钥
*/
@Schema(description = "客户端秘钥")
@ExcelProperty(value = "客户端秘钥", order = 4)
private String clientSecret;
/**
* 登录类型
*/
@Schema(description = "登录类型")
@ExcelProperty(value = "登录类型", order = 5)
private List<String> authType;
/**
* 客户端类型
*/
@Schema(description = "客户端类型")
@ExcelProperty(value = "客户端类型", order = 6)
private String clientType;
/**
* Token最低活跃频率-1为不限制
*/
@Schema(description = "Token最低活跃频率-1为不限制")
@ExcelProperty(value = "Token最低活跃频率-1为不限制", order = 7)
private Integer activeTimeout;
/**
* Token有效期默认30天单位
*/
@Schema(description = "Token有效期默认30天单位")
@ExcelProperty(value = "Token有效期默认30天单位", order = 8)
private Integer timeout;
/**
* 状态
*/
@Schema(description = "状态", example = "1")
@ExcelProperty(value = "状态", converter = ExcelBaseEnumConverter.class, order = 9)
private DisEnableStatusEnum status;
}

View File

@@ -28,6 +28,7 @@ import java.util.List;
* 客户端信息
*
* @author KAI
* @author Charles7c
* @since 2024/12/03 16:04
*/
@Data
@@ -38,50 +39,50 @@ public class ClientResp extends BaseResp {
private static final long serialVersionUID = 1L;
/**
* 客户端ID
* 客户端 ID
*/
@Schema(description = "客户端ID")
@Schema(description = "客户端 ID", example = "ef51c9a3e9046c4f2ea45142c8a8344a")
private String clientId;
/**
* 客户端Key
* 客户端 Key
*/
@Schema(description = "客户端Key")
@Schema(description = "客户端 Key", example = "PC")
private String clientKey;
/**
* 客户端秘钥
*/
@Schema(description = "客户端秘钥")
@Schema(description = "客户端秘钥", example = "dd77ab1e353a027e0d60ce3b151e8642")
private String clientSecret;
/**
* 认证类型
*/
@Schema(description = "认证类型")
@Schema(description = "认证类型", example = "ACCOUNT")
private List<String> authType;
/**
* 客户端类型
*/
@Schema(description = "客户端类型")
@Schema(description = "客户端类型", example = "PC")
private String clientType;
/**
* Token最低活跃频率-1不限制)
* Token 最低活跃频率(单位:秒,-1不限制,永不冻结
*/
@Schema(description = "Token最低活跃频率-1不限制")
@Schema(description = "Token 最低活跃频率(单位:秒,-1不限制,永不冻结)", example = "1800")
private Integer activeTimeout;
/**
* Token有效期默认30天单位:秒)
* Token 有效期(单位:秒-1永不过期
*/
@Schema(description = "Token有效期默认30天单位")
@Schema(description = "Token 有效期(单位:秒,-1永不过期", example = "86400")
private Integer timeout;
/**
* 状态1启用2禁用
* 状态
*/
@Schema(description = "状态")
@Schema(description = "状态", example = "1")
private DisEnableStatusEnum status;
}

View File

@@ -124,14 +124,6 @@ public interface UserService extends BaseService<UserResp, UserDetailResp, UserQ
*/
void updateEmail(String newEmail, String oldPassword, Long id);
/**
* 新增
*
* @param user 用户信息
* @return ID
*/
Long add(UserDO user);
/**
* 根据用户名查询
*

View File

@@ -439,13 +439,6 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
baseMapper.lambdaUpdate().set(UserDO::getEmail, newEmail).eq(UserDO::getId, id).update();
}
@Override
public Long add(UserDO user) {
user.setStatus(DisEnableStatusEnum.ENABLE);
baseMapper.insert(user);
return user.getId();
}
@Override
public UserDO getByUsername(String username) {
return baseMapper.selectByUsername(username);

View File

@@ -29,7 +29,7 @@ import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.scheduling.annotation.Async;
import top.continew.admin.auth.enums.AuthTypeEnum;
import top.continew.admin.auth.model.req.AccountAuthReq;
import top.continew.admin.auth.model.req.AccountLoginReq;
import top.continew.admin.common.constant.SysConstants;
import top.continew.admin.system.enums.LogStatusEnum;
import top.continew.admin.system.mapper.LogMapper;
@@ -148,7 +148,7 @@ public class LogDaoLocalImpl implements LogDao {
String requestBody = logRequest.getBody();
// 解析账号登录用户为操作人
if (requestBody.contains(AuthTypeEnum.ACCOUNT.getValue())) {
AccountAuthReq authReq = JSONUtil.toBean(requestBody, AccountAuthReq.class);
AccountLoginReq authReq = JSONUtil.toBean(requestBody, AccountLoginReq.class);
logDO.setCreateUser(ExceptionUtils.exToNull(() -> userService.getByUsername(authReq.getUsername())
.getId()));
return;

View File

@@ -19,23 +19,28 @@ package top.continew.admin.controller.auth;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.xkcoding.justauth.AuthRequestFactory;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import top.continew.admin.auth.model.req.AuthReq;
import top.continew.admin.auth.model.req.LoginReq;
import top.continew.admin.auth.model.resp.LoginResp;
import top.continew.admin.auth.model.resp.RouteResp;
import top.continew.admin.auth.model.resp.SocialAuthAuthorizeResp;
import top.continew.admin.auth.model.resp.UserInfoResp;
import top.continew.admin.auth.service.AuthService;
import top.continew.admin.common.context.UserContext;
import top.continew.admin.common.context.UserContextHolder;
import top.continew.admin.system.model.resp.user.UserDetailResp;
import top.continew.admin.system.service.UserService;
import top.continew.starter.core.exception.BadRequestException;
import top.continew.starter.log.annotation.Log;
import java.util.List;
@@ -56,11 +61,12 @@ public class AuthController {
private final AuthService authService;
private final UserService userService;
private final AuthRequestFactory authRequestFactory;
@SaIgnore
@Operation(summary = "登录", description = "用户登录")
@PostMapping("/login")
public LoginResp login(@Validated @RequestBody AuthReq req, HttpServletRequest request) {
public LoginResp login(@Validated @RequestBody LoginReq req, HttpServletRequest request) {
return authService.login(req, request);
}
@@ -73,6 +79,17 @@ public class AuthController {
return loginId;
}
@SaIgnore
@Operation(summary = "三方账号登录授权", description = "三方账号登录授权")
@Parameter(name = "source", description = "来源", example = "gitee", in = ParameterIn.PATH)
@GetMapping("/{source}")
public SocialAuthAuthorizeResp authorize(@PathVariable String source) {
AuthRequest authRequest = this.getAuthRequest(source);
return SocialAuthAuthorizeResp.builder()
.authorizeUrl(authRequest.authorize(AuthStateUtils.createState()))
.build();
}
@Log(ignore = true)
@Operation(summary = "获取用户信息", description = "获取登录用户信息")
@GetMapping("/user/info")
@@ -92,4 +109,12 @@ public class AuthController {
public List<RouteResp> listRoute() {
return authService.buildRouteTree(UserContextHolder.getUserId());
}
private AuthRequest getAuthRequest(String source) {
try {
return authRequestFactory.get(source);
} catch (Exception e) {
throw new BadRequestException("暂不支持 [%s] 平台账号登录".formatted(source));
}
}
}

View File

@@ -1,69 +0,0 @@
/*
* Copyright (c) 2022-present Charles7c 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 top.continew.admin.controller.auth;
import cn.dev33.satoken.annotation.SaIgnore;
import com.xkcoding.justauth.AuthRequestFactory;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import top.continew.admin.auth.model.resp.SocialAuthAuthorizeResp;
import top.continew.starter.core.exception.BadRequestException;
import top.continew.starter.log.annotation.Log;
/**
* 三方账号认证 API
*
* @author Charles7c
* @since 2023/10/8 22:52
*/
@Log(module = "登录")
@Tag(name = "三方账号认证 API")
@SaIgnore
@RestController
@RequiredArgsConstructor
@RequestMapping("/oauth")
public class SocialAuthController {
private final AuthRequestFactory authRequestFactory;
@Operation(summary = "三方账号登录授权", description = "三方账号登录授权")
@Parameter(name = "source", description = "来源", example = "gitee", in = ParameterIn.PATH)
@GetMapping("/{source}")
public SocialAuthAuthorizeResp authorize(@PathVariable String source) {
AuthRequest authRequest = this.getAuthRequest(source);
return SocialAuthAuthorizeResp.builder()
.authorizeUrl(authRequest.authorize(AuthStateUtils.createState()))
.build();
}
private AuthRequest getAuthRequest(String source) {
try {
return authRequestFactory.get(source);
} catch (Exception e) {
throw new BadRequestException("暂不支持 [%s] 平台账号登录".formatted(source));
}
}
}

View File

@@ -35,6 +35,6 @@ import top.continew.starter.extension.crud.enums.Api;
*/
@Tag(name = "客户端管理 API")
@RestController
@CrudRequestMapping(value = "/system/client", api = {Api.PAGE, Api.DETAIL, Api.ADD, Api.UPDATE, Api.DELETE, Api.EXPORT})
@CrudRequestMapping(value = "/system/client", api = {Api.PAGE, Api.DETAIL, Api.ADD, Api.UPDATE, Api.DELETE})
public class ClientController extends BaseController<ClientService, ClientResp, ClientDetailResp, ClientQuery, ClientReq> {
}

View File

@@ -127,17 +127,17 @@ knife4j:
--- ### Sa-Token 配置
sa-token:
# token 名称(同时也是 cookie 名称)
# Token 名称(同时也是 cookie 名称)
token-name: Authorization
# token 有效期(单位:秒,默认 30 天,-1 代表永不过期)
# Token 有效期(单位:秒,默认 30 天,-1 代表永不过期)
timeout: 86400
# token 最低活跃频率(单位:秒,默认 -1代表不限制永不冻结。如果 token 超过此时间没有访问系统就会被冻结)
# Token 最低活跃频率(单位:秒,默认 -1代表不限制永不冻结。如果 token 超过此时间没有访问系统就会被冻结)
active-timeout: 1800
# 是否打开自动续签(如果此值为 true框架会在每次直接或间接调用 getLoginId() 时进行一次过期检查与续签操作)
auto-renew: true
# 是否允许同一账号多地同时登录(为 true 时允许一起登录,为 false 时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时,是否共用一个 token为 true 时所有登录共用一个 token为 false 时每次登录新建一个 token
# 在多人登录同一账号时,是否共用一个 Token为 true 时所有登录共用一个 Token为 false 时每次登录新建一个 Token
is-share: false
# 是否输出操作日志
is-log: false