From 4a353896e3ab74e6bd9acda6bebc2fde167f2d00 Mon Sep 17 00:00:00 2001
From: 疯狂的狮子Li <15040126243@163.com>
Date: 星期三, 23 三月 2022 00:33:28 +0800
Subject: [PATCH] add 增加 短信登录 与 小程序登录 示例

---
 ruoyi-admin/src/main/resources/i18n/messages_en_US.properties                     |    5 +
 ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java          |  104 ++++++++++++++++++++++++++
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java  |   11 ++
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java |   34 ++++++++
 ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml                   |    5 +
 ruoyi-admin/src/main/resources/i18n/messages.properties                           |    5 +
 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java          |    8 ++
 ruoyi-common/src/main/java/com/ruoyi/common/enums/DeviceType.java                 |    7 +
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java             |    8 ++
 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java   |   33 ++++++++
 ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties                     |    5 +
 11 files changed, 224 insertions(+), 1 deletions(-)

diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
index fad8b5b..37f7e99 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
@@ -7,6 +7,7 @@
 import com.ruoyi.common.core.domain.entity.SysMenu;
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.core.domain.model.LoginBody;
+import com.ruoyi.common.core.domain.model.SmsLoginBody;
 import com.ruoyi.common.helper.LoginHelper;
 import com.ruoyi.system.domain.vo.RouterVo;
 import com.ruoyi.system.service.ISysMenuService;
@@ -22,6 +23,7 @@
 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.List;
 import java.util.Map;
@@ -60,6 +62,38 @@
         return R.ok(ajax);
     }
 
+    /**
+     * 鐭俊鐧诲綍(绀轰緥)
+     *
+     * @param smsLoginBody 鐧诲綍淇℃伅
+     * @return 缁撴灉
+     */
+    @ApiOperation("鐭俊鐧诲綍(绀轰緥)")
+    @PostMapping("/smsLogin")
+    public R<Map<String, Object>> smsLogin(@Validated @RequestBody SmsLoginBody smsLoginBody) {
+        Map<String, Object> ajax = new HashMap<>();
+        // 鐢熸垚浠ょ墝
+        String token = loginService.smsLogin(smsLoginBody.getPhonenumber(), smsLoginBody.getSmsCode());
+        ajax.put(Constants.TOKEN, token);
+        return R.ok(ajax);
+    }
+
+    /**
+     * 灏忕▼搴忕櫥褰�(绀轰緥)
+     *
+     * @param xcxCode 灏忕▼搴廲ode
+     * @return 缁撴灉
+     */
+    @ApiOperation("鐭俊鐧诲綍(绀轰緥)")
+    @PostMapping("/xcxLogin")
+    public R<Map<String, Object>> xcxLogin(@NotBlank(message = "{xcx.code.not.blank}") String xcxCode) {
+        Map<String, Object> ajax = new HashMap<>();
+        // 鐢熸垚浠ょ墝
+        String token = loginService.xcxLogin(xcxCode);
+        ajax.put(Constants.TOKEN, token);
+        return R.ok(ajax);
+    }
+
     @ApiOperation("鐧诲嚭鏂规硶")
     @PostMapping("/logout")
     public R<Void> logout() {
diff --git a/ruoyi-admin/src/main/resources/i18n/messages.properties b/ruoyi-admin/src/main/resources/i18n/messages.properties
index 522d1bc..ac1d8c2 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.length.valid=鐢ㄦ埛瀵嗙爜闀垮害蹇呴』鍦▄min}鍒皗max}涓瓧绗︿箣闂�
 user.password.not.valid=* 5-50涓瓧绗�
 user.email.not.valid=閭鏍煎紡閿欒
+user.phonenumber.not.blank=鐢ㄦ埛鎵嬫満鍙蜂笉鑳戒负绌�
 user.mobile.phone.number.not.valid=鎵嬫満鍙锋牸寮忛敊璇�
 user.login.success=鐧诲綍鎴愬姛
 user.register.success=娉ㄥ唽鎴愬姛
@@ -38,3 +39,7 @@
 no.view.permission=鎮ㄦ病鏈夋煡鐪嬫暟鎹殑鏉冮檺锛岃鑱旂郴绠$悊鍛樻坊鍔犳潈闄� [{0}]
 repeat.submit.message=涓嶅厑璁搁噸澶嶆彁浜わ紝璇风◢鍊欏啀璇�
 rate.limiter.message=璁块棶杩囦簬棰戠箒锛岃绋嶅�欏啀璇�
