add 增加 短信登录 与 小程序登录 示例

2.X
疯狂的狮子Li 3 years ago
parent 641a15158d
commit bb479c436a

@ -19,6 +19,22 @@ public interface RemoteUserService {
*/
LoginUser getUserInfo(String username) throws UserException;
/**
*
*
* @param phonenumber
* @return
*/
LoginUser getUserInfoByPhonenumber(String phonenumber) throws UserException;
/**
* openid
*
* @param openid openid
* @return
*/
LoginUser getUserInfoByOpenid(String openid) throws UserException;
/**
*
*

@ -2,12 +2,13 @@ package com.ruoyi.auth.controller;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.nacos.api.common.Constants;
import com.ruoyi.auth.form.LoginBody;
import com.ruoyi.auth.form.RegisterBody;
import com.ruoyi.auth.form.SmsLoginBody;
import com.ruoyi.auth.service.SysLoginService;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.satoken.utils.LoginHelper;
import com.ruoyi.system.api.model.LoginUser;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
@ -17,6 +18,7 @@ import org.springframework.web.bind.annotation.PostMapping;
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.Map;
@ -45,6 +47,38 @@ public class TokenController {
return R.ok(rspMap);
}
/**
* ()
*
* @param smsLoginBody
* @return
*/
@ApiOperation("短信登录(示例)")
@PostMapping("/smsLogin")
public R<Map<String, Object>> smsLogin(@Validated @RequestBody SmsLoginBody smsLoginBody) {
Map<String, Object> ajax = new HashMap<>();
// 生成令牌
String token = sysLoginService.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 = sysLoginService.xcxLogin(xcxCode);
ajax.put(Constants.TOKEN, token);
return R.ok(ajax);
}
@ApiOperation("登出方法")
@DeleteMapping("logout")
public R<Void> logout() {

@ -0,0 +1,33 @@
package com.ruoyi.auth.form;
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;
}

@ -75,6 +75,57 @@ public class SysLoginService {
return StpUtil.getTokenValue();
}
public String smsLogin(String phonenumber, String smsCode) {
// 通过手机号查找用户
LoginUser userInfo = remoteUserService.getUserInfoByPhonenumber(phonenumber);
// 获取用户登录错误次数(可自定义限制策略 例如: key + username + ip)
Integer errorNumber = RedisUtils.getCacheObject(CacheConstants.LOGIN_ERROR + userInfo.getUsername());
// 锁定时间内登录 则踢出
if (ObjectUtil.isNotNull(errorNumber) && errorNumber.equals(CacheConstants.LOGIN_ERROR_NUMBER)) {
recordLogininfor(userInfo.getUsername(), Constants.LOGIN_FAIL, MessageUtils.message("sms.code.retry.limit.exceed", CacheConstants.LOGIN_ERROR_LIMIT_TIME));
throw new UserException("sms.code.retry.limit.exceed", CacheConstants.LOGIN_ERROR_LIMIT_TIME);
}
if (!validateSmsCode(phonenumber, smsCode)) {
// 是否第一次
errorNumber = ObjectUtil.isNull(errorNumber) ? 1 : errorNumber + 1;
// 达到规定错误次数 则锁定登录
if (errorNumber.equals(CacheConstants.LOGIN_ERROR_NUMBER)) {
RedisUtils.setCacheObject(CacheConstants.LOGIN_ERROR + userInfo.getUsername(), errorNumber, CacheConstants.LOGIN_ERROR_LIMIT_TIME, TimeUnit.MINUTES);
recordLogininfor(userInfo.getUsername(), Constants.LOGIN_FAIL, MessageUtils.message("sms.code.retry.limit.exceed", CacheConstants.LOGIN_ERROR_LIMIT_TIME));
throw new UserException("sms.code.retry.limit.exceed", CacheConstants.LOGIN_ERROR_LIMIT_TIME);
} else {
// 未达到规定错误次数 则递增
RedisUtils.setCacheObject(CacheConstants.LOGIN_ERROR + userInfo.getUsername(), errorNumber);
recordLogininfor(userInfo.getUsername(), Constants.LOGIN_FAIL, MessageUtils.message("sms.code.retry.limit.count", errorNumber));
throw new UserException("sms.code.retry.limit.count", errorNumber);
}
}
// 登录成功 清空错误次数
RedisUtils.deleteObject(CacheConstants.LOGIN_ERROR + userInfo.getUsername());
// 生成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
String openid = "";
LoginUser userInfo = remoteUserService.getUserInfoByOpenid(openid);
// 生成token
LoginHelper.loginByDevice(userInfo, DeviceType.XCX);
recordLogininfor(userInfo.getUsername(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
return StpUtil.getTokenValue();
}
public void logout(String loginName) {
recordLogininfor(loginName, Constants.LOGOUT, MessageUtils.message("user.logout.success"));
}
@ -122,4 +173,12 @@ public class SysLoginService {
}
remoteLogService.saveLogininfor(logininfor);
}
/**
*
*/
private boolean validateSmsCode(String phonenumber, String smsCode) {
// todo 此处使用手机号查询redis验证码与参数验证码是否一致 用户自行实现
return true;
}
}

@ -21,7 +21,12 @@ public enum DeviceType {
/**
* app
*/
APP("app");
APP("app"),
/**
*
*/
XCX("xcx");
private final String device;
}

@ -19,6 +19,7 @@ user.password.not.blank=\u7528\u6237\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A
user.password.length.valid=\u7528\u6237\u5BC6\u7801\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4
user.password.not.valid=* 5-50\u4E2A\u5B57\u7B26
user.email.not.valid=\u90AE\u7BB1\u683C\u5F0F\u9519\u8BEF
user.phonenumber.not.blank=\u7528\u6237\u624B\u673A\u53F7\u4E0D\u80FD\u4E3A\u7A7A
user.mobile.phone.number.not.valid=\u624B\u673A\u53F7\u683C\u5F0F\u9519\u8BEF
user.login.success=\u767B\u5F55\u6210\u529F
user.register.success=\u6CE8\u518C\u6210\u529F
@ -38,3 +39,7 @@ no.delete.permission=\u60A8\u6CA1\u6709\u5220\u9664\u6570\u636E\u7684\u6743\u965
no.export.permission=\u60A8\u6CA1\u6709\u5BFC\u51FA\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
no.view.permission=\u60A8\u6CA1\u6709\u67E5\u770B\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
repeat.submit.message=\u4E0D\u5141\u8BB8\u91CD\u590D\u63D0\u4EA4\uFF0C\u8BF7\u7A0D\u5019\u518D\u8BD5
sms.code.not.blank=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A
sms.code.retry.limit.count=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21
sms.code.retry.limit.exceed=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u9519\u8BEF\u6B21\u6570\u8FC7\u591A\uFF0C\u5E10\u6237\u9501\u5B9A{0}\u5206\u949F
xcx.code.not.blank=\u5C0F\u7A0B\u5E8Fcode\u4E0D\u80FD\u4E3A\u7A7A

@ -19,6 +19,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.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.delete.permission=You do not have permission to delete data\uFF0Cplease conta
no.export.permission=You do not have permission to export data\uFF0Cplease contact your administrator to add permissions [{0}]
no.view.permission=You do not have permission to view data\uFF0Cplease contact your administrator to add permissions [{0}]
repeat.submit.message=Repeat submit is not allowed, 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

@ -19,6 +19,7 @@ user.password.not.blank=\u7528\u6237\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A
user.password.length.valid=\u7528\u6237\u5BC6\u7801\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4
user.password.not.valid=* 5-50\u4E2A\u5B57\u7B26
user.email.not.valid=\u90AE\u7BB1\u683C\u5F0F\u9519\u8BEF
user.phonenumber.not.blank=\u7528\u6237\u624B\u673A\u53F7\u4E0D\u80FD\u4E3A\u7A7A
user.mobile.phone.number.not.valid=\u624B\u673A\u53F7\u683C\u5F0F\u9519\u8BEF
user.login.success=\u767B\u5F55\u6210\u529F
user.register.success=\u6CE8\u518C\u6210\u529F
@ -38,3 +39,7 @@ no.delete.permission=\u60A8\u6CA1\u6709\u5220\u9664\u6570\u636E\u7684\u6743\u965
no.export.permission=\u60A8\u6CA1\u6709\u5BFC\u51FA\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
no.view.permission=\u60A8\u6CA1\u6709\u67E5\u770B\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
repeat.submit.message=\u4E0D\u5141\u8BB8\u91CD\u590D\u63D0\u4EA4\uFF0C\u8BF7\u7A0D\u5019\u518D\u8BD5
sms.code.not.blank=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A
sms.code.retry.limit.count=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21
sms.code.retry.limit.exceed=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u9519\u8BEF\u6B21\u6570\u8FC7\u591A\uFF0C\u5E10\u6237\u9501\u5B9A{0}\u5206\u949F
xcx.code.not.blank=\u5C0F\u7A0B\u5E8Fcode\u4E0D\u80FD\u4E3A\u7A7A

@ -18,7 +18,6 @@ import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Set;
/**
*
@ -46,22 +45,41 @@ public class RemoteUserServiceImpl implements RemoteUserService {
if (UserStatus.DISABLE.getCode().equals(sysUser.getStatus())) {
throw new UserException("user.blocked", username);
}
// 角色集合
Set<String> rolePermission = permissionService.getRolePermission(sysUser.getUserId());
// 权限集合
Set<String> menuPermissions = permissionService.getMenuPermission(sysUser.getUserId());
LoginUser loginUser = new LoginUser();
loginUser.setUserId(sysUser.getUserId());
loginUser.setDeptId(sysUser.getDeptId());
loginUser.setUsername(sysUser.getUserName());
loginUser.setPassword(sysUser.getPassword());
loginUser.setUserType(sysUser.getUserType());
loginUser.setDeptName(sysUser.getDept().getDeptName());
loginUser.setMenuPermission(menuPermissions);
loginUser.setRolePermission(rolePermission);
List<RoleDTO> roles = BeanUtil.copyToList(sysUser.getRoles(), RoleDTO.class);
loginUser.setRoles(roles);
return loginUser;
// 此处可根据登录用户的数据不同 自行创建 loginUser
return buildLoginUser(sysUser);
}
@Override
public LoginUser getUserInfoByPhonenumber(String phonenumber) throws UserException {
SysUser sysUser = userService.selectUserByPhonenumber(phonenumber);
if (ObjectUtil.isNull(sysUser)) {
throw new UserException("user.not.exists", phonenumber);
}
if (UserStatus.DELETED.getCode().equals(sysUser.getDelFlag())) {
throw new UserException("user.password.delete", phonenumber);
}
if (UserStatus.DISABLE.getCode().equals(sysUser.getStatus())) {
throw new UserException("user.blocked", phonenumber);
}
// 此处可根据登录用户的数据不同 自行创建 loginUser
return buildLoginUser(sysUser);
}
@Override
public LoginUser getUserInfoByOpenid(String openid) throws UserException {
// todo 自行实现 userService.selectUserByOpenid(openid);
SysUser sysUser = new SysUser();
if (ObjectUtil.isNull(sysUser)) {
// todo 用户不存在 业务逻辑自行实现
}
if (UserStatus.DELETED.getCode().equals(sysUser.getDelFlag())) {
// todo 用户已被删除 业务逻辑自行实现
}
if (UserStatus.DISABLE.getCode().equals(sysUser.getStatus())) {
// todo 用户已被停用 业务逻辑自行实现
}
// 此处可根据登录用户的数据不同 自行创建 loginUser
return buildLoginUser(sysUser);
}
@Override
@ -76,4 +94,21 @@ public class RemoteUserServiceImpl implements RemoteUserService {
return userService.registerUser(sysUser);
}
/**
*
*/
private LoginUser buildLoginUser(SysUser user) {
LoginUser loginUser = new LoginUser();
loginUser.setUserId(user.getUserId());
loginUser.setDeptId(user.getDeptId());
loginUser.setUsername(user.getUserName());
loginUser.setUserType(user.getUserType());
loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId()));
loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId()));
loginUser.setDeptName(user.getDept().getDeptName());
List<RoleDTO> roles = BeanUtil.copyToList(user.getRoles(), RoleDTO.class);
loginUser.setRoles(roles);
return loginUser;
}
}

