注册登录

pull/1121/head
梁宇奇 2025-06-05 17:09:58 +08:00
parent 457a99e833
commit 4cc903cba0
20 changed files with 661 additions and 142 deletions

View File

@ -31,7 +31,7 @@ import java.util.stream.Collectors;
*/
@Api(tags = "登录")
@RestController
@RequestMapping("/rest/v1/logon")
@RequestMapping("/rest/v1/login")
public class SysLoginController {
@Autowired
@ -55,15 +55,43 @@ public class SysLoginController {
*/
@ApiOperation(value = "用户名密码登录")
@PostMapping("/loginByUname")
public AjaxResult login(@RequestBody LoginBody loginBody) {
public AjaxResult login(@Validated @RequestBody LoginByUsernameVO loginBody) {
AjaxResult ajax = AjaxResult.success();
// 生成令牌
String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
loginBody.getUuid());
LoginCredential credential = LoginCredential.builder()
.loginType(ELoginType.USERNAME)
.username(loginBody.getUsername())
.password(loginBody.getPassword())
.imgUuid(loginBody.getUuid())
.imgVerificationCode(loginBody.getCode())
.build();
String token = loginService.login(credential);
ajax.put(Constants.TOKEN, token);
return ajax;
}
@ApiOperation(value = "短信验证码登录")
@PostMapping("/loginBySms")
public AjaxResult loginBySms(@Validated @RequestBody LoginBySmsCodeVO loginBody) {
AjaxResult ajax = AjaxResult.success();
// 生成令牌
LoginCredential credential = LoginCredential.builder()
.loginType(ELoginType.SMS_VERIFICATION_CODE)
.phoneNumber(loginBody.getPhoneNumber())
.smsVerificationCode(loginBody.getCode())
.build();
String token = loginService.login(credential);
ajax.put(Constants.TOKEN, token);
return ajax;
}
@ApiOperation(value = "发送登录短信验证码")
@PostMapping("/sendSmsVerificationCode")
public R sendSmsVerificationCode(@Validated @RequestBody LoginSmsReqVO vo) {
loginService.sendSmsVerificationCode(vo.getPhoneNumber(), vo.getCode(), vo.getUuid());
return R.ok();
}
/**
*
*

View File

@ -1,13 +1,20 @@
package com.ruoyi.web.controller.system;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.model.ESystemRole;
import com.ruoyi.common.core.domain.model.RegisterBody;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.service.SysRegisterService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.web.controller.system.vo.LoginSmsReqVO;
import com.ruoyi.web.controller.system.vo.RegisterBySmsCodeVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@ -28,6 +35,33 @@ public class SysRegisterController extends BaseController {
@Autowired
private ISysConfigService configService;
@ApiOperation(value = "档口供应商注册")
@PostMapping("/registerStore")
public AjaxResult registerStore(@Validated @RequestBody RegisterBySmsCodeVO vo) {
AjaxResult ajax = AjaxResult.success();
String token = registerService.registerByPhoneNumber(vo.getPhoneNumber(), vo.getPassword(), vo.getCode(),
ESystemRole.SUPPLIER);
ajax.put(Constants.TOKEN, token);
return ajax;
}
@ApiOperation(value = "卖家注册")
@PostMapping("/registerSeller")
public AjaxResult registerSeller(@Validated @RequestBody RegisterBySmsCodeVO vo) {
AjaxResult ajax = AjaxResult.success();
String token = registerService.registerByPhoneNumber(vo.getPhoneNumber(), vo.getPassword(), vo.getCode(),
ESystemRole.SELLER);
ajax.put(Constants.TOKEN, token);
return ajax;
}
@ApiOperation(value = "发送登录短信验证码")
@PostMapping("/sendSmsVerificationCode")
public R sendSmsVerificationCode(@Validated @RequestBody LoginSmsReqVO vo) {
registerService.sendSmsVerificationCode(vo.getPhoneNumber(), vo.getCode(), vo.getUuid());
return R.ok();
}
@PostMapping("/register")
public AjaxResult register(@RequestBody RegisterBody user) {
if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser")))) {

View File

@ -0,0 +1,25 @@
package com.ruoyi.web.controller.system.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
/**
* @author liangyq
* @date 2025-06-05 15:41
*/
@ApiModel
@Data
public class LoginBySmsCodeVO {
@NotEmpty(message = "手机号不能为空")
@ApiModelProperty("手机号")
private String phoneNumber;
@NotEmpty(message = "验证码不能为空")
@ApiModelProperty("验证码")
private String code;
}

View File

@ -0,0 +1,41 @@
package com.ruoyi.web.controller.system.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
/**
* @author liangyq
* @date 2025-06-05 15:41
*/
@ApiModel
@Data
public class LoginByUsernameVO {
/**
*
*/
@NotEmpty(message = "用户名不能为空")
@ApiModelProperty("用户名")
private String username;
/**
*
*/
@NotEmpty(message = "用户密码不能为空")
@ApiModelProperty("用户密码")
private String password;
/**
*
*/
@ApiModelProperty("验证码")
private String code;
/**
*
*/
@ApiModelProperty("唯一标识")
private String uuid;
}

View File

@ -0,0 +1,28 @@
package com.ruoyi.web.controller.system.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
/**
* @author liangyq
* @date 2025-06-05 15:41
*/
@ApiModel
@Data
public class LoginSmsReqVO {
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
@NotEmpty(message = "手机号不能为空")
@ApiModelProperty("手机号")
private String phoneNumber;
@ApiModelProperty("验证码")
private String code;
@ApiModelProperty("唯一标识")
private String uuid;
}

View File

@ -0,0 +1,28 @@
package com.ruoyi.web.controller.system.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
/**
* @author liangyq
* @date 2025-06-04 15:41
*/
@ApiModel
@Data
public class RegisterBySmsCodeVO {
@NotEmpty(message = "手机号不能为空")
@ApiModelProperty("手机号")
private String phoneNumber;
@NotEmpty(message = "短信验证码不能为空")
@ApiModelProperty("短信验证码")
private String code;
@ApiModelProperty("用户密码")
private String password;
}

View File

@ -37,6 +37,9 @@ sms:
accessKeySecret: gVpsVKSds5keQeN0mDDVKmY8YTGOkk
regionId:
send: true
verificationCode:
signName: 成都一方鹏商贸
templateCode: SMS_318925388
es:
#多个用","分割
hosts: 49.234.41.39:9222

View File

@ -22,6 +22,11 @@ public class CacheConstants {
*/
public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
/**
* / redis key
*/
public static final String SMS_LOGIN_CAPTCHA_CODE_KEY = "sms_login_captcha_codes:";
/**
* cache key
*/

View File

@ -0,0 +1,29 @@
package com.ruoyi.common.core.domain.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author liangyq
* @date 2025-05-27
*/
@Getter
@AllArgsConstructor
public enum ELoginType {
USERNAME(1, "用户名登录"),
SMS_VERIFICATION_CODE(2, "短信验证码登录");
private final Integer value;
private final String label;
public static ELoginType of(Integer value) {
for (ELoginType e : ELoginType.values()) {
if (e.getValue().equals(value)) {
return e;
}
}
return null;
}
}

View File

@ -0,0 +1,32 @@
package com.ruoyi.common.core.domain.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author liangyq
* @date 2025-06-05 11:16
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class LoginCredential {
private ELoginType loginType;
private String username;
private String password;
private String phoneNumber;
private String smsVerificationCode;
private String imgUuid;
private String imgVerificationCode;
}

View File

@ -111,7 +111,9 @@ public class SecurityConfig
.authorizeHttpRequests((requests) -> {
permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll());
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
requests.antMatchers("/rest/v1/logon/loginByUname", "/rest/v1/reg/register", "/rest/v1/captcha/image").permitAll()
requests.antMatchers("/rest/v1/login/loginByUname", "/rest/v1/login/loginBySms",
"/rest/v1/login/sendSmsVerificationCode", "/rest/v1/reg/registerStore", "/rest/v1/reg/registerSeller",
"/rest/v1/reg/sendSmsVerificationCode", "/rest/v1/reg/register", "/rest/v1/captcha/image").permitAll()
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()

View File

@ -17,6 +17,12 @@ public class SmsClientWrapper {
@Value("${sms.send:true}")
private Boolean doSend;
@Value("${sms.verificationCode.signName:}")
private String verificationCodeSignName;
@Value("${sms.verificationCode.templateCode:}")
private String verificationCodeTemplateCode;
public boolean sendSms(String signName, String phoneNumber, String templateCode, String templateParams) {
boolean sendResult;
if (doSend) {
@ -31,4 +37,9 @@ public class SmsClientWrapper {
return sendResult;
}
public boolean sendVerificationCode(String phoneNumber, String code) {
return sendSms(verificationCodeSignName, phoneNumber, verificationCodeTemplateCode,
"{\"code\":\"" + code + "\"}");
}
}

View File

@ -1,10 +1,15 @@
package com.ruoyi.framework.web.service;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.model.LoginCredential;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.domain.model.UserInfo;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.exception.user.*;
import com.ruoyi.common.utils.DateUtils;
@ -13,26 +18,28 @@ import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.framework.security.context.AuthenticationContextHolder;
import com.ruoyi.framework.sms.SmsClientWrapper;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
*
*
*
* @author ruoyi
*/
@Slf4j
@Component
public class SysLoginService
{
public class SysLoginService {
@Autowired
private TokenService tokenService;
@ -41,87 +48,150 @@ public class SysLoginService
@Autowired
private RedisCache redisCache;
@Autowired
private ISysUserService userService;
@Autowired
private ISysConfigService configService;
@Autowired
private SysPasswordService passwordService;
@Autowired
private SmsClientWrapper smsClient;
/**
*
*
* @param username
* @param password
* @param code
* @param uuid
* @return
*
* @param loginCredential
* @return
*/
public String login(String username, String password, String code, String uuid)
{
public String login(LoginCredential loginCredential) {
LoginUser loginUser;
switch (loginCredential.getLoginType()) {
case USERNAME:
loginUser = loginByUsername(loginCredential.getUsername(), loginCredential.getPassword(),
loginCredential.getImgVerificationCode(), loginCredential.getImgUuid());
break;
case SMS_VERIFICATION_CODE:
loginUser = loginBySmsVerificationCode(loginCredential.getPhoneNumber(),
loginCredential.getSmsVerificationCode());
break;
default:
throw new ServiceException("未知登录类型");
}
return createToken(loginUser);
}
// 验证码校验
validateCaptcha(username, code, uuid);
/**
* token
*
* @param loginUser
* @return
*/
public String createToken(LoginUser loginUser) {
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
// 登录前置校验
loginPreCheck(username, password);
// 用户验证
Authentication authentication = null;
try
{
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
AuthenticationContextHolder.setContext(authenticationToken);
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
authentication = authenticationManager.authenticate(authenticationToken);
}
catch (Exception e)
{
if (e instanceof BadCredentialsException)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
}
else
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
throw new ServiceException(e.getMessage());
}
}
finally
{
AuthenticationContextHolder.clearContext();
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginUser.getUsername(), Constants.LOGIN_SUCCESS,
MessageUtils.message("user.login.success")));
recordLoginInfo(loginUser.getUserId());
// 生成token
return tokenService.createToken(loginUser);
}
/**
*
*
*
*
* @param phoneNumber
* @param smsCode
* @return
*/
private LoginUser loginBySmsVerificationCode(String phoneNumber, String smsCode) {
// 验证码校验
validateSmsVerificationCode(phoneNumber, smsCode);
// 用户验证
UserInfo user = userService.getUserByPhoneNumber(phoneNumber);
loginUserInfoCheck(phoneNumber, user);
return new LoginUser(user);
}
/**
*
*
* @param username
* @param code
* @param uuid
* @param password
* @param code
* @param uuid
* @return
*/
public void validateCaptcha(String username, String code, String uuid)
{
private LoginUser loginByUsername(String username, String password, String code, String uuid) {
// 验证码校验
validateCaptcha(username, code, uuid);
// 登录前置校验
loginPreCheck(username, password);
// 用户验证
UserInfo user = userService.getUserByUsername(username);
loginUserInfoCheck(username, user);
// 密码验证
try {
passwordService.validate(user, password);
} catch (Exception e) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
throw new ServiceException(e.getMessage());
}
return new LoginUser(user);
}
private void loginUserInfoCheck(String username, UserInfo user) {
if (StringUtils.isNull(user)) {
log.info("登录用户:{} 不存在.", username);
String msg = MessageUtils.message("user.not.exists");
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, msg));
throw new ServiceException(msg);
} else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
log.info("登录用户:{} 已被删除.", username);
String msg = MessageUtils.message("user.password.delete");
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, msg));
throw new ServiceException(msg);
} else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
log.info("登录用户:{} 已被停用.", username);
String msg = MessageUtils.message("user.blocked");
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, msg));
throw new ServiceException(msg);
}
}
/**
*
*
* @param username
* @param code
* @param uuid
* @return
*/
public void validateCaptcha(String username, String code, String uuid) {
boolean captchaEnabled = configService.selectCaptchaEnabled();
if (captchaEnabled)
{
if (captchaEnabled) {
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
String captcha = redisCache.getCacheObject(verifyKey);
if (captcha == null)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
if (captcha == null) {
if (username != null) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
}
throw new CaptchaExpireException();
}
redisCache.deleteObject(verifyKey);
if (!code.equalsIgnoreCase(captcha))
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
if (!code.equalsIgnoreCase(captcha)) {
if (username != null) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
}
throw new CaptchaException();
}
}
@ -129,35 +199,31 @@ public class SysLoginService
/**
*
*
* @param username
* @param password
*/
public void loginPreCheck(String username, String password)
{
public void loginPreCheck(String username, String password) {
// 用户名或密码为空 错误
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password))
{
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
throw new UserNotExistsException();
}
// 密码如果不在指定范围内 错误
if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH)
{
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
}
// 用户名不在指定范围内 错误
if (username.length() < UserConstants.USERNAME_MIN_LENGTH
|| username.length() > UserConstants.USERNAME_MAX_LENGTH)
{
|| username.length() > UserConstants.USERNAME_MAX_LENGTH) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
}
// IP黑名单校验
String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
{
if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));
throw new BlackListException();
}
@ -168,8 +234,52 @@ public class SysLoginService
*
* @param userId ID
*/
public void recordLoginInfo(Long userId)
{
public void recordLoginInfo(Long userId) {
userService.updateLoginInfo(userId, IpUtils.getIpAddr(), DateUtils.getNowDate());
}
/**
* /
*
* @param phoneNumber
* @param code code
* @param uuid uuid
*/
public void sendSmsVerificationCode(String phoneNumber, String code, String uuid) {
validateCaptcha(null, code, uuid);
sendSmsVerificationCode(phoneNumber);
}
/**
* /
*
* @param phoneNumber
*/
public void sendSmsVerificationCode(String phoneNumber) {
String code = RandomUtil.randomNumbers(6);
boolean success = smsClient.sendVerificationCode(phoneNumber, code);
if (success) {
String rk = CacheConstants.SMS_LOGIN_CAPTCHA_CODE_KEY + phoneNumber;
redisCache.setCacheObject(rk, code, 5, TimeUnit.MINUTES);
}
}
/**
* /
*
* @param phoneNumber
* @param code
* @return
*/
public void validateSmsVerificationCode(String phoneNumber, String code) {
String rk = CacheConstants.SMS_LOGIN_CAPTCHA_CODE_KEY + phoneNumber;
String cacheCode = redisCache.getCacheObject(rk);
if (cacheCode == null) {
throw new CaptchaExpireException();
}
redisCache.deleteObject(rk);
if (!StrUtil.equals(cacheCode, code)) {
throw new CaptchaException();
}
}
}

