From bb479c436aef5f2033c7a2cb727860d64fd3f436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Wed, 23 Mar 2022 01:02:07 +0800 Subject: [PATCH] =?UTF-8?q?add=20=E5=A2=9E=E5=8A=A0=20=E7=9F=AD=E4=BF=A1?= =?UTF-8?q?=E7=99=BB=E5=BD=95=20=E4=B8=8E=20=E5=B0=8F=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=E7=99=BB=E5=BD=95=20=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ruoyi/system/api/RemoteUserService.java | 16 +++++ .../auth/controller/TokenController.java | 36 +++++++++- .../com/ruoyi/auth/form/SmsLoginBody.java | 33 +++++++++ .../ruoyi/auth/service/SysLoginService.java | 59 ++++++++++++++++ .../ruoyi/common/core/enums/DeviceType.java | 7 +- .../main/resources/i18n/messages.properties | 5 ++ .../resources/i18n/messages_en_US.properties | 5 ++ .../resources/i18n/messages_zh_CN.properties | 5 ++ .../system/dubbo/RemoteUserServiceImpl.java | 69 ++++++++++++++----- .../ruoyi/system/mapper/SysUserMapper.java | 8 +++ .../ruoyi/system/service/ISysUserService.java | 8 +++ .../service/impl/SysUserServiceImpl.java | 11 +++ .../resources/mapper/system/SysUserMapper.xml | 9 ++- 13 files changed, 250 insertions(+), 21 deletions(-) create mode 100644 ruoyi-auth/src/main/java/com/ruoyi/auth/form/SmsLoginBody.java diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteUserService.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteUserService.java index f45958244..09fef3c29 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteUserService.java +++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteUserService.java @@ -19,6 +19,22 @@ public interface RemoteUserService { */ LoginUser getUserInfo(String username) throws UserException; + /** + * 通过手机号查询用户信息 + * + * @param phonenumber 手机号 + * @return 结果 + */ + LoginUser getUserInfoByPhonenumber(String phonenumber) throws UserException; + + /** + * 通过openid查询用户信息 + * + * @param openid openid + * @return 结果 + */ + LoginUser getUserInfoByOpenid(String openid) throws UserException; + /** * 注册用户信息 * diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java index eed71c06c..91d332258 100644 --- a/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java +++ b/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java @@ -2,12 +2,13 @@ package com.ruoyi.auth.controller; import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.stp.StpUtil; +import com.alibaba.nacos.api.common.Constants; import com.ruoyi.auth.form.LoginBody; import com.ruoyi.auth.form.RegisterBody; +import com.ruoyi.auth.form.SmsLoginBody; import com.ruoyi.auth.service.SysLoginService; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.satoken.utils.LoginHelper; -import com.ruoyi.system.api.model.LoginUser; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; @@ -17,6 +18,7 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import javax.validation.constraints.NotBlank; import java.util.HashMap; import java.util.Map; @@ -45,6 +47,38 @@ public class TokenController { return R.ok(rspMap); } + /** + * 短信登录(示例) + * + * @param smsLoginBody 登录信息 + * @return 结果 + */ + @ApiOperation("短信登录(示例)") + @PostMapping("/smsLogin") + public R> smsLogin(@Validated @RequestBody SmsLoginBody smsLoginBody) { + Map ajax = new HashMap<>(); + // 生成令牌 + String token = sysLoginService.smsLogin(smsLoginBody.getPhonenumber(), smsLoginBody.getSmsCode()); + ajax.put(Constants.TOKEN, token); + return R.ok(ajax); + } + + /** + * 小程序登录(示例) + * + * @param xcxCode 小程序code + * @return 结果 + */ + @ApiOperation("短信登录(示例)") + @PostMapping("/xcxLogin") + public R> xcxLogin(@NotBlank(message = "{xcx.code.not.blank}") String xcxCode) { + Map ajax = new HashMap<>(); + // 生成令牌 + String token = sysLoginService.xcxLogin(xcxCode); + ajax.put(Constants.TOKEN, token); + return R.ok(ajax); + } + @ApiOperation("登出方法") @DeleteMapping("logout") public R logout() { diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/form/SmsLoginBody.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/form/SmsLoginBody.java new file mode 100644 index 000000000..a42273080 --- /dev/null +++ b/ruoyi-auth/src/main/java/com/ruoyi/auth/form/SmsLoginBody.java @@ -0,0 +1,33 @@ +package com.ruoyi.auth.form; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * 短信登录对象 + * + * @author Lion Li + */ + +@Data +@ApiModel("短信登录对象") +public class SmsLoginBody { + + /** + * 用户名 + */ + @NotBlank(message = "{user.phonenumber.not.blank}") + @ApiModelProperty(value = "用户手机号") + private String phonenumber; + + /** + * 用户密码 + */ + @NotBlank(message = "{sms.code.not.blank}") + @ApiModelProperty(value = "短信验证码") + private String smsCode; + +} diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/service/SysLoginService.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/service/SysLoginService.java index f698f933a..52528c91a 100644 --- a/ruoyi-auth/src/main/java/com/ruoyi/auth/service/SysLoginService.java +++ b/ruoyi-auth/src/main/java/com/ruoyi/auth/service/SysLoginService.java @@ -75,6 +75,57 @@ public class SysLoginService { return StpUtil.getTokenValue(); } + public String smsLogin(String phonenumber, String smsCode) { + // 通过手机号查找用户 + LoginUser userInfo = remoteUserService.getUserInfoByPhonenumber(phonenumber); + + // 获取用户登录错误次数(可自定义限制策略 例如: key + username + ip) + Integer errorNumber = RedisUtils.getCacheObject(CacheConstants.LOGIN_ERROR + userInfo.getUsername()); + // 锁定时间内登录 则踢出 + if (ObjectUtil.isNotNull(errorNumber) && errorNumber.equals(CacheConstants.LOGIN_ERROR_NUMBER)) { + recordLogininfor(userInfo.getUsername(), Constants.LOGIN_FAIL, MessageUtils.message("sms.code.retry.limit.exceed", CacheConstants.LOGIN_ERROR_LIMIT_TIME)); + throw new UserException("sms.code.retry.limit.exceed", CacheConstants.LOGIN_ERROR_LIMIT_TIME); + } + + if (!validateSmsCode(phonenumber, smsCode)) { + // 是否第一次 + errorNumber = ObjectUtil.isNull(errorNumber) ? 1 : errorNumber + 1; + // 达到规定错误次数 则锁定登录 + if (errorNumber.equals(CacheConstants.LOGIN_ERROR_NUMBER)) { + RedisUtils.setCacheObject(CacheConstants.LOGIN_ERROR + userInfo.getUsername(), errorNumber, CacheConstants.LOGIN_ERROR_LIMIT_TIME, TimeUnit.MINUTES); + recordLogininfor(userInfo.getUsername(), Constants.LOGIN_FAIL, MessageUtils.message("sms.code.retry.limit.exceed", CacheConstants.LOGIN_ERROR_LIMIT_TIME)); + throw new UserException("sms.code.retry.limit.exceed", CacheConstants.LOGIN_ERROR_LIMIT_TIME); + } else { + // 未达到规定错误次数 则递增 + RedisUtils.setCacheObject(CacheConstants.LOGIN_ERROR + userInfo.getUsername(), errorNumber); + recordLogininfor(userInfo.getUsername(), Constants.LOGIN_FAIL, MessageUtils.message("sms.code.retry.limit.count", errorNumber)); + throw new UserException("sms.code.retry.limit.count", errorNumber); + } + } + + // 登录成功 清空错误次数 + RedisUtils.deleteObject(CacheConstants.LOGIN_ERROR + userInfo.getUsername()); + + // 生成token + LoginHelper.loginByDevice(userInfo, DeviceType.APP); + + recordLogininfor(userInfo.getUsername(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); + return StpUtil.getTokenValue(); + } + + public String xcxLogin(String xcxCode) { + // xcxCode 为 小程序调用 wx.login 授权后获取 + // todo 自行实现 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid + String openid = ""; + LoginUser userInfo = remoteUserService.getUserInfoByOpenid(openid); + + // 生成token + LoginHelper.loginByDevice(userInfo, DeviceType.XCX); + + recordLogininfor(userInfo.getUsername(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); + return StpUtil.getTokenValue(); + } + public void logout(String loginName) { recordLogininfor(loginName, Constants.LOGOUT, MessageUtils.message("user.logout.success")); } @@ -122,4 +173,12 @@ public class SysLoginService { } remoteLogService.saveLogininfor(logininfor); } + + /** + * 校验短信验证码 + */ + private boolean validateSmsCode(String phonenumber, String smsCode) { + // todo 此处使用手机号查询redis验证码与参数验证码是否一致 用户自行实现 + return true; + } } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/DeviceType.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/DeviceType.java index 981b2cc92..b15b91c52 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/DeviceType.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/DeviceType.java @@ -21,7 +21,12 @@ public enum DeviceType { /** * app端 */ - APP("app"); + APP("app"), + + /** + * 小程序端 + */ + XCX("xcx"); private final String device; } diff --git a/ruoyi-common/ruoyi-common-core/src/main/resources/i18n/messages.properties b/ruoyi-common/ruoyi-common-core/src/main/resources/i18n/messages.properties index 9992e05e8..062d6172e 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/resources/i18n/messages.properties +++ b/ruoyi-common/ruoyi-common-core/src/main/resources/i18n/messages.properties @@ -19,6 +19,7 @@ user.password.not.blank=\u7528\u6237\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A user.password.length.valid=\u7528\u6237\u5BC6\u7801\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4 user.password.not.valid=* 5-50\u4E2A\u5B57\u7B26 user.email.not.valid=\u90AE\u7BB1\u683C\u5F0F\u9519\u8BEF +user.phonenumber.not.blank=\u7528\u6237\u624B\u673A\u53F7\u4E0D\u80FD\u4E3A\u7A7A user.mobile.phone.number.not.valid=\u624B\u673A\u53F7\u683C\u5F0F\u9519\u8BEF user.login.success=\u767B\u5F55\u6210\u529F user.register.success=\u6CE8\u518C\u6210\u529F @@ -38,3 +39,7 @@ no.delete.permission=\u60A8\u6CA1\u6709\u5220\u9664\u6570\u636E\u7684\u6743\u965 no.export.permission=\u60A8\u6CA1\u6709\u5BFC\u51FA\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}] no.view.permission=\u60A8\u6CA1\u6709\u67E5\u770B\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}] repeat.submit.message=\u4E0D\u5141\u8BB8\u91CD\u590D\u63D0\u4EA4\uFF0C\u8BF7\u7A0D\u5019\u518D\u8BD5 +sms.code.not.blank=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A +sms.code.retry.limit.count=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21 +sms.code.retry.limit.exceed=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u9519\u8BEF\u6B21\u6570\u8FC7\u591A\uFF0C\u5E10\u6237\u9501\u5B9A{0}\u5206\u949F +xcx.code.not.blank=\u5C0F\u7A0B\u5E8Fcode\u4E0D\u80FD\u4E3A\u7A7A diff --git a/ruoyi-common/ruoyi-common-core/src/main/resources/i18n/messages_en_US.properties b/ruoyi-common/ruoyi-common-core/src/main/resources/i18n/messages_en_US.properties index 5c276a0dc..23b315f8e 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/resources/i18n/messages_en_US.properties +++ b/ruoyi-common/ruoyi-common-core/src/main/resources/i18n/messages_en_US.properties @@ -19,6 +19,7 @@ user.password.not.blank=Password cannot be empty user.password.length.valid=Password length must be between {min} and {max} characters user.password.not.valid=* 5-50 characters user.email.not.valid=Mailbox format error +user.phonenumber.not.blank=Phone number cannot be blank user.mobile.phone.number.not.valid=Phone number format error user.login.success=Login successful user.register.success=Register successful @@ -38,3 +39,7 @@ no.delete.permission=You do not have permission to delete data\uFF0Cplease conta no.export.permission=You do not have permission to export data\uFF0Cplease contact your administrator to add permissions [{0}] no.view.permission=You do not have permission to view data\uFF0Cplease contact your administrator to add permissions [{0}] repeat.submit.message=Repeat submit is not allowed, please try again later +sms.code.not.blank=Sms code cannot be blank +sms.code.retry.limit.count=Sms code input error {0} times +sms.code.retry.limit.exceed=Too many sms code errors, account locked for {0} minutes +xcx.code.not.blank=Mini program code cannot be blank diff --git a/ruoyi-common/ruoyi-common-core/src/main/resources/i18n/messages_zh_CN.properties b/ruoyi-common/ruoyi-common-core/src/main/resources/i18n/messages_zh_CN.properties index 9992e05e8..062d6172e 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/resources/i18n/messages_zh_CN.properties +++ b/ruoyi-common/ruoyi-common-core/src/main/resources/i18n/messages_zh_CN.properties @@ -19,6 +19,7 @@ user.password.not.blank=\u7528\u6237\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A user.password.length.valid=\u7528\u6237\u5BC6\u7801\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4 user.password.not.valid=* 5-50\u4E2A\u5B57\u7B26 user.email.not.valid=\u90AE\u7BB1\u683C\u5F0F\u9519\u8BEF +user.phonenumber.not.blank=\u7528\u6237\u624B\u673A\u53F7\u4E0D\u80FD\u4E3A\u7A7A user.mobile.phone.number.not.valid=\u624B\u673A\u53F7\u683C\u5F0F\u9519\u8BEF user.login.success=\u767B\u5F55\u6210\u529F user.register.success=\u6CE8\u518C\u6210\u529F @@ -38,3 +39,7 @@ no.delete.permission=\u60A8\u6CA1\u6709\u5220\u9664\u6570\u636E\u7684\u6743\u965 no.export.permission=\u60A8\u6CA1\u6709\u5BFC\u51FA\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}] no.view.permission=\u60A8\u6CA1\u6709\u67E5\u770B\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}] repeat.submit.message=\u4E0D\u5141\u8BB8\u91CD\u590D\u63D0\u4EA4\uFF0C\u8BF7\u7A0D\u5019\u518D\u8BD5 +sms.code.not.blank=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A +sms.code.retry.limit.count=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21 +sms.code.retry.limit.exceed=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u9519\u8BEF\u6B21\u6570\u8FC7\u591A\uFF0C\u5E10\u6237\u9501\u5B9A{0}\u5206\u949F +xcx.code.not.blank=\u5C0F\u7A0B\u5E8Fcode\u4E0D\u80FD\u4E3A\u7A7A diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/dubbo/RemoteUserServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/dubbo/RemoteUserServiceImpl.java index 1d53e5bf8..305244249 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/dubbo/RemoteUserServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/dubbo/RemoteUserServiceImpl.java @@ -18,7 +18,6 @@ import org.apache.dubbo.config.annotation.DubboService; import org.springframework.stereotype.Service; import java.util.List; -import java.util.Set; /** * 操作日志记录 @@ -46,22 +45,41 @@ public class RemoteUserServiceImpl implements RemoteUserService { if (UserStatus.DISABLE.getCode().equals(sysUser.getStatus())) { throw new UserException("user.blocked", username); } - // 角色集合 - Set rolePermission = permissionService.getRolePermission(sysUser.getUserId()); - // 权限集合 - Set menuPermissions = permissionService.getMenuPermission(sysUser.getUserId()); - LoginUser loginUser = new LoginUser(); - loginUser.setUserId(sysUser.getUserId()); - loginUser.setDeptId(sysUser.getDeptId()); - loginUser.setUsername(sysUser.getUserName()); - loginUser.setPassword(sysUser.getPassword()); - loginUser.setUserType(sysUser.getUserType()); - loginUser.setDeptName(sysUser.getDept().getDeptName()); - loginUser.setMenuPermission(menuPermissions); - loginUser.setRolePermission(rolePermission); - List roles = BeanUtil.copyToList(sysUser.getRoles(), RoleDTO.class); - loginUser.setRoles(roles); - return loginUser; + // 此处可根据登录用户的数据不同 自行创建 loginUser + return buildLoginUser(sysUser); + } + + @Override + public LoginUser getUserInfoByPhonenumber(String phonenumber) throws UserException { + SysUser sysUser = userService.selectUserByPhonenumber(phonenumber); + if (ObjectUtil.isNull(sysUser)) { + throw new UserException("user.not.exists", phonenumber); + } + if (UserStatus.DELETED.getCode().equals(sysUser.getDelFlag())) { + throw new UserException("user.password.delete", phonenumber); + } + if (UserStatus.DISABLE.getCode().equals(sysUser.getStatus())) { + throw new UserException("user.blocked", phonenumber); + } + // 此处可根据登录用户的数据不同 自行创建 loginUser + return buildLoginUser(sysUser); + } + + @Override + public LoginUser getUserInfoByOpenid(String openid) throws UserException { + // todo 自行实现 userService.selectUserByOpenid(openid); + SysUser sysUser = new SysUser(); + if (ObjectUtil.isNull(sysUser)) { + // todo 用户不存在 业务逻辑自行实现 + } + if (UserStatus.DELETED.getCode().equals(sysUser.getDelFlag())) { + // todo 用户已被删除 业务逻辑自行实现 + } + if (UserStatus.DISABLE.getCode().equals(sysUser.getStatus())) { + // todo 用户已被停用 业务逻辑自行实现 + } + // 此处可根据登录用户的数据不同 自行创建 loginUser + return buildLoginUser(sysUser); } @Override @@ -76,4 +94,21 @@ public class RemoteUserServiceImpl implements RemoteUserService { return userService.registerUser(sysUser); } + /** + * 构建登录用户 + */ + private LoginUser buildLoginUser(SysUser user) { + LoginUser loginUser = new LoginUser(); + loginUser.setUserId(user.getUserId()); + loginUser.setDeptId(user.getDeptId()); + loginUser.setUsername(user.getUserName()); + loginUser.setUserType(user.getUserType()); + loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId())); + loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId())); + loginUser.setDeptName(user.getDept().getDeptName()); + List roles = BeanUtil.copyToList(user.getRoles(), RoleDTO.class); + loginUser.setRoles(roles); + return loginUser; + } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java index 006784cec..c64be9f91 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java @@ -69,6 +69,14 @@ public interface SysUserMapper extends BaseMapperPlus - where u.user_name = #{userName} + where u.del_flag = '0' and u.user_name = #{userName} + +