Browse Source

add 增加 邮箱验证码发送接口
add 增加 邮箱登陆接口

疯狂的狮子li 2 years ago
parent
commit
16d58bacbc

+ 26 - 2
ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java

@@ -10,10 +10,12 @@ import com.ruoyi.common.constant.Constants;
 import com.ruoyi.common.core.domain.R;
 import com.ruoyi.common.enums.CaptchaType;
 import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.email.MailUtils;
 import com.ruoyi.common.utils.redis.RedisUtils;
 import com.ruoyi.common.utils.reflect.ReflectUtils;
 import com.ruoyi.common.utils.spring.SpringUtils;
 import com.ruoyi.framework.config.properties.CaptchaProperties;
+import com.ruoyi.framework.config.properties.MailProperties;
 import com.ruoyi.sms.config.properties.SmsProperties;
 import com.ruoyi.sms.core.SmsTemplate;
 import com.ruoyi.sms.entity.SmsResult;
@@ -47,6 +49,7 @@ public class CaptchaController {
     private final CaptchaProperties captchaProperties;
     private final SmsProperties smsProperties;
     private final ISysConfigService configService;
+    private final MailProperties mailProperties;
 
     /**
      * 短信验证码
@@ -54,8 +57,7 @@ public class CaptchaController {
      * @param phonenumber 用户手机号
      */
     @GetMapping("/captchaSms")
-    public R<Void> smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}")
-                              String phonenumber) {
+    public R<Void> smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
         if (!smsProperties.getEnabled()) {
             return R.fail("当前系统没有开启短信功能!");
         }
@@ -76,6 +78,28 @@ public class CaptchaController {
     }
 
     /**
+     * 邮箱验证码
+     *
+     * @param email 邮箱
+     */
+    @GetMapping("/captchaEmail")
+    public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) {
+        if (!mailProperties.getEnabled()) {
+            return R.fail("当前系统没有开启邮箱功能!");
+        }
+        String key = CacheConstants.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();
+    }
+
+    /**
      * 生成验证码
      */
     @GetMapping("/captchaImage")

+ 17 - 1
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java

@@ -5,6 +5,7 @@ import com.ruoyi.common.constant.Constants;
 import com.ruoyi.common.core.domain.R;
 import com.ruoyi.common.core.domain.entity.SysMenu;
 import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.domain.model.EmailLoginBody;
 import com.ruoyi.common.core.domain.model.LoginBody;
 import com.ruoyi.common.core.domain.model.LoginUser;
 import com.ruoyi.common.core.domain.model.SmsLoginBody;
@@ -57,7 +58,7 @@ public class SysLoginController {
     }
 
     /**
-     * 短信登录(示例)
+     * 短信登录
      *
      * @param smsLoginBody 登录信息
      * @return 结果
@@ -73,6 +74,21 @@ public class SysLoginController {
     }
 
     /**
+     * 邮件登录
+     *
+     * @param body 登录信息
+     * @return 结果
+     */
+    @PostMapping("/emailLogin")
+    public R<Map<String, Object>> emailLogin(@Validated @RequestBody EmailLoginBody body) {
+        Map<String, Object> ajax = new HashMap<>();
+        // 生成令牌
+        String token = loginService.emailLogin(body.getEmail(), body.getEmailCode());
+        ajax.put(Constants.TOKEN, token);
+        return R.ok(ajax);
+    }
+
+    /**
      * 小程序登录(示例)
      *
      * @param xcxCode 小程序code

+ 4 - 0
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,4 +43,7 @@ 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不能为空

+ 4 - 0
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,4 +43,7 @@ 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

+ 4 - 0
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,4 +43,7 @@ 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不能为空

+ 30 - 0
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/EmailLoginBody.java

@@ -0,0 +1,30 @@
+package com.ruoyi.common.core.domain.model;
+
+import lombok.Data;
+
+import javax.validation.constraints.Email;
+import javax.validation.constraints.NotBlank;
+
+/**
+ * 短信登录对象
+ *
+ * @author Lion Li
+ */
+
+@Data
+public class EmailLoginBody {
+
+    /**
+     * 邮箱
+     */
+    @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;
+
+}