View File

@ -1,18 +1,16 @@
package com.ruoyi.framework.web.service;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.security.context.AuthenticationContextHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
*
@ -42,11 +40,9 @@ public class SysPasswordService
return CacheConstants.PWD_ERR_CNT_KEY + username;
}
public void validate(SysUser user)
public void validate(SysUser user, String password)
{
Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext();
String username = usernamePasswordAuthenticationToken.getName();
String password = usernamePasswordAuthenticationToken.getCredentials().toString();
String username = user.getUserName();
Integer retryCount = redisCache.getCacheObject(getCacheKey(username));

View File

@ -1,14 +1,11 @@
package com.ruoyi.framework.web.service;
import com.ruoyi.common.core.domain.model.UserInfoEdit;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.RegisterBody;
import com.ruoyi.common.core.domain.model.*;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.user.CaptchaException;
import com.ruoyi.common.exception.user.CaptchaExpireException;
@ -17,18 +14,33 @@ import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.framework.sms.SmsClientWrapper;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
*
*
*
* @author ruoyi
*/
@Slf4j
@Component
public class SysRegisterService
{
public class SysRegisterService {
@Value("${sms.verificationCode.signName:}")
private String verificationCodeSignName;
@Value("${sms.verificationCode.templateCode:}")
private String verificationCodeTemplateCode;
@Autowired
private ISysUserService userService;
@ -38,61 +50,81 @@ public class SysRegisterService
@Autowired
private RedisCache redisCache;
@Autowired
private SmsClientWrapper smsClient;
@Autowired
private SysLoginService loginService;
/**
*
*
* @param phoneNumber
* @param password
* @param smsVerificationCode
* @param roles
* @return
*/
public String registerByPhoneNumber(String phoneNumber, String password, String smsVerificationCode,
ESystemRole... roles) {
// 短信验证
validateSmsVerificationCode(phoneNumber, smsVerificationCode);
UserInfoEdit userEdit = new UserInfoEdit();
userEdit.setUserName(phoneNumber);
userEdit.setNickName(phoneNumber);
userEdit.setPhonenumber(phoneNumber);
userEdit.setPassword(password);
if (roles != null) {
userEdit.setRoleIds(Arrays.stream(roles).map(ESystemRole::getId).collect(Collectors.toList()));
}
//创建账号
Long userId = userService.createUser(userEdit);
//登录
UserInfo userInfo = userService.getUserById(userId);
LoginUser loginUser = new LoginUser(userInfo);
// 生成token
return loginService.createToken(loginUser);
}
/**
*
*/
public String register(RegisterBody registerBody)
{
public String register(RegisterBody registerBody) {
String msg = "", username = registerBody.getUsername(), password = registerBody.getPassword();
UserInfoEdit sysUser = new UserInfoEdit();
sysUser.setUserName(username);
// 验证码开关
boolean captchaEnabled = configService.selectCaptchaEnabled();
if (captchaEnabled)
{
if (captchaEnabled) {
validateCaptcha(username, registerBody.getCode(), registerBody.getUuid());
}
if (StringUtils.isEmpty(username))
{
if (StringUtils.isEmpty(username)) {
msg = "用户名不能为空";
}
else if (StringUtils.isEmpty(password))
{
} else if (StringUtils.isEmpty(password)) {
msg = "用户密码不能为空";
}
else if (username.length() < UserConstants.USERNAME_MIN_LENGTH
|| username.length() > UserConstants.USERNAME_MAX_LENGTH)
{
} else if (username.length() < UserConstants.USERNAME_MIN_LENGTH
|| username.length() > UserConstants.USERNAME_MAX_LENGTH) {
msg = "账户长度必须在2到20个字符之间";
}
else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH)
{
} else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH) {
msg = "密码长度必须在5到20个字符之间";
}
else if (!userService.checkUserNameUnique(sysUser))
{
} else if (!userService.checkUserNameUnique(sysUser)) {
msg = "保存用户'" + username + "'失败,注册账号已存在";
}
else
{
} else {
sysUser.setNickName(username);
sysUser.setPassword(SecurityUtils.encryptPassword(password));
boolean regFlag = false;
try {
//TODO USER
userService.createUser(sysUser);
}catch (Exception e){
log.error("用户注册失败",e);
} catch (Exception e) {
log.error("用户注册失败", e);
}
if (!regFlag)
{
if (!regFlag) {
msg = "注册失败,请联系系统管理人员";
}
else
{
} else {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success")));
}
}
@ -101,23 +133,65 @@ public class SysRegisterService
/**
*
*
*
* @param username
* @param code
* @param uuid
* @param code
* @param uuid
* @return
*/
public void validateCaptcha(String username, String code, String uuid)
{
public void validateCaptcha(String username, String code, String uuid) {
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
String captcha = redisCache.getCacheObject(verifyKey);
redisCache.deleteObject(verifyKey);
if (captcha == null)
{
if (captcha == null) {
throw new CaptchaExpireException();
}
if (!code.equalsIgnoreCase(captcha))
{
if (!code.equalsIgnoreCase(captcha)) {
throw new CaptchaException();
}
}
/**
* /
*
* @param phoneNumber
* @param code code
* @param uuid uuid
*/
public void sendSmsVerificationCode(String phoneNumber, String code, String uuid) {
validateCaptcha(null, code, uuid);
sendSmsVerificationCode(phoneNumber);
}
/**
* /
*
* @param phoneNumber
*/
public void sendSmsVerificationCode(String phoneNumber) {
String code = RandomUtil.randomNumbers(6);
boolean success = smsClient.sendVerificationCode(phoneNumber, code);
if (success) {
String rk = CacheConstants.SMS_LOGIN_CAPTCHA_CODE_KEY + phoneNumber;
redisCache.setCacheObject(rk, code, 5, TimeUnit.MINUTES);
}
}
/**
* /
*
* @param phoneNumber
* @param code
* @return
*/
public void validateSmsVerificationCode(String phoneNumber, String code) {
String rk = CacheConstants.SMS_LOGIN_CAPTCHA_CODE_KEY + phoneNumber;
String cacheCode = redisCache.getCacheObject(rk);
if (cacheCode == null) {
throw new CaptchaExpireException();
}
redisCache.deleteObject(rk);
if (!StrUtil.equals(cacheCode, code)) {
throw new CaptchaException();
}
}

View File

@ -6,10 +6,12 @@ import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.security.context.AuthenticationContextHolder;
import com.ruoyi.system.service.ISysUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
@ -51,7 +53,10 @@ public class UserDetailsServiceImpl implements UserDetailsService
throw new ServiceException(MessageUtils.message("user.blocked"));
}
passwordService.validate(user);
Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext();
String password = usernamePasswordAuthenticationToken.getCredentials().toString();
passwordService.validate(user, password);
return createLoginUser(user);
}

View File

@ -33,6 +33,14 @@ public interface SysUserMapper extends BaseMapper<SysUser> {
*/
UserInfo getUserInfoByUsername(@Param("userName") String userName);
/**
*
*
* @param phoneNumber
* @return
*/
UserInfo getUserInfoByPhoneNumber(@Param("phoneNumber") String phoneNumber);
/**
*
*

View File

@ -31,6 +31,14 @@ public interface ISysUserService {
*/
UserInfo getUserByUsername(String userName);
/**
*
*
* @param phoneNumber
* @return
*/
UserInfo getUserByPhoneNumber(String phoneNumber);
/**
*
*

View File

@ -69,6 +69,13 @@ public class SysUserServiceImpl implements ISysUserService {
return userInfo;
}
@Override
public UserInfo getUserByPhoneNumber(String phoneNumber) {
UserInfo userInfo = userMapper.getUserInfoByPhoneNumber(phoneNumber);
fillMenus(userInfo);
return userInfo;
}
@Override
public SysUser getBaseUser(Long userId) {
return userMapper.selectById(userId);
@ -451,12 +458,15 @@ public class SysUserServiceImpl implements ISysUserService {
Assert.notNull(user);
Assert.notEmpty(user.getUserName(), "用户名称不能为空");
Assert.notEmpty(user.getNickName(), "用户昵称不能为空");
Assert.isFalse(checkUserNameUnique(user), "用户名称已被注册");
if (StrUtil.isNotEmpty(user.getPhonenumber())) {
Assert.isTrue(checkPhoneUnique(user), "手机号已被注册");
}
Assert.isTrue(checkUserNameUnique(user), "用户名称已被注册");
if (StrUtil.isNotEmpty(user.getEmail())) {
Assert.isFalse(checkEmailUnique(user), "邮箱已被注册");
Assert.isTrue(checkEmailUnique(user), "邮箱已被注册");
}
if (StrUtil.isNotEmpty(user.getPhonenumber())) {
Assert.isFalse(checkPhoneUnique(user), "手机号已被注册");
Assert.isTrue(checkPhoneUnique(user), "手机号已被注册");
}
}

View File

@ -172,6 +172,47 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
su.del_flag = '0'
AND su.user_name = #{userName}
</select>
<select id="getUserInfoByPhoneNumber" resultMap="UserInfo">
SELECT
su.*,
sur.user_id AS rel_user_id,
sur.store_id AS rel_store_id,
sr.role_id,
sr.role_name,
sr.role_key,
sr.role_sort,
sr.store_id,
sr.status AS role_status,
sr.remark AS role_remark,
srm.role_id AS rel_role_id,
sm.menu_id,
sm.menu_name,
sm.parent_id,
sm.order_num,
sm.path,
sm.component,
sm.query,
sm.route_name,
sm.is_frame,
sm.is_cache,
sm.menu_type,
sm.visible,
sm.status AS menu_status,
sm.perms,
sm.icon,
sm.remark AS menu_remark
FROM
sys_user su
LEFT JOIN sys_user_role sur ON su.user_id = sur.user_id
LEFT JOIN sys_role sr ON sur.role_id = sr.role_id
LEFT JOIN sys_role_menu srm ON sr.role_id = srm.role_id
LEFT JOIN sys_menu sm ON srm.menu_id = sm.menu_id
WHERE
su.del_flag = '0'
AND su.phonenumber = #{phoneNumber}
</select>
<select id="listUser" parameterType="com.ruoyi.common.core.domain.model.UserQuery" resultMap="UserListItem">
SELECT
su.*,
@ -236,4 +277,5 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
LIMIT 1
</select>
</mapper>