疯狂的狮子Li
2022-03-23 4a353896e3ab74e6bd9acda6bebc2fde167f2d00
add 增加 短信登录 与 小程序登录 示例
已修改10个文件
已添加1个文件
225 ■■■■■ 文件已修改
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/i18n/messages.properties 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/i18n/messages_en_US.properties 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/enums/DeviceType.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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 å°ç¨‹åºcode
     * @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() {
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=小程序code不能为空
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,please 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
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=小程序code不能为空
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;
}
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;
}
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
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
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 æ­¤å¤„使用手机号查询redis验证码与参数验证码是否一致 ç”¨æˆ·è‡ªè¡Œå®žçް
        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;
    }
    /**
     * æž„建登录用户
     */
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
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}