From 99e4bdccccc8605603335b8c6565cc1b96d7f3f8 Mon Sep 17 00:00:00 2001 From: zlyx <1242874891@qq.com> Date: Sun, 18 Jun 2023 09:30:51 +0800 Subject: [PATCH] =?UTF-8?q?refactor=20=E9=87=8D=E6=9E=84=20Auth=20?= =?UTF-8?q?=E8=AE=A4=E8=AF=81=E6=A8=A1=E5=9D=97=20:=201.=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=20SysClient=20=E8=A1=A8,=20=E6=9B=B4=E6=96=B0=20sql?= =?UTF-8?q?=20=E6=96=87=E4=BB=B6=20;=202.=20=E4=BD=BF=E7=94=A8=E7=AD=96?= =?UTF-8?q?=E7=95=A5=E6=A8=A1=E5=BC=8F=E9=87=8D=E6=9E=84=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3,=20=E4=B8=8D=E5=90=8C=E6=8E=88=E6=9D=83?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E5=AF=B9=E5=BA=94=E4=B8=8D=E5=90=8C=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E7=AD=96=E7=95=A5=20;=203.=20=E6=9B=B4=E6=96=B0=20Log?= =?UTF-8?q?inBody,=20LoginVo=20=E5=AF=B9=E8=B1=A1=E5=AD=97=E6=AE=B5=20;=20?= =?UTF-8?q?4.=20=E6=96=B0=E5=A2=9E=E5=BC=82=E5=B8=B8=E7=B1=BB,=20=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E4=BF=A1=E6=81=AF=20;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/controller/AuthController.java | 84 ++---- .../org/dromara/web/domain/vo/LoginVo.java | 41 ++- .../org/dromara/web/enumd/AuthTypeEnumd.java | 53 ++++ .../org/dromara/web/factory/AuthFactory.java | 36 +++ .../dromara/web/service/IAuthStrategy.java | 24 ++ .../dromara/web/service/SysLoginService.java | 268 +++--------------- .../web/service/impl/EmailAuthStrategy.java | 104 +++++++ .../service/impl/PasswordAuthStrategy.java | 123 ++++++++ .../web/service/impl/SmsAuthStrategy.java | 104 +++++++ .../web/service/impl/XcxAuthStrategy.java | 82 ++++++ .../main/resources/i18n/messages.properties | 3 + .../resources/i18n/messages_en_US.properties | 3 + .../resources/i18n/messages_zh_CN.properties | 3 + .../common/core/domain/model/LoginBody.java | 66 ++++- .../user/AuthTypeErrorException.java | 19 ++ .../common/core/validate/auth/EmailGroup.java | 7 + .../core/validate/auth/PasswordGroup.java | 7 + .../common/core/validate/auth/SmsGroup.java | 7 + .../core/validate/auth/WechatGroup.java | 7 + .../org/dromara/system/domain/SysClient.java | 62 ++++ .../dromara/system/domain/bo/SysClientBo.java | 58 ++++ .../dromara/system/domain/vo/SysClientVo.java | 67 +++++ .../system/mapper/SysClientMapper.java | 15 + .../mapper/system/SysClientMapper.xml | 7 + script/sql/oracle/oracle_ry_vue_5.X.sql | 37 +++ script/sql/postgres/postgres_ry_vue_5.X.sql | 37 +++ script/sql/ry_vue_5.X.sql | 23 ++ script/sql/sqlserver/sqlserver_ry_vue_5.X.sql | 105 +++++++ 28 files changed, 1153 insertions(+), 299 deletions(-) create mode 100644 ruoyi-admin/src/main/java/org/dromara/web/enumd/AuthTypeEnumd.java create mode 100644 ruoyi-admin/src/main/java/org/dromara/web/factory/AuthFactory.java create mode 100644 ruoyi-admin/src/main/java/org/dromara/web/service/IAuthStrategy.java create mode 100644 ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java create mode 100644 ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java create mode 100644 ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java create mode 100644 ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java create mode 100644 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/user/AuthTypeErrorException.java create mode 100644 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/auth/EmailGroup.java create mode 100644 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/auth/PasswordGroup.java create mode 100644 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/auth/SmsGroup.java create mode 100644 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/auth/WechatGroup.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysClient.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysClientBo.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysClientVo.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysClientMapper.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysClientMapper.xml diff --git a/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java b/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java index bba6113da..b182f96fe 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java @@ -2,14 +2,9 @@ package org.dromara.web.controller; import cn.dev33.satoken.annotation.SaIgnore; import cn.hutool.core.collection.CollUtil; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.validation.constraints.NotBlank; -import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.R; -import org.dromara.common.core.domain.model.EmailLoginBody; import org.dromara.common.core.domain.model.LoginBody; import org.dromara.common.core.domain.model.RegisterBody; -import org.dromara.common.core.domain.model.SmsLoginBody; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; @@ -21,8 +16,12 @@ import org.dromara.system.service.ISysTenantService; import org.dromara.web.domain.vo.LoginTenantVo; import org.dromara.web.domain.vo.LoginVo; import org.dromara.web.domain.vo.TenantListVo; +import org.dromara.web.factory.AuthFactory; +import org.dromara.web.service.IAuthStrategy; import org.dromara.web.service.SysLoginService; import org.dromara.web.service.SysRegisterService; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -49,70 +48,23 @@ public class AuthController { /** * 登录方法 * - * @param body 登录信息 + * @param loginBody 登录信息 * @return 结果 */ @PostMapping("/login") - public R login(@Validated @RequestBody LoginBody body) { - LoginVo loginVo = new LoginVo(); - // 生成令牌 - String token = loginService.login( - body.getTenantId(), - body.getUsername(), body.getPassword(), - body.getCode(), body.getUuid()); - loginVo.setToken(token); - return R.ok(loginVo); - } - - /** - * 短信登录 - * - * @param body 登录信息 - * @return 结果 - */ - @PostMapping("/smsLogin") - public R smsLogin(@Validated @RequestBody SmsLoginBody body) { - LoginVo loginVo = new LoginVo(); - // 生成令牌 - String token = loginService.smsLogin( - body.getTenantId(), - body.getPhonenumber(), - body.getSmsCode()); - loginVo.setToken(token); - return R.ok(loginVo); - } - - /** - * 邮件登录 - * - * @param body 登录信息 - * @return 结果 - */ - @PostMapping("/emailLogin") - public R emailLogin(@Validated @RequestBody EmailLoginBody body) { - LoginVo loginVo = new LoginVo(); - // 生成令牌 - String token = loginService.emailLogin( - body.getTenantId(), - body.getEmail(), - body.getEmailCode()); - loginVo.setToken(token); - return R.ok(loginVo); - } - - /** - * 小程序登录(示例) - * - * @param xcxCode 小程序code - * @return 结果 - */ - @PostMapping("/xcxLogin") - public R xcxLogin(@NotBlank(message = "{xcx.code.not.blank}") String xcxCode) { - LoginVo loginVo = new LoginVo(); - // 生成令牌 - String token = loginService.xcxLogin(xcxCode); - loginVo.setToken(token); - return R.ok(loginVo); + public R login(@Validated @RequestBody LoginBody loginBody) { + // 校验类型和id + String clientId = loginBody.getClientId(); + String grantType = loginBody.getGrantType(); + IAuthStrategy authStrategy = AuthFactory.instance(grantType); + // 校验请求参数 + authStrategy.validate(loginBody); + // 校验授权类型和id + loginService.checkClientType(clientId, grantType); + // 校验租户 + loginService.checkTenant(loginBody.getTenantId()); + // 登录 + return R.ok(authStrategy.login(clientId, loginBody)); } /** diff --git a/ruoyi-admin/src/main/java/org/dromara/web/domain/vo/LoginVo.java b/ruoyi-admin/src/main/java/org/dromara/web/domain/vo/LoginVo.java index ef5dac0df..834afe5a1 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/domain/vo/LoginVo.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/domain/vo/LoginVo.java @@ -1,5 +1,6 @@ package org.dromara.web.domain.vo; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; /** @@ -10,6 +11,44 @@ import lombok.Data; @Data public class LoginVo { - private String token; + /** + * 授权令牌 + */ + @JsonProperty("access_token") + private String accessToken; + + /** + * 刷新令牌 + */ + @JsonProperty("refresh_token") + private String refreshToken; + + /** + * 授权令牌 access_token 的有效期 + */ + @JsonProperty("expire_in") + private Long expireIn; + + /** + * 刷新令牌 refresh_token 的有效期 + */ + @JsonProperty("refresh_expire_in") + private Long refreshExpireIn; + + /** + * 应用id + */ + @JsonProperty("client_id") + private String clientId; + + /** + * 令牌权限 + */ + private String scope; + + /** + * 用户 openid + */ + private String openid; } diff --git a/ruoyi-admin/src/main/java/org/dromara/web/enumd/AuthTypeEnumd.java b/ruoyi-admin/src/main/java/org/dromara/web/enumd/AuthTypeEnumd.java new file mode 100644 index 000000000..772f282a5 --- /dev/null +++ b/ruoyi-admin/src/main/java/org/dromara/web/enumd/AuthTypeEnumd.java @@ -0,0 +1,53 @@ +package org.dromara.web.enumd; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.dromara.web.service.impl.EmailAuthStrategy; +import org.dromara.web.service.impl.PasswordAuthStrategy; +import org.dromara.web.service.impl.SmsAuthStrategy; +import org.dromara.web.service.impl.XcxAuthStrategy; + +/** + * 授权类型枚举 + * + * @author Michelle.Chung + */ +@Getter +@AllArgsConstructor +public enum AuthTypeEnumd { + + /** + * 密码认证 + */ + PASSWORD("password", PasswordAuthStrategy.class), + + /** + * 短信认证 + */ + SMS("sms", SmsAuthStrategy.class), + + /** + * 邮件认证 + */ + EMAIL("email", EmailAuthStrategy.class), + + /** + * 邮件认证 + */ + XCX("xcx", XcxAuthStrategy.class) + ; + + private final String type; + + private final Class strategyClass; + + public static Class getStrategyClass(String type) { + for (AuthTypeEnumd clazz : values()) { + if (clazz.getType().equals(type)) { + return clazz.getStrategyClass(); + } + } + return null; + } + +} diff --git a/ruoyi-admin/src/main/java/org/dromara/web/factory/AuthFactory.java b/ruoyi-admin/src/main/java/org/dromara/web/factory/AuthFactory.java new file mode 100644 index 000000000..dcc05b54d --- /dev/null +++ b/ruoyi-admin/src/main/java/org/dromara/web/factory/AuthFactory.java @@ -0,0 +1,36 @@ +package org.dromara.web.factory; + +import cn.hutool.core.util.ObjectUtil; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.web.enumd.AuthTypeEnumd; +import org.dromara.web.service.IAuthStrategy; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 授权 Factory + * + * @author Michelle.Chung + */ +@Slf4j +public class AuthFactory { + + private static final Map AUTH_STRATEGY_MAP = new ConcurrentHashMap<>(); + + /** + * 实例化 + * + * @param authType 认证类型 + */ + public static IAuthStrategy instance(String authType) { + IAuthStrategy authStrategy = AUTH_STRATEGY_MAP.get(authType); + if (ObjectUtil.isNull(authStrategy)) { + authStrategy = (IAuthStrategy) SpringUtils.getBean(AuthTypeEnumd.getStrategyClass(authType)); + AUTH_STRATEGY_MAP.put(authType, authStrategy); + } + return authStrategy; + } + +} diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/IAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/IAuthStrategy.java new file mode 100644 index 000000000..970a38c8e --- /dev/null +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/IAuthStrategy.java @@ -0,0 +1,24 @@ +package org.dromara.web.service; + + +import org.dromara.common.core.domain.model.LoginBody; +import org.dromara.web.domain.vo.LoginVo; + +/** + * 授权策略 + * + * @author Michelle.Chung + */ +public interface IAuthStrategy { + + /** + * 参数校验 + */ + void validate(LoginBody loginBody); + + /** + * 登录 + */ + LoginVo login(String clientId, LoginBody loginBody); + +} diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java b/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java index c035d127b..ee49dad71 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java @@ -1,7 +1,6 @@ package org.dromara.web.service; import cn.dev33.satoken.exception.NotLoginException; -import cn.dev33.satoken.secure.BCrypt; import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.ObjectUtil; @@ -13,13 +12,9 @@ import org.dromara.common.core.constant.GlobalConstants; import org.dromara.common.core.constant.TenantConstants; import org.dromara.common.core.domain.dto.RoleDTO; import org.dromara.common.core.domain.model.LoginUser; -import org.dromara.common.core.domain.model.XcxLoginUser; -import org.dromara.common.core.enums.DeviceType; import org.dromara.common.core.enums.LoginType; import org.dromara.common.core.enums.TenantStatus; -import org.dromara.common.core.enums.UserStatus; -import org.dromara.common.core.exception.user.CaptchaException; -import org.dromara.common.core.exception.user.CaptchaExpireException; +import org.dromara.common.core.exception.user.AuthTypeErrorException; import org.dromara.common.core.exception.user.UserException; import org.dromara.common.core.utils.*; import org.dromara.common.log.event.LogininforEvent; @@ -27,10 +22,11 @@ import org.dromara.common.redis.utils.RedisUtils; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.tenant.exception.TenantException; import org.dromara.common.tenant.helper.TenantHelper; -import org.dromara.common.web.config.properties.CaptchaProperties; +import org.dromara.system.domain.SysClient; import org.dromara.system.domain.SysUser; import org.dromara.system.domain.vo.SysTenantVo; import org.dromara.system.domain.vo.SysUserVo; +import org.dromara.system.mapper.SysClientMapper; import org.dromara.system.mapper.SysUserMapper; import org.dromara.system.service.ISysPermissionService; import org.dromara.system.service.ISysTenantService; @@ -52,107 +48,16 @@ import java.util.function.Supplier; @Service public class SysLoginService { - private final SysUserMapper userMapper; - private final CaptchaProperties captchaProperties; - private final ISysPermissionService permissionService; - private final ISysTenantService tenantService; - @Value("${user.password.maxRetryCount}") private Integer maxRetryCount; @Value("${user.password.lockTime}") private Integer lockTime; - /** - * 登录验证 - * - * @param username 用户名 - * @param password 密码 - * @param code 验证码 - * @param uuid 唯一标识 - * @return 结果 - */ - public String login(String tenantId, String username, String password, String code, String uuid) { - boolean captchaEnabled = captchaProperties.getEnable(); - // 验证码开关 - if (captchaEnabled) { - validateCaptcha(tenantId, username, code, uuid); - } - // 校验租户 - checkTenant(tenantId); - - // 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可 - SysUserVo user = loadUserByUsername(tenantId, username); - checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword())); - // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 - LoginUser loginUser = buildLoginUser(user); - // 生成token - LoginHelper.loginByDevice(loginUser, DeviceType.PC); - - recordLogininfor(loginUser.getTenantId(), username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); - recordLoginInfo(user.getUserId()); - return StpUtil.getTokenValue(); - } - - public String smsLogin(String tenantId, String phonenumber, String smsCode) { - // 校验租户 - checkTenant(tenantId); - // 通过手机号查找用户 - SysUserVo user = loadUserByPhonenumber(tenantId, phonenumber); - - checkLogin(LoginType.SMS, tenantId, user.getUserName(), () -> !validateSmsCode(tenantId, phonenumber, smsCode)); - // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 - LoginUser loginUser = buildLoginUser(user); - // 生成token - LoginHelper.loginByDevice(loginUser, DeviceType.APP); - - recordLogininfor(loginUser.getTenantId(), user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); - recordLoginInfo(user.getUserId()); - return StpUtil.getTokenValue(); - } - - public String emailLogin(String tenantId, String email, String emailCode) { - // 校验租户 - checkTenant(tenantId); - // 通过邮箱查找用户 - SysUserVo user = loadUserByEmail(tenantId, email); - - checkLogin(LoginType.EMAIL, tenantId, user.getUserName(), () -> !validateEmailCode(tenantId, email, emailCode)); - // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 - LoginUser loginUser = buildLoginUser(user); - // 生成token - LoginHelper.loginByDevice(loginUser, DeviceType.APP); - - recordLogininfor(loginUser.getTenantId(), user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); - recordLoginInfo(user.getUserId()); - return StpUtil.getTokenValue(); - } - - - public String xcxLogin(String xcxCode) { - // xcxCode 为 小程序调用 wx.login 授权后获取 - // todo 以下自行实现 - // 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid - String openid = ""; - // 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可 - SysUserVo user = loadUserByOpenid(openid); - // 校验租户 - checkTenant(user.getTenantId()); - - // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 - XcxLoginUser loginUser = new XcxLoginUser(); - loginUser.setTenantId(user.getTenantId()); - loginUser.setUserId(user.getUserId()); - loginUser.setUsername(user.getUserName()); - loginUser.setUserType(user.getUserType()); - loginUser.setOpenid(openid); - // 生成token - LoginHelper.loginByDevice(loginUser, DeviceType.XCX); - - recordLogininfor(loginUser.getTenantId(), user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); - recordLoginInfo(user.getUserId()); - return StpUtil.getTokenValue(); - } + private final ISysTenantService tenantService; + private final ISysPermissionService permissionService; + private final SysUserMapper userMapper; + private final SysClientMapper clientMapper; /** * 退出登录 @@ -178,7 +83,7 @@ public class SysLoginService { * @param status 状态 * @param message 消息内容 */ - private void recordLogininfor(String tenantId, String username, String status, String message) { + public void recordLogininfor(String tenantId, String username, String status, String message) { LogininforEvent logininforEvent = new LogininforEvent(); logininforEvent.setTenantId(tenantId); logininforEvent.setUsername(username); @@ -188,123 +93,10 @@ public class SysLoginService { SpringUtils.context().publishEvent(logininforEvent); } - /** - * 校验短信验证码 - */ - private boolean validateSmsCode(String tenantId, String phonenumber, String smsCode) { - String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + phonenumber); - if (StringUtils.isBlank(code)) { - recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); - throw new CaptchaExpireException(); - } - return code.equals(smsCode); - } - - /** - * 校验邮箱验证码 - */ - private boolean validateEmailCode(String tenantId, String email, String emailCode) { - String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + email); - if (StringUtils.isBlank(code)) { - recordLogininfor(tenantId, email, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); - throw new CaptchaExpireException(); - } - return code.equals(emailCode); - } - - /** - * 校验验证码 - * - * @param username 用户名 - * @param code 验证码 - * @param uuid 唯一标识 - */ - public void validateCaptcha(String tenantId, String username, String code, String uuid) { - String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, ""); - String captcha = RedisUtils.getCacheObject(verifyKey); - RedisUtils.deleteObject(verifyKey); - if (captcha == null) { - recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); - throw new CaptchaExpireException(); - } - if (!code.equalsIgnoreCase(captcha)) { - recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")); - throw new CaptchaException(); - } - } - - private SysUserVo loadUserByUsername(String tenantId, String username) { - SysUser user = userMapper.selectOne(new LambdaQueryWrapper() - .select(SysUser::getUserName, SysUser::getStatus) - .eq(TenantHelper.isEnable(), SysUser::getTenantId, tenantId) - .eq(SysUser::getUserName, username)); - if (ObjectUtil.isNull(user)) { - log.info("登录用户:{} 不存在.", username); - throw new UserException("user.not.exists", username); - } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { - log.info("登录用户:{} 已被停用.", username); - throw new UserException("user.blocked", username); - } - if (TenantHelper.isEnable()) { - return userMapper.selectTenantUserByUserName(username, tenantId); - } - return userMapper.selectUserByUserName(username); - } - - private SysUserVo loadUserByPhonenumber(String tenantId, String phonenumber) { - SysUser user = userMapper.selectOne(new LambdaQueryWrapper() - .select(SysUser::getPhonenumber, SysUser::getStatus) - .eq(TenantHelper.isEnable(), SysUser::getTenantId, tenantId) - .eq(SysUser::getPhonenumber, phonenumber)); - if (ObjectUtil.isNull(user)) { - log.info("登录用户:{} 不存在.", phonenumber); - throw new UserException("user.not.exists", phonenumber); - } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { - log.info("登录用户:{} 已被停用.", phonenumber); - throw new UserException("user.blocked", phonenumber); - } - if (TenantHelper.isEnable()) { - return userMapper.selectTenantUserByPhonenumber(phonenumber, tenantId); - } - return userMapper.selectUserByPhonenumber(phonenumber); - } - - private SysUserVo loadUserByEmail(String tenantId, String email) { - SysUser user = userMapper.selectOne(new LambdaQueryWrapper() - .select(SysUser::getEmail, SysUser::getStatus) - .eq(TenantHelper.isEnable(), SysUser::getTenantId, tenantId) - .eq(SysUser::getEmail, email)); - if (ObjectUtil.isNull(user)) { - log.info("登录用户:{} 不存在.", email); - throw new UserException("user.not.exists", email); - } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { - log.info("登录用户:{} 已被停用.", email); - throw new UserException("user.blocked", email); - } - if (TenantHelper.isEnable()) { - return userMapper.selectTenantUserByEmail(email, tenantId); - } - return userMapper.selectUserByEmail(email); - } - - private SysUserVo loadUserByOpenid(String openid) { - // 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户 - // todo 自行实现 userService.selectUserByOpenid(openid); - SysUserVo user = new SysUserVo(); - if (ObjectUtil.isNull(user)) { - log.info("登录用户:{} 不存在.", openid); - // todo 用户不存在 业务逻辑自行实现 - } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { - log.info("登录用户:{} 已被停用.", openid); - // todo 用户已被停用 业务逻辑自行实现 - } - return user; - } - /** * 构建登录用户 */ - private LoginUser buildLoginUser(SysUserVo user) { + public LoginUser buildLoginUser(SysUserVo user) { LoginUser loginUser = new LoginUser(); loginUser.setTenantId(user.getTenantId()); loginUser.setUserId(user.getUserId()); @@ -336,28 +128,29 @@ public class SysLoginService { /** * 登录校验 */ - private void checkLogin(LoginType loginType, String tenantId, String username, Supplier supplier) { + public void checkLogin(LoginType loginType, String tenantId, String username, Supplier supplier) { String errorKey = GlobalConstants.PWD_ERR_CNT_KEY + username; String loginFail = Constants.LOGIN_FAIL; - // 获取用户登录错误次数,默认为0 (可自定义限制策略 例如: key + username + ip) - int errorNumber = ObjectUtil.defaultIfNull(RedisUtils.getCacheObject(errorKey), 0); + // 获取用户登录错误次数(可自定义限制策略 例如: key + username + ip) + Integer errorNumber = RedisUtils.getCacheObject(errorKey); // 锁定时间内登录 则踢出 - if (errorNumber >= maxRetryCount) { + if (ObjectUtil.isNotNull(errorNumber) && errorNumber.equals(maxRetryCount)) { recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime)); throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime); } if (supplier.get()) { - // 错误次数递增 - errorNumber++; - RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime)); + // 是否第一次 + errorNumber = ObjectUtil.isNull(errorNumber) ? 1 : errorNumber + 1; // 达到规定错误次数 则锁定登录 - if (errorNumber >= maxRetryCount) { + if (errorNumber.equals(maxRetryCount)) { + RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime)); recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime)); throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime); } else { - // 未达到规定错误次数 + // 未达到规定错误次数 则递增 + RedisUtils.setCacheObject(errorKey, errorNumber); recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber)); throw new UserException(loginType.getRetryLimitCount(), errorNumber); } @@ -367,7 +160,12 @@ public class SysLoginService { RedisUtils.deleteObject(errorKey); } - private void checkTenant(String tenantId) { + /** + * 校验租户 + * + * @param tenantId 租户ID + */ + public void checkTenant(String tenantId) { if (!TenantHelper.isEnable()) { return; } @@ -382,10 +180,24 @@ public class SysLoginService { log.info("登录租户:{} 已被停用.", tenantId); throw new TenantException("tenant.blocked"); } else if (ObjectUtil.isNotNull(tenant.getExpireTime()) - && new Date().after(tenant.getExpireTime())) { + && new Date().after(tenant.getExpireTime())) { log.info("登录租户:{} 已超过有效期.", tenantId); throw new TenantException("tenant.expired"); } } + /** + * 认证类型校验 + */ + public void checkClientType(String clientId, String grantType) { + Long count = clientMapper.selectCount( + new LambdaQueryWrapper() + .eq(SysClient::getClientId, clientId) + .like(SysClient::getGrantType, grantType)); + if (count == 0) { + log.info("客户端id:{} 认证类型:{} 不存在.", clientId, grantType); + throw new AuthTypeErrorException(); + } + } + } diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java new file mode 100644 index 000000000..e7daeb8e8 --- /dev/null +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java @@ -0,0 +1,104 @@ +package org.dromara.web.service.impl; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.constant.Constants; +import org.dromara.common.core.constant.GlobalConstants; +import org.dromara.common.core.domain.model.LoginBody; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.enums.DeviceType; +import org.dromara.common.core.enums.LoginType; +import org.dromara.common.core.enums.UserStatus; +import org.dromara.common.core.exception.user.CaptchaExpireException; +import org.dromara.common.core.exception.user.UserException; +import org.dromara.common.core.utils.MessageUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.ValidatorUtils; +import org.dromara.common.core.validate.auth.EmailGroup; +import org.dromara.common.redis.utils.RedisUtils; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.tenant.helper.TenantHelper; +import org.dromara.system.domain.SysUser; +import org.dromara.system.domain.vo.SysUserVo; +import org.dromara.system.mapper.SysUserMapper; +import org.dromara.web.domain.vo.LoginVo; +import org.dromara.web.service.IAuthStrategy; +import org.dromara.web.service.SysLoginService; +import org.springframework.stereotype.Service; + +/** + * 邮件认证策略 + * + * @author Michelle.Chung + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class EmailAuthStrategy implements IAuthStrategy { + + private final SysLoginService loginService; + private final SysUserMapper userMapper; + + @Override + public void validate(LoginBody loginBody) { + ValidatorUtils.validate(loginBody, EmailGroup.class); + } + + @Override + public LoginVo login(String clientId, LoginBody loginBody) { + String tenantId = loginBody.getTenantId(); + String email = loginBody.getEmail(); + String emailCode = loginBody.getEmailCode(); + + // 校验租户 + loginService.checkTenant(tenantId); + // 通过邮箱查找用户 + SysUserVo user = loadUserByEmail(tenantId, email); + + loginService.checkLogin(LoginType.EMAIL, tenantId, user.getUserName(), () -> !validateEmailCode(tenantId, email, emailCode)); + // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 + LoginUser loginUser = loginService.buildLoginUser(user); + // 生成token + LoginHelper.loginByDevice(loginUser, DeviceType.APP); + + loginService.recordLogininfor(loginUser.getTenantId(), user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); + loginService.recordLoginInfo(user.getUserId()); + LoginVo loginVo = new LoginVo(); + loginVo.setAccessToken(StpUtil.getTokenValue()); + return loginVo; + } + + /** + * 校验邮箱验证码 + */ + private boolean validateEmailCode(String tenantId, String email, String emailCode) { + String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + email); + if (StringUtils.isBlank(code)) { + loginService.recordLogininfor(tenantId, email, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); + throw new CaptchaExpireException(); + } + return code.equals(emailCode); + } + + private SysUserVo loadUserByEmail(String tenantId, String email) { + SysUser user = userMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getEmail, SysUser::getStatus) + .eq(TenantHelper.isEnable(), SysUser::getTenantId, tenantId) + .eq(SysUser::getEmail, email)); + if (ObjectUtil.isNull(user)) { + log.info("登录用户:{} 不存在.", email); + throw new UserException("user.not.exists", email); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", email); + throw new UserException("user.blocked", email); + } + if (TenantHelper.isEnable()) { + return userMapper.selectTenantUserByEmail(email, tenantId); + } + return userMapper.selectUserByEmail(email); + } + +} diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java new file mode 100644 index 000000000..3d9d66099 --- /dev/null +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java @@ -0,0 +1,123 @@ +package org.dromara.web.service.impl; + +import cn.dev33.satoken.secure.BCrypt; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.constant.Constants; +import org.dromara.common.core.constant.GlobalConstants; +import org.dromara.common.core.domain.model.LoginBody; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.enums.DeviceType; +import org.dromara.common.core.enums.LoginType; +import org.dromara.common.core.enums.UserStatus; +import org.dromara.common.core.exception.user.CaptchaException; +import org.dromara.common.core.exception.user.CaptchaExpireException; +import org.dromara.common.core.exception.user.UserException; +import org.dromara.common.core.utils.MessageUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.ValidatorUtils; +import org.dromara.common.core.validate.auth.PasswordGroup; +import org.dromara.common.redis.utils.RedisUtils; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.tenant.helper.TenantHelper; +import org.dromara.common.web.config.properties.CaptchaProperties; +import org.dromara.system.domain.SysUser; +import org.dromara.system.domain.vo.SysUserVo; +import org.dromara.system.mapper.SysUserMapper; +import org.dromara.web.domain.vo.LoginVo; +import org.dromara.web.service.IAuthStrategy; +import org.dromara.web.service.SysLoginService; +import org.springframework.stereotype.Service; + +/** + * 密码认证策略 + * + * @author Michelle.Chung + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class PasswordAuthStrategy implements IAuthStrategy { + + private final CaptchaProperties captchaProperties; + private final SysLoginService loginService; + private final SysUserMapper userMapper; + + @Override + public void validate(LoginBody loginBody) { + ValidatorUtils.validate(loginBody, PasswordGroup.class); + } + + @Override + public LoginVo login(String clientId, LoginBody loginBody) { + String tenantId = loginBody.getTenantId(); + String username = loginBody.getUsername(); + String password = loginBody.getPassword(); + String code = loginBody.getCode(); + String uuid = loginBody.getUuid(); + + boolean captchaEnabled = captchaProperties.getEnable(); + // 验证码开关 + if (captchaEnabled) { + validateCaptcha(tenantId, username, code, uuid); + } + + SysUserVo user = loadUserByUsername(tenantId, username); + loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword())); + // 此处可根据登录用户的数据不同 自行创建 loginUser + LoginUser loginUser = loginService.buildLoginUser(user); + // 生成token + LoginHelper.loginByDevice(loginUser, DeviceType.PC); + + loginService.recordLogininfor(loginUser.getTenantId(), username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); + loginService.recordLoginInfo(user.getUserId()); + + LoginVo loginVo = new LoginVo(); + loginVo.setAccessToken(StpUtil.getTokenValue()); + loginVo.setExpireIn(StpUtil.getTokenTimeout()); + return loginVo; + } + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + */ + private void validateCaptcha(String tenantId, String username, String code, String uuid) { + String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, ""); + String captcha = RedisUtils.getCacheObject(verifyKey); + RedisUtils.deleteObject(verifyKey); + if (captcha == null) { + loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) { + loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")); + throw new CaptchaException(); + } + } + + private SysUserVo loadUserByUsername(String tenantId, String username) { + SysUser user = userMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getUserName, SysUser::getStatus) + .eq(TenantHelper.isEnable(), SysUser::getTenantId, tenantId) + .eq(SysUser::getUserName, username)); + if (ObjectUtil.isNull(user)) { + log.info("登录用户:{} 不存在.", username); + throw new UserException("user.not.exists", username); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", username); + throw new UserException("user.blocked", username); + } + if (TenantHelper.isEnable()) { + return userMapper.selectTenantUserByUserName(username, tenantId); + } + return userMapper.selectUserByUserName(username); + } + +} diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java new file mode 100644 index 000000000..3ac1bb183 --- /dev/null +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java @@ -0,0 +1,104 @@ +package org.dromara.web.service.impl; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.constant.Constants; +import org.dromara.common.core.constant.GlobalConstants; +import org.dromara.common.core.domain.model.LoginBody; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.enums.DeviceType; +import org.dromara.common.core.enums.LoginType; +import org.dromara.common.core.enums.UserStatus; +import org.dromara.common.core.exception.user.CaptchaExpireException; +import org.dromara.common.core.exception.user.UserException; +import org.dromara.common.core.utils.MessageUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.ValidatorUtils; +import org.dromara.common.core.validate.auth.SmsGroup; +import org.dromara.common.redis.utils.RedisUtils; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.tenant.helper.TenantHelper; +import org.dromara.system.domain.SysUser; +import org.dromara.system.domain.vo.SysUserVo; +import org.dromara.system.mapper.SysUserMapper; +import org.dromara.web.domain.vo.LoginVo; +import org.dromara.web.service.IAuthStrategy; +import org.dromara.web.service.SysLoginService; +import org.springframework.stereotype.Service; + +/** + * 短信认证策略 + * + * @author Michelle.Chung + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class SmsAuthStrategy implements IAuthStrategy { + + private final SysLoginService loginService; + private final SysUserMapper userMapper; + + @Override + public void validate(LoginBody loginBody) { + ValidatorUtils.validate(loginBody, SmsGroup.class); + } + + @Override + public LoginVo login(String clientId, LoginBody loginBody) { + String tenantId = loginBody.getTenantId(); + String phonenumber = loginBody.getPhonenumber(); + String smsCode = loginBody.getSmsCode(); + + // 校验租户 + loginService.checkTenant(tenantId); + // 通过手机号查找用户 + SysUserVo user = loadUserByPhonenumber(tenantId, phonenumber); + + loginService.checkLogin(LoginType.SMS, tenantId, user.getUserName(), () -> !validateSmsCode(tenantId, phonenumber, smsCode)); + // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 + LoginUser loginUser = loginService.buildLoginUser(user); + // 生成token + LoginHelper.loginByDevice(loginUser, DeviceType.APP); + + loginService.recordLogininfor(loginUser.getTenantId(), user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); + loginService.recordLoginInfo(user.getUserId()); + LoginVo loginVo = new LoginVo(); + loginVo.setAccessToken(StpUtil.getTokenValue()); + return loginVo; + } + + /** + * 校验短信验证码 + */ + private boolean validateSmsCode(String tenantId, String phonenumber, String smsCode) { + String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + phonenumber); + if (StringUtils.isBlank(code)) { + loginService.recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); + throw new CaptchaExpireException(); + } + return code.equals(smsCode); + } + + private SysUserVo loadUserByPhonenumber(String tenantId, String phonenumber) { + SysUser user = userMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getPhonenumber, SysUser::getStatus) + .eq(TenantHelper.isEnable(), SysUser::getTenantId, tenantId) + .eq(SysUser::getPhonenumber, phonenumber)); + if (ObjectUtil.isNull(user)) { + log.info("登录用户:{} 不存在.", phonenumber); + throw new UserException("user.not.exists", phonenumber); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", phonenumber); + throw new UserException("user.blocked", phonenumber); + } + if (TenantHelper.isEnable()) { + return userMapper.selectTenantUserByPhonenumber(phonenumber, tenantId); + } + return userMapper.selectUserByPhonenumber(phonenumber); + } + +} diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java new file mode 100644 index 000000000..79af4652d --- /dev/null +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java @@ -0,0 +1,82 @@ +package org.dromara.web.service.impl; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.util.ObjectUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.constant.Constants; +import org.dromara.common.core.domain.model.LoginBody; +import org.dromara.common.core.domain.model.XcxLoginUser; +import org.dromara.common.core.enums.DeviceType; +import org.dromara.common.core.enums.UserStatus; +import org.dromara.common.core.utils.MessageUtils; +import org.dromara.common.core.utils.ValidatorUtils; +import org.dromara.common.core.validate.auth.WechatGroup; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.system.domain.vo.SysUserVo; +import org.dromara.web.domain.vo.LoginVo; +import org.dromara.web.service.IAuthStrategy; +import org.dromara.web.service.SysLoginService; +import org.springframework.stereotype.Service; + +/** + * 邮件认证策略 + * + * @author Michelle.Chung + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class XcxAuthStrategy implements IAuthStrategy { + + private final SysLoginService loginService; + + @Override + public void validate(LoginBody loginBody) { + ValidatorUtils.validate(loginBody, WechatGroup.class); + } + + @Override + public LoginVo login(String clientId, LoginBody loginBody) { + // xcxCode 为 小程序调用 wx.login 授权后获取 + String xcxCode = loginBody.getXcxCode(); + // todo 以下自行实现 + // 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid + String openid = ""; + // 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可 + SysUserVo user = loadUserByOpenid(openid); + // 校验租户 + loginService.checkTenant(user.getTenantId()); + + // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 + XcxLoginUser loginUser = new XcxLoginUser(); + loginUser.setTenantId(user.getTenantId()); + loginUser.setUserId(user.getUserId()); + loginUser.setUsername(user.getUserName()); + loginUser.setUserType(user.getUserType()); + loginUser.setOpenid(openid); + // 生成token + LoginHelper.loginByDevice(loginUser, DeviceType.XCX); + + loginService.recordLogininfor(loginUser.getTenantId(), user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); + loginService.recordLoginInfo(user.getUserId()); + LoginVo loginVo = new LoginVo(); + loginVo.setAccessToken(StpUtil.getTokenValue()); + return loginVo; + } + + private SysUserVo loadUserByOpenid(String openid) { + // 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户 + // todo 自行实现 userService.selectUserByOpenid(openid); + SysUserVo user = new SysUserVo(); + if (ObjectUtil.isNull(user)) { + log.info("登录用户:{} 不存在.", openid); + // todo 用户不存在 业务逻辑自行实现 + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", openid); + // todo 用户已被停用 业务逻辑自行实现 + } + return user; + } + +} diff --git a/ruoyi-admin/src/main/resources/i18n/messages.properties b/ruoyi-admin/src/main/resources/i18n/messages.properties index 4f2da28b4..5c9f42271 100644 --- a/ruoyi-admin/src/main/resources/i18n/messages.properties +++ b/ruoyi-admin/src/main/resources/i18n/messages.properties @@ -28,6 +28,9 @@ user.register.error=注册失败,请联系系统管理人员 user.notfound=请重新登录 user.forcelogout=管理员强制退出,请重新登录 user.unknown.error=未知错误,请重新登录 +auth.grant.type.error=认证权限类型错误 +auth.grant.type.not.blank=认证权限类型不能为空 +auth.clientid.not.blank=认证客户端id不能为空 ##文件上传消息 upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{0}MB! upload.filename.exceed.length=上传的文件名最长{0}个字符 diff --git a/ruoyi-admin/src/main/resources/i18n/messages_en_US.properties b/ruoyi-admin/src/main/resources/i18n/messages_en_US.properties index 0c738a37e..2ab82a667 100644 --- a/ruoyi-admin/src/main/resources/i18n/messages_en_US.properties +++ b/ruoyi-admin/src/main/resources/i18n/messages_en_US.properties @@ -28,6 +28,9 @@ user.register.error=Register failed, please contact system administrator user.notfound=Please login again user.forcelogout=The administrator is forced to exit,please login again user.unknown.error=Unknown error, please login again +auth.grant.type.error=Auth grant type error +auth.grant.type.not.blank=Auth grant type cannot be blank +auth.clientid.not.blank=Auth clientid cannot be blank ##文件上传消息 upload.exceed.maxSize=The uploaded file size exceeds the limit file size!
the maximum allowed file size is:{0}MB! upload.filename.exceed.length=The maximum length of uploaded file name is {0} characters diff --git a/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties b/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties index 4f2da28b4..5c9f42271 100644 --- a/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties +++ b/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties @@ -28,6 +28,9 @@ user.register.error=注册失败,请联系系统管理人员 user.notfound=请重新登录 user.forcelogout=管理员强制退出,请重新登录 user.unknown.error=未知错误,请重新登录 +auth.grant.type.error=认证权限类型错误 +auth.grant.type.not.blank=认证权限类型不能为空 +auth.clientid.not.blank=认证客户端id不能为空 ##文件上传消息 upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{0}MB! upload.filename.exceed.length=上传的文件名最长{0}个字符 diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/LoginBody.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/LoginBody.java index 73f678cc9..09842a93e 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/LoginBody.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/LoginBody.java @@ -1,7 +1,12 @@ package org.dromara.common.core.domain.model; +import jakarta.validation.constraints.Email; import org.dromara.common.core.constant.UserConstants; import lombok.Data; +import org.dromara.common.core.validate.auth.EmailGroup; +import org.dromara.common.core.validate.auth.PasswordGroup; +import org.dromara.common.core.validate.auth.SmsGroup; +import org.dromara.common.core.validate.auth.WechatGroup; import org.hibernate.validator.constraints.Length; import jakarta.validation.constraints.NotBlank; @@ -15,6 +20,28 @@ import jakarta.validation.constraints.NotBlank; @Data public class LoginBody { + /** + * 客户端id + */ + @NotBlank(message = "{auth.clientid.not.blank}") + private String clientId; + + /** + * 客户端key + */ + private String clientKey; + + /** + * 客户端秘钥 + */ + private String clientSecret; + + /** + * 授权类型 + */ + @NotBlank(message = "{auth.grant.type.not.blank}") + private String grantType; + /** * 租户ID */ @@ -24,15 +51,15 @@ public class LoginBody { /** * 用户名 */ - @NotBlank(message = "{user.username.not.blank}") - @Length(min = UserConstants.USERNAME_MIN_LENGTH, max = UserConstants.USERNAME_MAX_LENGTH, message = "{user.username.length.valid}") + @NotBlank(message = "{user.username.not.blank}", groups = {PasswordGroup.class}) + @Length(min = UserConstants.USERNAME_MIN_LENGTH, max = UserConstants.USERNAME_MAX_LENGTH, message = "{user.username.length.valid}", groups = {PasswordGroup.class}) private String username; /** * 用户密码 */ - @NotBlank(message = "{user.password.not.blank}") - @Length(min = UserConstants.PASSWORD_MIN_LENGTH, max = UserConstants.PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}") + @NotBlank(message = "{user.password.not.blank}", groups = {PasswordGroup.class}) + @Length(min = UserConstants.PASSWORD_MIN_LENGTH, max = UserConstants.PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}", groups = {PasswordGroup.class}) private String password; /** @@ -45,4 +72,35 @@ public class LoginBody { */ private String uuid; + /** + * 手机号 + */ + @NotBlank(message = "{user.phonenumber.not.blank}", groups = {SmsGroup.class}) + private String phonenumber; + + /** + * 短信code + */ + @NotBlank(message = "{sms.code.not.blank}", groups = {SmsGroup.class}) + private String smsCode; + + /** + * 邮箱 + */ + @NotBlank(message = "{user.email.not.blank}", groups = {EmailGroup.class}) + @Email(message = "{user.email.not.valid}") + private String email; + + /** + * 邮箱code + */ + @NotBlank(message = "{email.code.not.blank}", groups = {EmailGroup.class}) + private String emailCode; + + /** + * 小程序code + */ + @NotBlank(message = "{xcx.code.not.blank}", groups = {WechatGroup.class}) + private String xcxCode; + } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/user/AuthTypeErrorException.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/user/AuthTypeErrorException.java new file mode 100644 index 000000000..9fc110162 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/user/AuthTypeErrorException.java @@ -0,0 +1,19 @@ +package org.dromara.common.core.exception.user; + +import java.io.Serial; + +/** + * 认证权限类型异常类 + * + * @author Michelle.Chung + */ +public class AuthTypeErrorException extends UserException { + + @Serial + private static final long serialVersionUID = 1L; + + public AuthTypeErrorException() { + super("auth.grant.type.error"); + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/auth/EmailGroup.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/auth/EmailGroup.java new file mode 100644 index 000000000..130932f4a --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/auth/EmailGroup.java @@ -0,0 +1,7 @@ +package org.dromara.common.core.validate.auth; + +/** + * @Author Michelle.Chung + */ +public interface EmailGroup { +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/auth/PasswordGroup.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/auth/PasswordGroup.java new file mode 100644 index 000000000..1516172ad --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/auth/PasswordGroup.java @@ -0,0 +1,7 @@ +package org.dromara.common.core.validate.auth; + +/** + * @Author Michelle.Chung + */ +public interface PasswordGroup { +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/auth/SmsGroup.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/auth/SmsGroup.java new file mode 100644 index 000000000..d56286027 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/auth/SmsGroup.java @@ -0,0 +1,7 @@ +package org.dromara.common.core.validate.auth; + +/** + * @Author Michelle.Chung + */ +public interface SmsGroup { +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/auth/WechatGroup.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/auth/WechatGroup.java new file mode 100644 index 000000000..b397f1870 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/auth/WechatGroup.java @@ -0,0 +1,7 @@ +package org.dromara.common.core.validate.auth; + +/** + * @Author Michelle.Chung + */ +public interface WechatGroup { +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysClient.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysClient.java new file mode 100644 index 000000000..5ec3412fb --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysClient.java @@ -0,0 +1,62 @@ +package org.dromara.system.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 授权管理对象 sys_auth + * + * @author Michelle.Chung + * @date 2023-05-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_client") +public class SysClient extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(value = "id") + private Long id; + + /** + * 客户端id + */ + private String clientId; + + /** + * 客户端key + */ + private String clientKey; + + /** + * 客户端秘钥 + */ + private String clientSecret; + + /** + * 授权类型 + */ + private String grantType; + + /** + * 状态(0正常 1停用) + */ + private String status; + + /** + * 删除标志(0代表存在 2代表删除) + */ + @TableLogic + private String delFlag; + + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysClientBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysClientBo.java new file mode 100644 index 000000000..63da6684e --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysClientBo.java @@ -0,0 +1,58 @@ +package org.dromara.system.domain.bo; + +import org.dromara.system.domain.SysClient; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * 授权管理业务对象 sys_auth + * + * @author Michelle.Chung + * @date 2023-05-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysClient.class, reverseConvertGenerate = false) +public class SysClientBo extends BaseEntity { + + /** + * id + */ + @NotNull(message = "id不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 客户端id + */ + private String clientId; + + /** + * 客户端key + */ + @NotBlank(message = "客户端key不能为空", groups = { AddGroup.class, EditGroup.class }) + private String clientKey; + + /** + * 客户端秘钥 + */ + @NotBlank(message = "客户端秘钥不能为空", groups = { AddGroup.class, EditGroup.class }) + private String clientSecret; + + /** + * 授权类型 + */ + @NotBlank(message = "授权类型不能为空", groups = { AddGroup.class, EditGroup.class }) + private String grantType; + + /** + * 状态(0正常 1停用) + */ + private String status; + + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysClientVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysClientVo.java new file mode 100644 index 000000000..69a32919d --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysClientVo.java @@ -0,0 +1,67 @@ +package org.dromara.system.domain.vo; + +import org.dromara.system.domain.SysClient; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 授权管理视图对象 sys_auth + * + * @author Michelle.Chung + * @date 2023-05-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysClient.class) +public class SysClientVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @ExcelProperty(value = "id") + private Long id; + + /** + * 客户端id + */ + @ExcelProperty(value = "客户端id") + private String clientId; + + /** + * 客户端key + */ + @ExcelProperty(value = "客户端key") + private String clientKey; + + /** + * 客户端秘钥 + */ + @ExcelProperty(value = "客户端秘钥") + private String clientSecret; + + /** + * 授权类型 + */ + @ExcelProperty(value = "授权类型") + private String grantType; + + /** + * 状态(0正常 1停用) + */ + @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=正常,1=停用") + private String status; + + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysClientMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysClientMapper.java new file mode 100644 index 000000000..15bcfb4d3 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysClientMapper.java @@ -0,0 +1,15 @@ +package org.dromara.system.mapper; + +import org.dromara.system.domain.SysClient; +import org.dromara.system.domain.vo.SysClientVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 授权管理Mapper接口 + * + * @author Michelle.Chung + * @date 2023-05-15 + */ +public interface SysClientMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysClientMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysClientMapper.xml new file mode 100644 index 000000000..fd150ad12 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysClientMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/script/sql/oracle/oracle_ry_vue_5.X.sql b/script/sql/oracle/oracle_ry_vue_5.X.sql index 68edb59c6..4a3c091fc 100644 --- a/script/sql/oracle/oracle_ry_vue_5.X.sql +++ b/script/sql/oracle/oracle_ry_vue_5.X.sql @@ -1108,6 +1108,43 @@ insert into sys_oss_config values (3, '000000', 'aliyun', 'XXXXXXXXXXXXXXX', 'X insert into sys_oss_config values (4, '000000', 'qcloud', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi-1250000000', '', 'cos.ap-beijing.myqcloud.com', '','N', 'ap-beijing', '1', '1', '', NULL, 103, 1, sysdate, 1, sysdate); insert into sys_oss_config values (5, '000000', 'image', 'ruoyi', 'ruoyi123', 'ruoyi', 'image', '127.0.0.1:9000', '','N', '', '1', '1', '', NULL, 103, 1, sysdate, 1, sysdate); +-- ---------------------------- +-- 系统授权表 +-- ---------------------------- +create table sys_client ( + id number(20) not null, + client_id varchar(64) default null, + client_key varchar(32) default null, + client_secret varchar(255) default null, + grant_type varchar(255) default null, + status char(1) default '0', + del_flag char(1) default '0', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date +) + +alter table sys_client add constraint pk_sys_client primary key (id); + +comment on table sys_client is '系统授权表'; +comment on column sys_client.id is '主建'; +comment on column sys_client.client_id is '客户端id'; +comment on column sys_client.client_key is '客户端key'; +comment on column sys_client.client_secret is '客户端秘钥'; +comment on column sys_client.grant_type is '授权类型'; +comment on column sys_client.status is '状态(0正常 1停用)'; +comment on column sys_client.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_client.create_dept is '创建部门'; +comment on column sys_client.create_by is '创建者'; +comment on column sys_client.create_time is '创建时间'; +comment on column sys_client.update_by is '更新者'; +comment on column sys_client.update_time is '更新时间'; + +insert into sys_client values (1, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password', 0, 0, 103, 1, sysdate, 1, sysdate); +insert into sys_client values (2, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms', 0, 0, 103, 1, sysdate, 1, sysdate); + -- ---------------------------- -- 钩子 ,用于session连接之后 自动设置默认的date类型格式化 简化时间查询 diff --git a/script/sql/postgres/postgres_ry_vue_5.X.sql b/script/sql/postgres/postgres_ry_vue_5.X.sql index 546deee41..45c4efc88 100644 --- a/script/sql/postgres/postgres_ry_vue_5.X.sql +++ b/script/sql/postgres/postgres_ry_vue_5.X.sql @@ -1132,6 +1132,43 @@ insert into sys_oss_config values (3, '000000', 'aliyun', 'XXXXXXXXXXXXXXX', 'X insert into sys_oss_config values (4, '000000', 'qcloud', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi-1250000000', '', 'cos.ap-beijing.myqcloud.com', '','N', 'ap-beijing', '1', '1', '', 103, 1, now(), 1, now(), null); insert into sys_oss_config values (5, '000000', 'image', 'ruoyi', 'ruoyi123', 'ruoyi', 'image', '127.0.0.1:9000', '','N', '', '1', '1', '', 103, 1, now(), 1, now(), NULL); +-- ---------------------------- +-- 系统授权表 +-- ---------------------------- +drop table if exists sys_client; +create table sys_client ( + id int8, + client_id varchar(64) ''::varchar, + client_key varchar(32) ''::varchar, + client_secret varchar(255) ''::varchar, + grant_type varchar(255) ''::varchar, + status char(1) default '0'::bpchar, + del_flag char(1) default '0'::bpchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + constraint sys_client_pk primary key (id) +) + +comment on table sys_client is '系统授权表'; +comment on column sys_client.id is '主建'; +comment on column sys_client.client_id is '客户端id'; +comment on column sys_client.client_key is '客户端key'; +comment on column sys_client.client_secret is '客户端秘钥'; +comment on column sys_client.grant_type is '授权类型'; +comment on column sys_client.status is '状态(0正常 1停用)'; +comment on column sys_client.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_client.create_dept is '创建部门'; +comment on column sys_client.create_by is '创建者'; +comment on column sys_client.create_time is '创建时间'; +comment on column sys_client.update_by is '更新者'; +comment on column sys_client.update_time is '更新时间'; + +insert into sys_client values (1, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password', 0, 0, 103, 1, now(), 1, now()); +insert into sys_client values (2, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms', 0, 0, 103, 1, now(), 1, now()); + -- 字符串自动转时间 避免框架时间查询报错问题 create or replace function cast_varchar_to_timestamp(varchar) returns timestamptz as $$ select to_timestamp($1, 'yyyy-mm-dd hh24:mi:ss'); diff --git a/script/sql/ry_vue_5.X.sql b/script/sql/ry_vue_5.X.sql index 258542b44..1628c83fb 100644 --- a/script/sql/ry_vue_5.X.sql +++ b/script/sql/ry_vue_5.X.sql @@ -795,3 +795,26 @@ insert into sys_oss_config values (2, '000000', 'qiniu', 'XXXXXXXXXXXXXXX', 'X insert into sys_oss_config values (3, '000000', 'aliyun', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', '', 'oss-cn-beijing.aliyuncs.com', '','N', '', '1' ,'1', '', 103, 1, sysdate(), 1, sysdate(), NULL); insert into sys_oss_config values (4, '000000', 'qcloud', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi-1250000000', '', 'cos.ap-beijing.myqcloud.com', '','N', 'ap-beijing', '1' ,'1', '', 103, 1, sysdate(), 1, sysdate(), NULL); insert into sys_oss_config values (5, '000000', 'image', 'ruoyi', 'ruoyi123', 'ruoyi', 'image', '127.0.0.1:9000', '','N', '', '1' ,'1', '', 103, 1, sysdate(), 1, sysdate(), NULL); + +-- ---------------------------- +-- 系统授权表 +-- ---------------------------- +drop table if exists sys_client; +create table sys_client ( + id bigint(20) not null comment 'id', + client_id varchar(64) default null comment '客户端id', + client_key varchar(32) default null comment '客户端key', + client_secret varchar(255) default null comment '客户端秘钥', + grant_type varchar(255) default null comment '授权类型', + status char(1) default '0' comment '状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + create_dept bigint(20) default null comment '创建部门', + create_by bigint(20) default null comment '创建者', + create_time datetime default null comment '创建时间', + update_by bigint(20) default null comment '更新者', + update_time datetime default null comment '更新时间', + primary key (id) +) engine=innodb comment='系统授权表'; + +insert into sys_client values (1, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password', 0, 0, 103, 1, sysdate(), 1, sysdate()); +insert into sys_client values (2, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms', 0, 0, 103, 1, sysdate(), 1, sysdate()); diff --git a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql index 50f6afa92..8b8b553fb 100644 --- a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql +++ b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql @@ -2819,3 +2819,108 @@ INSERT INTO sys_oss_config VALUES (N'4', N'000000', N'qcloud', N'XXXXXXXXXXXXXXX GO INSERT INTO sys_oss_config VALUES (N'5', N'000000', N'image', N'ruoyi', N'ruoyi123', N'ruoyi', N'image', N'127.0.0.1:9000', N'',N'N', N'', N'1', N'1', N'', 103, 1, getdate(), 1, getdate(), NULL) GO + + +CREATE TABLE sys_client +( + id bigint NOT NULL, + client_id nvarchar(20) DEFAULT '' NULL, + client_key nvarchar(255) DEFAULT '' NULL, + client_secret nvarchar(255) DEFAULT '' NULL, + grant_type nvarchar(255) DEFAULT '' NULL, + status nchar(1) DEFAULT ('0') NULL, + del_flag nchar(1) DEFAULT ('0') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL + CONSTRAINT PK__sys_client___BFBDE87009ED2882 PRIMARY KEY CLUSTERED (id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty +'MS_Description', N'主建', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'id' +GO +EXEC sys.sp_addextendedproperty +'MS_Description', N'客户端id' , +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'tenant_id' +GO +EXEC sp_addextendedproperty +'MS_Description', N'客户端key', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'client_key' +GO +EXEC sp_addextendedproperty +'MS_Description', N'客户端秘钥', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'client_secret' +GO +EXEC sp_addextendedproperty +'MS_Description', N'授权类型', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'grant_type' +GO +EXEC sp_addextendedproperty +'MS_Description', N'状态(0正常 1停用)', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'status' +GO +EXEC sp_addextendedproperty +'MS_Description', N'删除标志(0代表存在 2代表删除)', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'del_flag' +GO +EXEC sys.sp_addextendedproperty +'MS_Description', N'创建部门' , +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'create_dept' +GO +EXEC sp_addextendedproperty +'MS_Description', N'创建者', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'create_by' +GO +EXEC sp_addextendedproperty +'MS_Description', N'创建时间', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'create_time' +GO +EXEC sp_addextendedproperty +'MS_Description', N'更新者', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'update_by' +GO +EXEC sp_addextendedproperty +'MS_Description', N'更新时间', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'update_time' +GO +EXEC sp_addextendedproperty +'MS_Description', N'系统授权表', +'SCHEMA', N'dbo', +'TABLE', N'sys_client' +GO + +INSERT INTO sys_client VALUES (N'1', N'e5cd7e4891bf95d1d19206ce24a7b32e', N'pc', N'pc123', N'password', N'0', N'0', 103, 1, getdate(), 1, getdate()) +GO +INSERT INTO sys_client VALUES (N'2', N'428a8310cd442757ae699df5d894f051', N'app', N'app123', N'password,sms', N'0', N'0', 103, 1, getdate(), 1, getdate()) +GO