add 增加 短信登录 与 小程序登录 示例
This commit is contained in:
parent
0e4372b4a3
commit
4a353896e3
@ -7,6 +7,7 @@ import com.ruoyi.common.core.domain.R;
|
||||
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.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.List;
|
||||
import java.util.Map;
|
||||
@ -60,6 +62,38 @@ public class SysLoginController {
|
||||
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() {
|
||||
|
@ -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.phonenumber.not.blank=用户手机号不能为空
|
||||
user.mobile.phone.number.not.valid=手机号格式错误
|
||||
user.login.success=登录成功
|
||||
user.register.success=注册成功
|
||||
@ -38,3 +39,7 @@ no.export.permission=您没有导出数据的权限,请联系管理员添加
|
||||
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不能为空
|
||||
|
@ -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.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.export.permission=You do not have permission to export data,please contact
|
||||
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
|
||||
|
@ -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.phonenumber.not.blank=用户手机号不能为空
|
||||
user.mobile.phone.number.not.valid=手机号格式错误
|
||||
user.login.success=登录成功
|
||||
user.register.success=注册成功
|
||||
@ -38,3 +39,7 @@ no.export.permission=您没有导出数据的权限,请联系管理员添加
|
||||
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不能为空
|
||||
|
@ -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;
|
||||
|
||||
}
|
@ -21,7 +21,12 @@ public enum DeviceType {
|
||||
/**
|
||||
* app端
|
||||
*/
|
||||
APP("app");
|
||||
APP("app"),
|
||||
|
||||
/**
|
||||
* 小程序端
|
||||
*/
|
||||
XCX("xcx");
|
||||
|
||||
private final String device;
|
||||
}
|
||||
|
@ -68,6 +68,14 @@ public interface SysUserMapper extends BaseMapperPlus<SysUserMapper, SysUser, Sy
|
||||
*/
|
||||
SysUser selectUserByUserName(String userName);
|
||||
|
||||
/**
|
||||
* 通过手机号查询用户
|
||||
*
|
||||
* @param phonenumber 手机号
|
||||
* @return 用户对象信息
|
||||
*/
|
||||
SysUser selectUserByPhonenumber(String phonenumber);
|
||||
|
||||
/**
|
||||
* 通过用户ID查询用户
|
||||
*
|
||||
|
@ -48,6 +48,14 @@ public interface ISysUserService {
|
||||
*/
|
||||
SysUser selectUserByUserName(String userName);
|
||||
|
||||
/**
|
||||
* 通过手机号查询用户
|
||||
*
|
||||
* @param phonenumber 手机号
|
||||
* @return 用户对象信息
|
||||
*/
|
||||
SysUser selectUserByPhonenumber(String phonenumber);
|
||||
|
||||
/**
|
||||
* 通过用户ID查询用户
|
||||
*
|
||||
|
@ -87,6 +87,8 @@ public class SysLoginService {
|
||||
|
||||
// 登录成功 清空错误次数
|
||||
RedisUtils.deleteObject(Constants.LOGIN_ERROR + username);
|
||||
|
||||
// 此处可根据登录用户的数据不同 自行创建 loginUser
|
||||
LoginUser loginUser = buildLoginUser(user);
|
||||
// 生成token
|
||||
LoginHelper.loginByDevice(loginUser, DeviceType.PC);
|
||||
@ -96,10 +98,80 @@ public class SysLoginService {
|
||||
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 @@ public class SysLoginService {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建登录用户
|
||||
*/
|
||||
|
@ -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查询用户
|
||||
*
|
||||
|
@ -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}
|
||||
|
Loading…
x
Reference in New Issue
Block a user