From 6ed424f89e0bb7f0611b89a2ee947b6e0b1c089b 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: Thu, 30 Mar 2023 10:19:04 +0800 Subject: [PATCH] =?UTF-8?q?add=20=E5=A2=9E=E5=8A=A0=20=E9=82=AE=E7=AE=B1?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81=E5=8F=91=E9=80=81=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=20add=20=E5=A2=9E=E5=8A=A0=20=E9=82=AE=E7=AE=B1=E7=99=BB?= =?UTF-8?q?=E9=99=86=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ruoyi/web/controller/AuthController.java | 18 ++++++- .../web/controller/CaptchaController.java | 28 ++++++++++- .../ruoyi/web/service/SysLoginService.java | 47 +++++++++++++++++++ .../main/resources/i18n/messages.properties | 4 ++ .../resources/i18n/messages_en_US.properties | 4 ++ .../resources/i18n/messages_zh_CN.properties | 4 ++ .../core/domain/model/EmailLoginBody.java | 35 ++++++++++++++ .../core/domain/model/SmsLoginBody.java | 4 +- .../ruoyi/common/core/enums/LoginType.java | 5 ++ .../ruoyi/system/mapper/SysUserMapper.java | 18 +++++++ .../resources/mapper/system/SysUserMapper.xml | 10 ++++ 11 files changed, 172 insertions(+), 5 deletions(-) create mode 100644 ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/EmailLoginBody.java diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/AuthController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/AuthController.java index 81af4dfa0..9c0a638e7 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/AuthController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/AuthController.java @@ -3,6 +3,7 @@ package com.ruoyi.web.controller; import cn.dev33.satoken.annotation.SaIgnore; import cn.hutool.core.collection.CollUtil; import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.domain.model.EmailLoginBody; import com.ruoyi.common.core.domain.model.LoginBody; import com.ruoyi.common.core.domain.model.RegisterBody; import com.ruoyi.common.core.domain.model.SmsLoginBody; @@ -62,7 +63,7 @@ public class AuthController { } /** - * 短信登录(示例) + * 短信登录 * * @param body 登录信息 * @return 结果 @@ -76,6 +77,21 @@ public class AuthController { 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); + } + /** * 小程序登录(示例) * diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/CaptchaController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/CaptchaController.java index ab9c4d15a..757caf5c0 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/CaptchaController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/CaptchaController.java @@ -11,6 +11,8 @@ import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.utils.SpringUtils; import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.reflect.ReflectUtils; +import com.ruoyi.common.mail.config.properties.MailProperties; +import com.ruoyi.common.mail.utils.MailUtils; import com.ruoyi.common.redis.utils.RedisUtils; import com.ruoyi.common.sms.config.properties.SmsProperties; import com.ruoyi.common.sms.core.SmsTemplate; @@ -46,6 +48,7 @@ public class CaptchaController { private final CaptchaProperties captchaProperties; private final SmsProperties smsProperties; + private final MailProperties mailProperties; /** * 短信验证码 @@ -53,8 +56,7 @@ public class CaptchaController { * @param phonenumber 用户手机号 */ @GetMapping("/sms/code") - public R smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}") - String phonenumber) { + public R smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) { if (!smsProperties.getEnabled()) { return R.fail("当前系统没有开启短信功能!"); } @@ -74,6 +76,28 @@ public class CaptchaController { return R.ok(); } + /** + * 邮箱验证码 + * + * @param email 邮箱 + */ + @GetMapping("/email/code") + public R emailCode(@NotBlank(message = "{user.email.not.blank}") String email) { + if (!mailProperties.getEnabled()) { + return R.fail("当前系统没有开启邮箱功能!"); + } + String key = GlobalConstants.CAPTCHA_CODE_KEY + email; + String code = RandomUtil.randomNumbers(4); + RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); + try { + MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。"); + } catch (Exception e) { + log.error("验证码短信发送异常 => {}", e.getMessage()); + return R.fail(e.getMessage()); + } + return R.ok(); + } + /** * 生成验证码 */ diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/service/SysLoginService.java b/ruoyi-admin/src/main/java/com/ruoyi/web/service/SysLoginService.java index fece94287..b853ab4b5 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/service/SysLoginService.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/service/SysLoginService.java @@ -112,6 +112,23 @@ public class SysLoginService { 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 授权后获取 @@ -184,6 +201,18 @@ public class SysLoginService { 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); + } + /** * 校验验证码 * @@ -241,6 +270,24 @@ public class SysLoginService { 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); diff --git a/ruoyi-admin/src/main/resources/i18n/messages.properties b/ruoyi-admin/src/main/resources/i18n/messages.properties index 3a5eeac88..4f2da28b4 100644 --- a/ruoyi-admin/src/main/resources/i18n/messages.properties +++ b/ruoyi-admin/src/main/resources/i18n/messages.properties @@ -18,6 +18,7 @@ user.password.not.blank=用户密码不能为空 user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间 user.password.not.valid=* 5-50个字符 user.email.not.valid=邮箱格式错误 +user.email.not.blank=邮箱不能为空 user.phonenumber.not.blank=用户手机号不能为空 user.mobile.phone.number.not.valid=手机号格式错误 user.login.success=登录成功 @@ -42,6 +43,9 @@ rate.limiter.message=访问过于频繁,请稍候再试 sms.code.not.blank=短信验证码不能为空 sms.code.retry.limit.count=短信验证码输入错误{0}次 sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟 +email.code.not.blank=邮箱验证码不能为空 +email.code.retry.limit.count=邮箱验证码输入错误{0}次 +email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟 xcx.code.not.blank=小程序code不能为空 ##租户 tenant.number.not.blank=租户编号不能为空 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 7568f6805..0c738a37e 100644 --- a/ruoyi-admin/src/main/resources/i18n/messages_en_US.properties +++ b/ruoyi-admin/src/main/resources/i18n/messages_en_US.properties @@ -18,6 +18,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.email.not.blank=Mailbox cannot be blank user.phonenumber.not.blank=Phone number cannot be blank user.mobile.phone.number.not.valid=Phone number format error user.login.success=Login successful @@ -42,6 +43,9 @@ rate.limiter.message=Visit too frequently, 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=Sms code input error {0} times, account locked for {1} minutes +email.code.not.blank=Email code cannot be blank +email.code.retry.limit.count=Email code input error {0} times +email.code.retry.limit.exceed=Email code input error {0} times, account locked for {1} minutes xcx.code.not.blank=Mini program code cannot be blank ##租户 tenant.number.not.blank=Tenant number cannot be blank 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 3a5eeac88..4f2da28b4 100644 --- a/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties +++ b/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties @@ -18,6 +18,7 @@ user.password.not.blank=用户密码不能为空 user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间 user.password.not.valid=* 5-50个字符 user.email.not.valid=邮箱格式错误 +user.email.not.blank=邮箱不能为空 user.phonenumber.not.blank=用户手机号不能为空 user.mobile.phone.number.not.valid=手机号格式错误 user.login.success=登录成功 @@ -42,6 +43,9 @@ rate.limiter.message=访问过于频繁,请稍候再试 sms.code.not.blank=短信验证码不能为空 sms.code.retry.limit.count=短信验证码输入错误{0}次 sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟 +email.code.not.blank=邮箱验证码不能为空 +email.code.retry.limit.count=邮箱验证码输入错误{0}次 +email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟 xcx.code.not.blank=小程序code不能为空 ##租户 tenant.number.not.blank=租户编号不能为空 diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/EmailLoginBody.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/EmailLoginBody.java new file mode 100644 index 000000000..645967395 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/EmailLoginBody.java @@ -0,0 +1,35 @@ +package com.ruoyi.common.core.domain.model; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +/** + * 短信登录对象 + * + * @author Lion Li + */ + +@Data +public class EmailLoginBody { + + /** + * 租户ID + */ + @NotBlank(message = "{tenant.number.not.blank}") + private String tenantId; + + /** + * 邮箱 + */ + @NotBlank(message = "{user.email.not.blank}") + @Email(message = "{user.email.not.valid}") + private String email; + + /** + * 邮箱code + */ + @NotBlank(message = "{email.code.not.blank}") + private String emailCode; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java index 3b25fce00..70df20d20 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java @@ -20,13 +20,13 @@ public class SmsLoginBody { private String tenantId; /** - * 用户名 + * 手机号 */ @NotBlank(message = "{user.phonenumber.not.blank}") private String phonenumber; /** - * 用户密码 + * 短信code */ @NotBlank(message = "{sms.code.not.blank}") private String smsCode; diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/LoginType.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/LoginType.java index fcd48dfbb..dbd6de1fb 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/LoginType.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/LoginType.java @@ -22,6 +22,11 @@ public enum LoginType { */ SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"), + /** + * 邮箱登录 + */ + EMAIL("email.code.retry.limit.exceed", "email.code.retry.limit.count"), + /** * 小程序登录 */ 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 84b26fd48..2aacf487e 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 @@ -78,6 +78,14 @@ public interface SysUserMapper extends BaseMapperPlus { */ SysUserVo selectUserByPhonenumber(String phonenumber); + /** + * 通过邮箱查询用户 + * + * @param email 邮箱 + * @return 用户对象信息 + */ + SysUserVo selectUserByEmail(String email); + /** * 通过用户名查询用户(不走租户插件) * @@ -98,6 +106,16 @@ public interface SysUserMapper extends BaseMapperPlus { @InterceptorIgnore(tenantLine = "true") SysUserVo selectTenantUserByPhonenumber(String phonenumber, String tenantId); + /** + * 通过邮箱查询用户(不走租户插件) + * + * @param email 邮箱 + * @param tenantId 租户id + * @return 用户对象信息 + */ + @InterceptorIgnore(tenantLine = "true") + SysUserVo selectTenantUserByEmail(String email, String tenantId); + /** * 通过用户ID查询用户 * diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml index 481c17ab5..92d8c9e05 100644 --- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -102,6 +102,11 @@ where u.del_flag = '0' and u.phonenumber = #{phonenumber} + + + +