diff --git a/pom.xml b/pom.xml index b20e7f059..b5ea53896 100644 --- a/pom.xml +++ b/pom.xml @@ -191,6 +191,13 @@ ${satoken.version} + + + cn.dev33 + sa-token-oauth2 + ${satoken.version} + + com.baomidou 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 f39fd3068..fa01e1430 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 @@ -3,10 +3,7 @@ package org.dromara.web.controller; import cn.dev33.satoken.annotation.SaIgnore; import cn.hutool.core.collection.CollUtil; 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; @@ -18,10 +15,11 @@ 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 jakarta.validation.constraints.NotBlank; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -49,62 +47,23 @@ public class AuthController { /** * 登录方法 * - * @param body 登录信息 + * @param loginBody 登录信息 * @return 结果 */ - @PostMapping("/login") - public R login(@Validated @RequestBody LoginBody body) { + @PostMapping("/login/{clientId}/{authType}") + public R login(@PathVariable String clientId, + @PathVariable String authType, + @RequestParam(required = false) String tenantId, + @Validated @RequestBody T loginBody) { + // FIXME 后续不用泛型传参,考虑改为 req 获取 + // 校验类型和id + loginService.checkAuthType(clientId, authType); + // FIXME 抽取出来,校验租户,前端没改暂时注释 +// loginService.checkTenant(tenantId); + // 登录 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); + IAuthStrategy authStrategy = AuthFactory.instance(authType); + String token = authStrategy.login(clientId, authStrategy.getLoginBody(loginBody)); loginVo.setToken(token); return R.ok(loginVo); } 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..b524d973e --- /dev/null +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/IAuthStrategy.java @@ -0,0 +1,21 @@ +package org.dromara.web.service; + + +/** + * 授权策略 + * + * @author Michelle.Chung + */ +public interface IAuthStrategy { + + /** + * 获取登录实体 + */ + Object getLoginBody(Object loginBody); + + /** + * 登录 + */ + String login(String clientId, T 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 689a55de1..dae4ea662 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.SysAuth; 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.SysAuthMapper; 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 SysAuthMapper authMapper; /** * 退出登录 @@ -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::getPhonenumber, 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,7 +128,7 @@ 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; @@ -368,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; } @@ -383,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 checkAuthType(String clientId, String authType) { + Long count = authMapper.selectCount( + new LambdaQueryWrapper() + .eq(SysAuth::getClientId, clientId) + .like(SysAuth::getAuthType, authType)); + if (count == 0) { + log.info("客户端id:{} 认证类型:{} 不存在.", clientId, authType); + 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..763661566 --- /dev/null +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java @@ -0,0 +1,100 @@ +package org.dromara.web.service.impl; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +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.EmailLoginBody; +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.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.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 Object getLoginBody(Object loginBody) { + return BeanUtil.toBean(loginBody, EmailLoginBody.class); + } + + @Override + public String login(String clientId, EmailLoginBody 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()); + return StpUtil.getTokenValue(); + } + + /** + * 校验邮箱验证码 + */ + 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::getPhonenumber, 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..fc065b01e --- /dev/null +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java @@ -0,0 +1,129 @@ +package org.dromara.web.service.impl; + +import cn.dev33.satoken.oauth2.logic.SaOAuth2Util; +import cn.dev33.satoken.oauth2.model.AccessTokenModel; +import cn.dev33.satoken.oauth2.model.RequestAuthModel; +import cn.dev33.satoken.secure.BCrypt; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +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.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.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 Object getLoginBody(Object loginBody) { + return BeanUtil.toBean(loginBody, LoginBody.class); + } + + @Override + public String 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); + } + // 校验租户 + loginService.checkTenant(tenantId); + + 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()); + + // FIXME 参考 demo 生成 AccessToken,但是 loginUser 获取不到值,未解决 + RequestAuthModel ra = new RequestAuthModel(); + ra.clientId = clientId; + ra.loginId = StpUtil.getLoginId(); + AccessTokenModel at = SaOAuth2Util.generateAccessToken(ra, true); + return at.accessToken; + } + + /** + * 校验验证码 + * + * @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..26b49db18 --- /dev/null +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java @@ -0,0 +1,100 @@ +package org.dromara.web.service.impl; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +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.LoginUser; +import org.dromara.common.core.domain.model.SmsLoginBody; +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.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.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 Object getLoginBody(Object loginBody) { + return BeanUtil.toBean(loginBody, SmsLoginBody.class); + } + + @Override + public String login(String clientId, SmsLoginBody 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()); + return StpUtil.getTokenValue(); + } + + /** + * 校验短信验证码 + */ + 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..7186a293f --- /dev/null +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java @@ -0,0 +1,78 @@ +package org.dromara.web.service.impl; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +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.XcxLoginBody; +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.satoken.utils.LoginHelper; +import org.dromara.system.domain.vo.SysUserVo; +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 Object getLoginBody(Object loginBody) { + return BeanUtil.toBean(loginBody, XcxLoginBody.class); + } + + @Override + public String login(String clientId, XcxLoginBody 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()); + return StpUtil.getTokenValue(); + } + + 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/application.yml b/ruoyi-admin/src/main/resources/application.yml index 31d0de5ff..51776b63a 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -145,6 +145,7 @@ tenant: - sys_role_menu - sys_user_post - sys_user_role + - sys_auth # MyBatisPlus配置 # https://baomidou.com/config/ diff --git a/ruoyi-admin/src/main/resources/i18n/messages.properties b/ruoyi-admin/src/main/resources/i18n/messages.properties index 4f2da28b4..3ff179e4e 100644 --- a/ruoyi-admin/src/main/resources/i18n/messages.properties +++ b/ruoyi-admin/src/main/resources/i18n/messages.properties @@ -28,6 +28,7 @@ user.register.error=注册失败,请联系系统管理人员 user.notfound=请重新登录 user.forcelogout=管理员强制退出,请重新登录 user.unknown.error=未知错误,请重新登录 +auth.type.error=认证权限类型错误 ##文件上传消息 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..b6c85c83f 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,7 @@ 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.type.error=Auth type error ##文件上传消息 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..3ff179e4e 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,7 @@ user.register.error=注册失败,请联系系统管理人员 user.notfound=请重新登录 user.forcelogout=管理员强制退出,请重新登录 user.unknown.error=未知错误,请重新登录 +auth.type.error=认证权限类型错误 ##文件上传消息 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/AuthBody.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/AuthBody.java new file mode 100644 index 000000000..99af0556b --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/AuthBody.java @@ -0,0 +1,40 @@ +package org.dromara.common.core.domain.model; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 授权管理对象 + * + * @author Michelle.Chung + */ +@Data +public class AuthBody implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 客户端id + */ + private String clientId; + + /** + * 客户端key + */ + private String clientKey; + + /** + * 客户端秘钥 + */ + private String clientSecret; + + /** + * 授权类型 + */ + private String authType; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/XcxLoginBody.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/XcxLoginBody.java new file mode 100644 index 000000000..101bf4414 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/XcxLoginBody.java @@ -0,0 +1,17 @@ +package org.dromara.common.core.domain.model; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +/** + * 小程序登录对象 + * + * @author Michelle.Chung + */ +@Data +public class XcxLoginBody { + + @NotBlank(message = "{xcx.code.not.blank}") + 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..52850293a --- /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.type.error"); + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/AuthService.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/AuthService.java new file mode 100644 index 000000000..0526fd85f --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/AuthService.java @@ -0,0 +1,19 @@ +package org.dromara.common.core.service; + +import org.dromara.common.core.domain.model.AuthBody; + +/** + * 通用 认证服务 + * + * @author Michelle.Chung + */ +public interface AuthService { + + /** + * 获取认证授权对象 + * + * @param clientId 客户端id + */ + AuthBody getAuthBody(String clientId); + +} diff --git a/ruoyi-common/ruoyi-common-satoken/pom.xml b/ruoyi-common/ruoyi-common-satoken/pom.xml index 01c282b63..ac2a543a1 100644 --- a/ruoyi-common/ruoyi-common-satoken/pom.xml +++ b/ruoyi-common/ruoyi-common-satoken/pom.xml @@ -36,6 +36,12 @@ sa-token-jwt
+ + + cn.dev33 + sa-token-oauth2 + + diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaOAuth2TemplateImpl.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaOAuth2TemplateImpl.java new file mode 100644 index 000000000..07062f24f --- /dev/null +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaOAuth2TemplateImpl.java @@ -0,0 +1,60 @@ +package org.dromara.common.satoken.core.service; + +import cn.dev33.satoken.oauth2.logic.SaOAuth2Template; +import cn.dev33.satoken.oauth2.model.SaClientModel; +import cn.dev33.satoken.stp.StpUtil; +import org.dromara.common.core.domain.model.AuthBody; +import org.dromara.common.core.service.AuthService; +import org.dromara.common.core.utils.SpringUtils; +import org.springframework.stereotype.Component; + +/** + * Sa-Token-OAuth2 模块实现类 + * + * @author Michelle.Chung + */ +@Component +public class SaOAuth2TemplateImpl extends SaOAuth2Template { + + /** + * 重写 Access-Token 生成策略:复用登录会话的Token + * + * @param clientId 应用id + * @param loginId 账号id + * @param scope 权限 + */ + @Override + public String randomAccessToken(String clientId, Object loginId, String scope) { + String tokenValue = StpUtil.createLoginSession(loginId); + return tokenValue; + } + + /** + * 根据 id 获取 Client 信息 + * + * @param clientId 应用id + */ + @Override + public SaClientModel getClientModel(String clientId) { + AuthService authService = SpringUtils.getBean(AuthService.class); + AuthBody authBody = authService.getAuthBody(clientId); + return new SaClientModel() + .setClientId(clientId) + .setClientSecret(authBody.getClientSecret()) + .setAllowUrl("*") + .setIsPassword(true); + } + + /** + * 根据ClientId 和 LoginId 获取openid + * + * @param clientId 应用id + * @param loginId 账号id + */ + @Override + public String getOpenid(String clientId, Object loginId) { + // 此为模拟数据,真实环境需要从数据库查询 + return "gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__"; + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysAuthController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysAuthController.java new file mode 100644 index 000000000..c03b5a73d --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysAuthController.java @@ -0,0 +1,105 @@ +package org.dromara.system.controller.system; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.system.domain.vo.SysAuthVo; +import org.dromara.system.domain.bo.SysAuthBo; +import org.dromara.system.service.ISysAuthService; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +/** + * 授权管理 + * + * @author Michelle.Chung + * @date 2023-05-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/auth") +public class SysAuthController extends BaseController { + + private final ISysAuthService sysAuthService; + + /** + * 查询授权管理列表 + */ + @SaCheckPermission("system:auth:list") + @GetMapping("/list") + public TableDataInfo list(SysAuthBo bo, PageQuery pageQuery) { + return sysAuthService.queryPageList(bo, pageQuery); + } + + /** + * 导出授权管理列表 + */ + @SaCheckPermission("system:auth:export") + @Log(title = "授权管理", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(SysAuthBo bo, HttpServletResponse response) { + List list = sysAuthService.queryList(bo); + ExcelUtil.exportExcel(list, "授权管理", SysAuthVo.class, response); + } + + /** + * 获取授权管理详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("system:auth:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(sysAuthService.queryById(id)); + } + + /** + * 新增授权管理 + */ + @SaCheckPermission("system:auth:add") + @Log(title = "授权管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody SysAuthBo bo) { + return toAjax(sysAuthService.insertByBo(bo)); + } + + /** + * 修改授权管理 + */ + @SaCheckPermission("system:auth:edit") + @Log(title = "授权管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody SysAuthBo bo) { + return toAjax(sysAuthService.updateByBo(bo)); + } + + /** + * 删除授权管理 + * + * @param ids 主键串 + */ + @SaCheckPermission("system:auth:remove") + @Log(title = "授权管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(sysAuthService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysAuth.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysAuth.java new file mode 100644 index 000000000..6f95eceab --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysAuth.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_auth") +public class SysAuth 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 authType; + + /** + * 状态(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/SysAuthBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysAuthBo.java new file mode 100644 index 000000000..52b6a9ec8 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysAuthBo.java @@ -0,0 +1,58 @@ +package org.dromara.system.domain.bo; + +import org.dromara.system.domain.SysAuth; +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 = SysAuth.class, reverseConvertGenerate = false) +public class SysAuthBo 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 authType; + + /** + * 状态(0正常 1停用) + */ + private String status; + + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysAuthVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysAuthVo.java new file mode 100644 index 000000000..4f5eb7d8c --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysAuthVo.java @@ -0,0 +1,69 @@ +package org.dromara.system.domain.vo; + +import org.dromara.system.domain.SysAuth; +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; +import java.util.Date; + + + +/** + * 授权管理视图对象 sys_auth + * + * @author Michelle.Chung + * @date 2023-05-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysAuth.class) +public class SysAuthVo 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 authType; + + /** + * 状态(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/SysAuthMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysAuthMapper.java new file mode 100644 index 000000000..dfd37dafa --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysAuthMapper.java @@ -0,0 +1,15 @@ +package org.dromara.system.mapper; + +import org.dromara.system.domain.SysAuth; +import org.dromara.system.domain.vo.SysAuthVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 授权管理Mapper接口 + * + * @author Michelle.Chung + * @date 2023-05-15 + */ +public interface SysAuthMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysAuthService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysAuthService.java new file mode 100644 index 000000000..371b6ac3c --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysAuthService.java @@ -0,0 +1,48 @@ +package org.dromara.system.service; + +import org.dromara.system.domain.vo.SysAuthVo; +import org.dromara.system.domain.bo.SysAuthBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 授权管理Service接口 + * + * @author Michelle.Chung + * @date 2023-05-15 + */ +public interface ISysAuthService { + + /** + * 查询授权管理 + */ + SysAuthVo queryById(Long id); + + /** + * 查询授权管理列表 + */ + TableDataInfo queryPageList(SysAuthBo bo, PageQuery pageQuery); + + /** + * 查询授权管理列表 + */ + List queryList(SysAuthBo bo); + + /** + * 新增授权管理 + */ + Boolean insertByBo(SysAuthBo bo); + + /** + * 修改授权管理 + */ + Boolean updateByBo(SysAuthBo bo); + + /** + * 校验并批量删除授权管理信息 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysAuthServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysAuthServiceImpl.java new file mode 100644 index 000000000..f63fffc31 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysAuthServiceImpl.java @@ -0,0 +1,129 @@ +package org.dromara.system.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import org.dromara.common.core.domain.model.AuthBody; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.exception.user.AuthTypeErrorException; +import org.dromara.common.core.service.AuthService; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.dromara.system.domain.bo.SysAuthBo; +import org.dromara.system.domain.vo.SysAuthVo; +import org.dromara.system.domain.SysAuth; +import org.dromara.system.mapper.SysAuthMapper; +import org.dromara.system.service.ISysAuthService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 授权管理Service业务层处理 + * + * @author Michelle.Chung + * @date 2023-05-15 + */ +@RequiredArgsConstructor +@Service +public class SysAuthServiceImpl implements ISysAuthService, AuthService { + + private final SysAuthMapper baseMapper; + + /** + * 查询授权管理 + */ + @Override + public SysAuthVo queryById(Long id){ + return baseMapper.selectVoById(id); + } + + /** + * 查询授权管理列表 + */ + @Override + public TableDataInfo queryPageList(SysAuthBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询授权管理列表 + */ + @Override + public List queryList(SysAuthBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(SysAuthBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(StringUtils.isNotBlank(bo.getClientId()), SysAuth::getClientId, bo.getClientId()); + lqw.eq(StringUtils.isNotBlank(bo.getClientKey()), SysAuth::getClientKey, bo.getClientKey()); + lqw.eq(StringUtils.isNotBlank(bo.getClientSecret()), SysAuth::getClientSecret, bo.getClientSecret()); + lqw.eq(StringUtils.isNotBlank(bo.getAuthType()), SysAuth::getAuthType, bo.getAuthType()); + lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysAuth::getStatus, bo.getStatus()); + return lqw; + } + + /** + * 新增授权管理 + */ + @Override + public Boolean insertByBo(SysAuthBo bo) { + SysAuth add = MapstructUtils.convert(bo, SysAuth.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改授权管理 + */ + @Override + public Boolean updateByBo(SysAuthBo bo) { + SysAuth update = MapstructUtils.convert(bo, SysAuth.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(SysAuth entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 批量删除授权管理 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteBatchIds(ids) > 0; + } + + @Override + public AuthBody getAuthBody(String clientId) { + SysAuth sysAuth = baseMapper.selectOne( + new LambdaQueryWrapper().eq(SysAuth::getClientId, clientId)); + if (ObjectUtil.isEmpty(sysAuth)) { + throw new ServiceException("clientId 错误"); + } + return BeanUtil.toBean(sysAuth, AuthBody.class); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysAuthMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysAuthMapper.xml new file mode 100644 index 000000000..82106f5be --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysAuthMapper.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 3e93ae98d..5d0377966 100644 --- a/script/sql/oracle/oracle_ry_vue_5.X.sql +++ b/script/sql/oracle/oracle_ry_vue_5.X.sql @@ -1108,6 +1108,42 @@ 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_auth ( + id number(20) not null, + client_id varchar(64) default null, + client_key varchar(32) default null, + client_secret varchar(255) default null, + auth_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_auth add constraint pk_sys_auth primary key (id); + +comment on table sys_auth is '系统授权表'; +comment on column sys_auth.id is '主建'; +comment on column sys_auth.client_id is '客户端id'; +comment on column sys_auth.client_key is '客户端key'; +comment on column sys_auth.client_secret is '客户端秘钥'; +comment on column sys_auth.auth_type is '授权类型'; +comment on column sys_auth.status is '状态(0正常 1停用)'; +comment on column sys_auth.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_auth.create_dept is '创建部门'; +comment on column sys_auth.create_by is '创建者'; +comment on column sys_auth.create_time is '创建时间'; +comment on column sys_auth.update_by is '更新者'; +comment on column sys_auth.update_time is '更新时间'; + +insert into sys_auth values (1, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password', 0, 0, 103, 1, sysdate, 1, sysdate); +insert into sys_auth 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 b51004246..28f1863cc 100644 --- a/script/sql/postgres/postgres_ry_vue_5.X.sql +++ b/script/sql/postgres/postgres_ry_vue_5.X.sql @@ -1132,6 +1132,44 @@ 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_auth; +create table sys_auth ( + id int8, + client_id varchar(64) ''::varchar, + client_key varchar(32) ''::varchar, + client_secret varchar(255) ''::varchar, + auth_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_auth_pk primary key (id) +) + +comment on table sys_auth is '系统授权表'; +comment on column sys_auth.id is '主建'; +comment on column sys_auth.client_id is '客户端id'; +comment on column sys_auth.client_key is '客户端key'; +comment on column sys_auth.client_secret is '客户端秘钥'; +comment on column sys_auth.auth_type is '授权类型'; +comment on column sys_auth.status is '状态(0正常 1停用)'; +comment on column sys_auth.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_auth.create_dept is '创建部门'; +comment on column sys_auth.create_by is '创建者'; +comment on column sys_auth.create_time is '创建时间'; +comment on column sys_auth.update_by is '更新者'; +comment on column sys_auth.update_time is '更新时间'; + +insert into sys_auth values (1, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password', 0, 0, 103, 1, now(), 1, now()); +insert into sys_auth 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 713d962d4..feea2bf6c 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_auth; +create table sys_auth ( + 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 '客户端秘钥', + auth_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_auth values (1, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password', 0, 0, 103, 1, sysdate(), 1, sysdate()); +insert into sys_auth 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 4bff0ed57..5ef3e335b 100644 --- a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql +++ b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql @@ -2819,3 +2819,107 @@ 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_auth +( + id bigint NOT NULL, + client_id nvarchar(20) DEFAULT '' NULL, + client_key nvarchar(255) DEFAULT '' NULL, + client_secret nvarchar(255) DEFAULT '' NULL, + auth_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_auth___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_auth', + 'COLUMN', N'id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'客户端id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_auth', + 'COLUMN', N'tenant_id' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'客户端key', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_auth', + 'COLUMN', N'client_key' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'客户端秘钥', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_auth', + 'COLUMN', N'client_secret' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'授权类型', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_auth', + 'COLUMN', N'auth_type' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'状态(0正常 1停用)', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'status' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'删除标志(0代表存在 2代表删除)', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_auth', + 'COLUMN', N'del_flag' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_auth', + 'COLUMN', N'create_dept' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'创建者', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_auth', + 'COLUMN', N'create_by' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_auth', + 'COLUMN', N'create_time' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'更新者', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_auth', + 'COLUMN', N'update_by' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'更新时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_auth', + 'COLUMN', N'update_time' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'系统授权表', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_auth' +GO + +INSERT INTO sys_auth VALUES (N'1', N'e5cd7e4891bf95d1d19206ce24a7b32e', N'pc', N'pc123', N'password', N'0', N'0', 103, 1, getdate(), 1, getdate()) +GO +INSERT INTO sys_auth VALUES (N'2', N'428a8310cd442757ae699df5d894f051', N'app', N'app123', N'password,sms', N'0', N'0', 103, 1, getdate(), 1, getdate()) +GO