+sms.code.not.blank=鐭俊楠岃瘉鐮佷笉鑳戒负绌�
+sms.code.retry.limit.count=鐭俊楠岃瘉鐮佽緭鍏ラ敊璇瘂0}娆�
+sms.code.retry.limit.exceed=鐭俊楠岃瘉鐮侀敊璇鏁拌繃澶氾紝甯愭埛閿佸畾{0}鍒嗛挓
+xcx.code.not.blank=灏忕▼搴廲ode涓嶈兘涓虹┖
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 5dbdac5..9076f79 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.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.view.permission=You do not have permission to view data锛宲lease contact your administrator to add permissions [{0}]
 repeat.submit.message=Repeat submit is not allowed, please try again later
 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=Too many sms code errors, account locked for {0} minutes
+xcx.code.not.blank=Mini program code 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 522d1bc..ac1d8c2 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.length.valid=鐢ㄦ埛瀵嗙爜闀垮害蹇呴』鍦▄min}鍒皗max}涓瓧绗︿箣闂�
 user.password.not.valid=* 5-50涓瓧绗�
 user.email.not.valid=閭鏍煎紡閿欒
+user.phonenumber.not.blank=鐢ㄦ埛鎵嬫満鍙蜂笉鑳戒负绌�
 user.mobile.phone.number.not.valid=鎵嬫満鍙锋牸寮忛敊璇�
 user.login.success=鐧诲綍鎴愬姛
 user.register.success=娉ㄥ唽鎴愬姛
@@ -38,3 +39,7 @@
 no.view.permission=鎮ㄦ病鏈夋煡鐪嬫暟鎹殑鏉冮檺锛岃鑱旂郴绠$悊鍛樻坊鍔犳潈闄� [{0}]
 repeat.submit.message=涓嶅厑璁搁噸澶嶆彁浜わ紝璇风◢鍊欏啀璇�
 rate.limiter.message=璁块棶杩囦簬棰戠箒锛岃绋嶅�欏啀璇�
+sms.code.not.blank=鐭俊楠岃瘉鐮佷笉鑳戒负绌�
+sms.code.retry.limit.count=鐭俊楠岃瘉鐮佽緭鍏ラ敊璇瘂0}娆�
+sms.code.retry.limit.exceed=鐭俊楠岃瘉鐮侀敊璇鏁拌繃澶氾紝甯愭埛閿佸畾{0}鍒嗛挓
+xcx.code.not.blank=灏忕▼搴廲ode涓嶈兘涓虹┖
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java
new file mode 100644
index 0000000..f3ef445
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java
@@ -0,0 +1,33 @@
+package com.ruoyi.common.core.domain.model;
+
+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-common/src/main/java/com/ruoyi/common/enums/DeviceType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DeviceType.java
index e6ac849..118a212 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DeviceType.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DeviceType.java
@@ -21,7 +21,12 @@
     /**
      * app绔�
      */
-    APP("app");
+    APP("app"),
+
+    /**
+     * 灏忕▼搴忕
+     */
+    XCX("xcx");
 
     private final String device;
 }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
