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 3589a3c8..9442dc29 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 @@ -28,6 +28,14 @@ public interface RemoteUserService { */ LoginUser getUserInfoByPhonenumber(String phonenumber) throws UserException; + /** + * 通过邮箱查询用户信息 + * + * @param email 邮箱 + * @return 结果 + */ + LoginUser getUserInfoByEmail(String email) throws UserException; + /** * 通过openid查询用户信息 * 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 37f44878..5f0b4831 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 @@ -1,5 +1,6 @@ package com.ruoyi.auth.controller; +import com.ruoyi.auth.form.EmailLoginBody; import com.ruoyi.auth.form.LoginBody; import com.ruoyi.auth.form.RegisterBody; import com.ruoyi.auth.form.SmsLoginBody; @@ -58,6 +59,21 @@ public class TokenController { return R.ok(ajax); } + /** + * 邮件登录 + * + * @param body 登录信息 + * @return 结果 + */ + @PostMapping("/emailLogin") + public R> emailLogin(@Validated @RequestBody EmailLoginBody body) { + Map ajax = new HashMap<>(); + // 生成令牌 + String token = sysLoginService.emailLogin(body.getEmail(), body.getEmailCode()); + ajax.put(Constants.ACCESS_TOKEN, token); + return R.ok(ajax); + } + /** * 小程序登录(示例) * diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/form/EmailLoginBody.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/form/EmailLoginBody.java new file mode 100644 index 00000000..196fb7c9 --- /dev/null +++ b/ruoyi-auth/src/main/java/com/ruoyi/auth/form/EmailLoginBody.java @@ -0,0 +1,30 @@ +package com.ruoyi.auth.form; + +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; + +} 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 index 1e7d35dd..5d44d5ea 100644 --- a/ruoyi-auth/src/main/java/com/ruoyi/auth/form/SmsLoginBody.java +++ b/ruoyi-auth/src/main/java/com/ruoyi/auth/form/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; 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 426ed045..d11e09eb 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 @@ -78,6 +78,18 @@ public class SysLoginService { return StpUtil.getTokenValue(); } + public String emailLogin(String email, String emailCode) { + // 通过邮箱查找用户 + LoginUser userInfo = remoteUserService.getUserInfoByEmail(email); + + checkLogin(LoginType.EMAIL, userInfo.getUsername(), () -> !validateEmailCode(email, emailCode)); + // 生成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 @@ -171,6 +183,18 @@ public class SysLoginService { return code.equals(smsCode); } + /** + * 校验邮箱验证码 + */ + 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); + } + /** * 登录校验 */ 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 fcd48dfb..dbd6de1f 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-common/ruoyi-common-core/src/main/resources/i18n/messages.properties b/ruoyi-common/ruoyi-common-core/src/main/resources/i18n/messages.properties index 4ff55f41..ffdd8f30 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 @@ -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不能为空 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 c0faca93..c1ca4391 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 @@ -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 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 4ff55f41..ffdd8f30 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 @@ -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不能为空 diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/config/properties/MailProperties.java b/ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/config/properties/MailProperties.java index f019473a..7970e47a 100644 --- a/ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/config/properties/MailProperties.java +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/config/properties/MailProperties.java @@ -15,7 +15,7 @@ public class MailProperties { /** * 过滤开关 */ - private String enabled; + private Boolean enabled; /** * SMTP服务器域名 diff --git a/ruoyi-modules/ruoyi-resource/src/main/java/com/ruoyi/resource/controller/SysEmailController.java b/ruoyi-modules/ruoyi-resource/src/main/java/com/ruoyi/resource/controller/SysEmailController.java new file mode 100644 index 00000000..495e6e4c --- /dev/null +++ b/ruoyi-modules/ruoyi-resource/src/main/java/com/ruoyi/resource/controller/SysEmailController.java @@ -0,0 +1,58 @@ +package com.ruoyi.resource.controller; + + +import cn.hutool.core.util.RandomUtil; +import com.ruoyi.common.core.constant.CacheConstants; +import com.ruoyi.common.core.constant.Constants; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.web.controller.BaseController; +import com.ruoyi.common.mail.config.properties.MailProperties; +import com.ruoyi.common.mail.utils.MailUtils; +import com.ruoyi.common.redis.utils.RedisUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.constraints.NotBlank; +import java.time.Duration; + +/** + * 邮件功能 + * + * @author Lion Li + */ +@Slf4j +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/email") +public class SysEmailController extends BaseController { + + private final MailProperties mailProperties; + + /** + * 邮箱验证码 + * + * @param email 邮箱 + */ + @GetMapping("/code") + public R 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(); + } + +} 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 357a2559..1f43d909 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 @@ -66,6 +66,21 @@ public class RemoteUserServiceImpl implements RemoteUserService { return buildLoginUser(userMapper.selectUserByPhonenumber(phonenumber)); } + @Override + public LoginUser getUserInfoByEmail(String email) throws UserException { + SysUser user = userMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getPhonenumber, SysUser::getStatus) + .eq(SysUser::getEmail, email)); + if (ObjectUtil.isNull(user)) { + throw new UserException("user.not.exists", email); + } + if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + throw new UserException("user.blocked", email); + } + // 此处可根据登录用户的数据不同 自行创建 loginUser + return buildLoginUser(userMapper.selectUserByEmail(email)); + } + @Override public XcxLoginUser getUserInfoByOpenid(String openid) throws UserException { // todo 自行实现 userService.selectUserByOpenid(openid); 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 c64be9f9..78a98d64 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 @@ -77,6 +77,14 @@ public interface SysUserMapper extends BaseMapperPlus + +