@ -69,6 +69,14 @@ public interface SysUserMapper extends BaseMapperPlus<SysUserMapper, SysUser, Sy
*/
SysUser selectUserByUserName(String userName);
/**
*
*
* @param phonenumber
* @return
*/
SysUser selectUserByPhonenumber(String phonenumber);
/**
* ID
*

@ -46,6 +46,14 @@ public interface ISysUserService {
*/
SysUser selectUserByUserName(String userName);
/**
*
*
* @param phonenumber
* @return
*/
SysUser selectUserByPhonenumber(String phonenumber);
/**
* ID
*

@ -137,6 +137,17 @@ public class SysUserServiceImpl implements ISysUserService {
return baseMapper.selectUserByUserName(userName);
}
/**
*
*
* @param phonenumber
* @return
*/
@Override
public SysUser selectUserByPhonenumber(String phonenumber) {
return baseMapper.selectUserByPhonenumber(phonenumber);
}
/**
* ID
*

@ -116,12 +116,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<select id="selectUserByUserName" parameterType="String" resultMap="SysUserResult">
<include refid="selectUserVo"/>
where u.user_name = #{userName}
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.user_id = #{userId}
where u.del_flag = '0' and u.user_id = #{userId}
</select>
</mapper>

Loading…
Cancel
Save