+ 2 - 2
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java

@@ -14,13 +14,13 @@ import javax.validation.constraints.NotBlank;
 public class SmsLoginBody {
 
     /**
-     * 用户名
+     * 手机号
      */
     @NotBlank(message = "{user.phonenumber.not.blank}")
     private String phonenumber;
 
     /**
-     * 用户密码
+     * 短信code
      */
     @NotBlank(message = "{sms.code.not.blank}")
     private String smsCode;

+ 5 - 0
ruoyi-common/src/main/java/com/ruoyi/common/enums/LoginType.java

@@ -23,6 +23,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"),
+
+    /**
      * 小程序登录
      */
     XCX("", "");

+ 8 - 0
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java

@@ -77,6 +77,14 @@ public interface SysUserMapper extends BaseMapperPlus<SysUserMapper, SysUser, Sy
     SysUser selectUserByPhonenumber(String phonenumber);
 
     /**
+     * 通过邮箱查询用户
+     *
+     * @param email 邮箱
+     * @return 用户对象信息
+     */
+    SysUser selectUserByEmail(String email);
+
+    /**
      * 通过用户ID查询用户
      *
      * @param userId 用户ID

+ 40 - 0
ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java

@@ -98,6 +98,20 @@ public class SysLoginService {
         return StpUtil.getTokenValue();
     }
 
+    public String emailLogin(String email, String emailCode) {
+        // 通过手机号查找用户
+        SysUser user = loadUserByEmail(email);
+
+        checkLogin(LoginType.EMAIL, user.getUserName(), () -> !validateEmailCode(email, emailCode));
+        // 此处可根据登录用户的数据不同 自行创建 loginUser
+        LoginUser loginUser = buildLoginUser(user);
+        // 生成token
+        LoginHelper.loginByDevice(loginUser, DeviceType.APP);
+
+        recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
+        recordLoginInfo(user.getUserId(), user.getUserName());
+        return StpUtil.getTokenValue();
+    }
 
     public String xcxLogin(String xcxCode) {
         // xcxCode 为 小程序调用 wx.login 授权后获取
@@ -161,6 +175,18 @@ public class SysLoginService {
     }
 
     /**
+     * 校验邮箱验证码
+     */
+    private boolean validateEmailCode(String email, String emailCode) {
+        String code = RedisUtils.getCacheObject(CacheConstants.CAPTCHA_CODE_KEY + email);
+        if (StringUtils.isBlank(code)) {
+            recordLogininfor(email, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
+            throw new CaptchaExpireException();
+        }
+        return code.equals(emailCode);
+    }
+
+    /**
      * 校验验证码
      *
      * @param username 用户名
@@ -209,6 +235,20 @@ public class SysLoginService {
         return userMapper.selectUserByPhonenumber(phonenumber);
     }
 
+    private SysUser loadUserByEmail(String email) {
+        SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
+            .select(SysUser::getPhonenumber, SysUser::getStatus)
+            .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);
+        }
+        return userMapper.selectUserByEmail(email);
+    }
+
     private SysUser loadUserByOpenid(String openid) {
         // 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户
         // todo 自行实现 userService.selectUserByOpenid(openid);

+ 5 - 0
ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml

@@ -128,6 +128,11 @@
         where u.del_flag = '0' and u.phonenumber = #{phonenumber}
     </select>
 
+    <select id="selectUserByEmail" parameterType="String" resultMap="SysUserResult">
+        <include refid="selectUserVo"/>
+        where u.del_flag = '0' and u.email = #{email}
+    </select>
+
     <select id="selectUserById" parameterType="Long" resultMap="SysUserResult">
         <include refid="selectUserVo"/>
         where u.del_flag = '0' and u.user_id = #{userId}