index 46e48fe..9b585e1 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
@@ -69,6 +69,14 @@
     SysUser selectUserByUserName(String userName);
 
     /**
+     * 閫氳繃鎵嬫満鍙锋煡璇㈢敤鎴�
+     *
+     * @param phonenumber 鎵嬫満鍙�
+     * @return 鐢ㄦ埛瀵硅薄淇℃伅
+     */
+    SysUser selectUserByPhonenumber(String phonenumber);
+
+    /**
      * 閫氳繃鐢ㄦ埛ID鏌ヨ鐢ㄦ埛
      *
      * @param userId 鐢ㄦ埛ID
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
index 741ae4b..9ecd86e 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
@@ -49,6 +49,14 @@
     SysUser selectUserByUserName(String userName);
 
     /**
+     * 閫氳繃鎵嬫満鍙锋煡璇㈢敤鎴�
+     *
+     * @param phonenumber 鎵嬫満鍙�
+     * @return 鐢ㄦ埛瀵硅薄淇℃伅
+     */
+    SysUser selectUserByPhonenumber(String phonenumber);
+
+    /**
      * 閫氳繃鐢ㄦ埛ID鏌ヨ鐢ㄦ埛
      *
      * @param userId 鐢ㄦ埛ID
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java
index 62ca18b..be5aac2 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java
@@ -87,6 +87,8 @@
 
         // 鐧诲綍鎴愬姛 娓呯┖閿欒娆℃暟
         RedisUtils.deleteObject(Constants.LOGIN_ERROR + username);
+
+        // 姝ゅ鍙牴鎹櫥褰曠敤鎴风殑鏁版嵁涓嶅悓 鑷鍒涘缓 loginUser
         LoginUser loginUser = buildLoginUser(user);
         // 鐢熸垚token
         LoginHelper.loginByDevice(loginUser, DeviceType.PC);
@@ -96,8 +98,78 @@
         return StpUtil.getTokenValue();
     }
 
+    public String smsLogin(String phonenumber, String smsCode) {
+        // 閫氳繃鎵嬫満鍙锋煡鎵剧敤鎴�
+        SysUser user = loadUserByPhonenumber(phonenumber);
+
+        HttpServletRequest request = ServletUtils.getRequest();
+        // 鑾峰彇鐢ㄦ埛鐧诲綍閿欒娆℃暟(鍙嚜瀹氫箟闄愬埗绛栫暐 渚嬪: key + username + ip)
+        Integer errorNumber = RedisUtils.getCacheObject(Constants.LOGIN_ERROR + user.getUserName());
+        // 閿佸畾鏃堕棿鍐呯櫥褰� 鍒欒涪鍑�
+        if (ObjectUtil.isNotNull(errorNumber) && errorNumber.equals(Constants.LOGIN_ERROR_NUMBER)) {
+            asyncService.recordLogininfor(user.getUserName(), Constants.LOGIN_FAIL, MessageUtils.message("sms.code.retry.limit.exceed", Constants.LOGIN_ERROR_LIMIT_TIME), request);
+            throw new UserException("sms.code.retry.limit.exceed", Constants.LOGIN_ERROR_LIMIT_TIME);
+        }
+
+        if (!validateSmsCode(phonenumber, smsCode)) {
+            // 鏄惁绗竴娆�
+            errorNumber = ObjectUtil.isNull(errorNumber) ? 1 : errorNumber + 1;
+            // 杈惧埌瑙勫畾閿欒娆℃暟 鍒欓攣瀹氱櫥褰�
+            if (errorNumber.equals(Constants.LOGIN_ERROR_NUMBER)) {
+                RedisUtils.setCacheObject(Constants.LOGIN_ERROR + user.getUserName(), errorNumber, Constants.LOGIN_ERROR_LIMIT_TIME, TimeUnit.MINUTES);
+                asyncService.recordLogininfor(user.getUserName(), Constants.LOGIN_FAIL, MessageUtils.message("sms.code.retry.limit.exceed", Constants.LOGIN_ERROR_LIMIT_TIME), request);
+                throw new UserException("sms.code.retry.limit.exceed", Constants.LOGIN_ERROR_LIMIT_TIME);
+            } else {
+                // 鏈揪鍒拌瀹氶敊璇鏁� 鍒欓�掑
+                RedisUtils.setCacheObject(Constants.LOGIN_ERROR + user.getUserName(), errorNumber);
+                asyncService.recordLogininfor(user.getUserName(), Constants.LOGIN_FAIL, MessageUtils.message("sms.code.retry.limit.count", errorNumber), request);
+                throw new UserException("sms.code.retry.limit.count", errorNumber);
+            }
+        }
+
+        // 鐧诲綍鎴愬姛 娓呯┖閿欒娆℃暟
+        RedisUtils.deleteObject(Constants.LOGIN_ERROR + user.getUserName());
+
+        // 姝ゅ鍙牴鎹櫥褰曠敤鎴风殑鏁版嵁涓嶅悓 鑷鍒涘缓 loginUser
+        LoginUser loginUser = buildLoginUser(user);
+        // 鐢熸垚token
+        LoginHelper.loginByDevice(loginUser, DeviceType.APP);
+
+        asyncService.recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request);
+        recordLoginInfo(user.getUserId(), user.getUserName());
+        return StpUtil.getTokenValue();
+    }
+
+
+    public String xcxLogin(String xcxCode) {
+        HttpServletRequest request = ServletUtils.getRequest();
+        // xcxCode 涓� 灏忕▼搴忚皟鐢� wx.login 鎺堟潈鍚庤幏鍙�
+        // todo 浠ヤ笅鑷瀹炵幇
+        // 鏍¢獙 appid + appsrcret + xcxCode 璋冪敤鐧诲綍鍑瘉鏍¢獙鎺ュ彛 鑾峰彇 session_key 涓� openid
+        String openid = "";
+        SysUser user = loadUserByOpenid(openid);
+
+        // 姝ゅ鍙牴鎹櫥褰曠敤鎴风殑鏁版嵁涓嶅悓 鑷鍒涘缓 loginUser
+        LoginUser loginUser = buildLoginUser(user);
+        // 鐢熸垚token
+        LoginHelper.loginByDevice(loginUser, DeviceType.XCX);
+
+        asyncService.recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request);
+        recordLoginInfo(user.getUserId(), user.getUserName());
+        return StpUtil.getTokenValue();
+    }
+
+
     public void logout(String loginName) {
         asyncService.recordLogininfor(loginName, Constants.LOGOUT, MessageUtils.message("user.logout.success"), ServletUtils.getRequest());
+    }
+
+    /**
+     * 鏍¢獙鐭俊楠岃瘉鐮�
+     */
+    private boolean validateSmsCode(String phonenumber, String smsCode) {
+        // todo 姝ゅ浣跨敤鎵嬫満鍙锋煡璇edis楠岃瘉鐮佷笌鍙傛暟楠岃瘉鐮佹槸鍚︿竴鑷� 鐢ㄦ埛鑷瀹炵幇
+        return true;
     }
 
     /**
@@ -136,6 +208,38 @@
         return user;
     }
 
+    private SysUser loadUserByPhonenumber(String phonenumber) {
+        SysUser user = userService.selectUserByPhonenumber(phonenumber);
+        if (ObjectUtil.isNull(user)) {
+            log.info("鐧诲綍鐢ㄦ埛锛歿} 涓嶅瓨鍦�.", phonenumber);
+            throw new UserException("user.not.exists", phonenumber);
+        } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
+            log.info("鐧诲綍鐢ㄦ埛锛歿} 宸茶鍒犻櫎.", phonenumber);
+            throw new UserException("user.password.delete", phonenumber);
+        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
+            log.info("鐧诲綍鐢ㄦ埛锛歿} 宸茶鍋滅敤.", phonenumber);
+            throw new UserException("user.blocked", phonenumber);
+        }
+        return user;
+    }
+
+    private SysUser loadUserByOpenid(String openid) {
+        // 浣跨敤 openid 鏌ヨ缁戝畾鐢ㄦ埛 濡傛湭缁戝畾鐢ㄦ埛 鍒欐牴鎹笟鍔¤嚜琛屽鐞� 渚嬪 鍒涘缓榛樿鐢ㄦ埛
+        // todo 鑷瀹炵幇 userService.selectUserByOpenid(openid);
+        SysUser user = new SysUser();
+        if (ObjectUtil.isNull(user)) {
+            log.info("鐧诲綍鐢ㄦ埛锛歿} 涓嶅瓨鍦�.", openid);
+            // todo 鐢ㄦ埛涓嶅瓨鍦� 涓氬姟閫昏緫鑷瀹炵幇
+        } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
+            log.info("鐧诲綍鐢ㄦ埛锛歿} 宸茶鍒犻櫎.", openid);
+            // todo 鐢ㄦ埛宸茶鍒犻櫎 涓氬姟閫昏緫鑷瀹炵幇
+        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
+            log.info("鐧诲綍鐢ㄦ埛锛歿} 宸茶鍋滅敤.", openid);
+            // todo 鐢ㄦ埛宸茶鍋滅敤 涓氬姟閫昏緫鑷瀹炵幇
+        }
+        return user;
+    }
+
     /**
      * 鏋勫缓鐧诲綍鐢ㄦ埛
      */
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
index 09c5ba2..11ff7b9 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
@@ -138,6 +138,17 @@
     }
 
     /**
+     * 閫氳繃鎵嬫満鍙锋煡璇㈢敤鎴�
+     *
+     * @param phonenumber 鎵嬫満鍙�
+     * @return 鐢ㄦ埛瀵硅薄淇℃伅
+     */
+    @Override
+    public SysUser selectUserByPhonenumber(String phonenumber) {
+        return baseMapper.selectUserByPhonenumber(phonenumber);
+    }
+
+    /**
      * 閫氳繃鐢ㄦ埛ID鏌ヨ鐢ㄦ埛
      *
      * @param userId 鐢ㄦ埛ID
diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
index 3117788..d4fd23f 100644
--- a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
@@ -121,6 +121,11 @@
         where u.del_flag = '0' and u.user_name = #{userName}
     </select>
 
+    <select id="selectUserByPhonenumber" parameterType="String" resultMap="SysUserResult">
+        <include refid="selectUserVo"/>
+        where u.del_flag = '0' and u.phonenumber = #{phonenumber}
+    </select>
+
     <select id="selectUserById" parameterType="Long" resultMap="SysUserResult">
         <include refid="selectUserVo"/>
         where u.del_flag = '0' and u.user_id = #{userId}

--
Gitblit v1.9.3