疯狂的狮子li
2023-03-30 6ed424f89e0bb7f0611b89a2ee947b6e0b1c089b
add 增加 邮箱验证码发送接口
add 增加 邮箱登陆接口
已修改10个文件
已添加1个文件
177 ■■■■■ 文件已修改
ruoyi-admin/src/main/java/com/ruoyi/web/controller/AuthController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/CaptchaController.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/service/SysLoginService.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/i18n/messages.properties 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/i18n/messages_en_US.properties 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/EmailLoginBody.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/LoginType.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/AuthController.java
@@ -3,6 +3,7 @@
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 @@
    }
    /**
     * çŸ­ä¿¡ç™»å½•(示例)
     * çŸ­ä¿¡ç™»å½•
     *
     * @param body ç™»å½•信息
     * @return ç»“æžœ
@@ -77,6 +78,21 @@
    }
    /**
     * é‚®ä»¶ç™»å½•
     *
     * @param body ç™»å½•信息
     * @return ç»“æžœ
     */
    @PostMapping("/emailLogin")
    public R<LoginVo> 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
ruoyi-admin/src/main/java/com/ruoyi/web/controller/CaptchaController.java
@@ -11,6 +11,8 @@
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 @@
    private final CaptchaProperties captchaProperties;
    private final SmsProperties smsProperties;
    private final MailProperties mailProperties;
    /**
     * çŸ­ä¿¡éªŒè¯ç 
@@ -53,8 +56,7 @@
     * @param phonenumber ç”¨æˆ·æ‰‹æœºå·
     */
    @GetMapping("/sms/code")
    public R<Void> smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}")
                              String phonenumber) {
    public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
        if (!smsProperties.getEnabled()) {
            return R.fail("当前系统没有开启短信功能!");
        }
@@ -75,6 +77,28 @@
    }
    /**
     * é‚®ç®±éªŒè¯ç 
     *
     * @param email é‚®ç®±
     */
    @GetMapping("/email/code")
    public R<Void> 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();
    }
    /**
     * ç”ŸæˆéªŒè¯ç 
     */
    @GetMapping("/code")
ruoyi-admin/src/main/java/com/ruoyi/web/service/SysLoginService.java
@@ -112,6 +112,23 @@
        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 æŽˆæƒåŽèŽ·å–
@@ -185,6 +202,18 @@
    }
    /**
     * æ ¡éªŒé‚®ç®±éªŒè¯ç 
     */
    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 ç”¨æˆ·å
@@ -241,6 +270,24 @@
        return userMapper.selectUserByPhonenumber(phonenumber);
    }
    private SysUserVo loadUserByEmail(String tenantId, String email) {
        SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
            .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);
ruoyi-admin/src/main/resources/i18n/messages.properties
@@ -18,6 +18,7 @@
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 @@
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=租户编号不能为空
ruoyi-admin/src/main/resources/i18n/messages_en_US.properties
@@ -18,6 +18,7 @@
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 @@
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
ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties
@@ -18,6 +18,7 @@
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 @@
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=租户编号不能为空
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;
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java
@@ -20,13 +20,13 @@
    private String tenantId;
    /**
     * ç”¨æˆ·å
     * æ‰‹æœºå·
     */
    @NotBlank(message = "{user.phonenumber.not.blank}")
    private String phonenumber;
    /**
     * ç”¨æˆ·å¯†ç 
     * çŸ­ä¿¡code
     */
    @NotBlank(message = "{sms.code.not.blank}")
    private String smsCode;
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/LoginType.java
@@ -23,6 +23,11 @@
    SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"),
    /**
     * é‚®ç®±ç™»å½•
     */
    EMAIL("email.code.retry.limit.exceed", "email.code.retry.limit.count"),
    /**
     * å°ç¨‹åºç™»å½•
     */
    XCX("", "");
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
@@ -79,6 +79,14 @@
    SysUserVo selectUserByPhonenumber(String phonenumber);
    /**
     * é€šè¿‡é‚®ç®±æŸ¥è¯¢ç”¨æˆ·
     *
     * @param email é‚®ç®±
     * @return ç”¨æˆ·å¯¹è±¡ä¿¡æ¯
     */
    SysUserVo selectUserByEmail(String email);
    /**
     * é€šè¿‡ç”¨æˆ·åæŸ¥è¯¢ç”¨æˆ·(不走租户插件)
     *
     * @param userName ç”¨æˆ·å
@@ -99,6 +107,16 @@
    SysUserVo selectTenantUserByPhonenumber(String phonenumber, String tenantId);
    /**
     * é€šè¿‡é‚®ç®±æŸ¥è¯¢ç”¨æˆ·(不走租户插件)
     *
     * @param email    é‚®ç®±
     * @param tenantId ç§Ÿæˆ·id
     * @return ç”¨æˆ·å¯¹è±¡ä¿¡æ¯
     */
    @InterceptorIgnore(tenantLine = "true")
    SysUserVo selectTenantUserByEmail(String email, String tenantId);
    /**
     * é€šè¿‡ç”¨æˆ·ID查询用户
     *
     * @param userId ç”¨æˆ·ID
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
@@ -102,6 +102,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="selectTenantUserByUserName" parameterType="String" resultMap="SysUserResult">
        <include refid="selectUserVo"/>
        where u.del_flag = '0' and u.user_name = #{userName} and u.tenant_id = #{tenantId}
@@ -112,6 +117,11 @@
        where u.del_flag = '0' and u.phonenumber = #{phonenumber} and u.tenant_id = #{tenantId}
    </select>
    <select id="selectTenantUserByEmail" parameterType="String" resultMap="SysUserResult">
        <include refid="selectUserVo"/>
        where u.del_flag = '0' and u.email = #{email} and u.tenant_id = #{tenantId}
    </select>
    <select id="selectUserById" parameterType="Long" resultMap="SysUserResult">
        <include refid="selectUserVo"/>
        where u.del_flag = '0' and u.user_id = #{userId}