pull/1121/head
梁宇奇 2025-07-22 16:45:40 +08:00
parent 56f8abbc12
commit d641eb102a
10 changed files with 139 additions and 35 deletions

View File

@ -135,6 +135,8 @@ public class StoreUserController extends BaseController {
Assert.notNull(storeId);
Assert.notEmpty(vo.getPhonenumber(), "用户手机号不能为空");
Set<Long> subRoleIds = roleService.getSubRoleIdsByStore(storeId);
CollUtil.emptyIfNull(vo.getRoleIds())
.forEach(roleId -> Assert.isTrue(subRoleIds.contains(roleId), "角色非法"));
UserInfo info = userService.getUserByPhoneNumber(vo.getPhonenumber());
Assert.notNull(info, "用户不存在");
List<Long> roleIds = new ArrayList<>();
@ -153,8 +155,8 @@ public class StoreUserController extends BaseController {
}
UserInfoEdit dto = BeanUtil.toBean(info, UserInfoEdit.class);
roleIds.addAll(CollUtil.emptyIfNull(vo.getRoleIds()));
dto.setUserName(vo.getUserName());
dto.setRoleIds(roleIds);
dto.setUserName(vo.getUserName());
Long userId = userService.updateUser(dto);
// 清除用户缓存(退出登录)
tokenService.deleteCacheUser(userId);

View File

@ -22,6 +22,7 @@ import com.ruoyi.system.service.ISysMenuService;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.web.controller.system.vo.*;
import com.ruoyi.xkt.manager.AliAuthManager;
import com.ruoyi.xkt.service.IStoreService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@ -59,6 +60,8 @@ public class SysLoginController {
private TokenService tokenService;
@Autowired
private RedisCache redisCache;
@Autowired
private AliAuthManager aliAuthManager;
/**
*
@ -69,14 +72,17 @@ public class SysLoginController {
@ApiOperation(value = "用户名密码登录")
@PostMapping("/loginByUname")
public AjaxResult login(@Validated @RequestBody LoginByUsernameVO loginBody) {
boolean captchaPass = aliAuthManager.validate(loginBody.getLot_number(), loginBody.getCaptcha_output(),
loginBody.getPass_token(), loginBody.getGen_time());
if (!captchaPass) {
return AjaxResult.error("验证失败");
}
AjaxResult ajax = AjaxResult.success();
// 生成令牌
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);
@ -101,7 +107,12 @@ public class SysLoginController {
@ApiOperation(value = "发送登录短信验证码")
@PostMapping("/sendSmsVerificationCode")
public R sendSmsVerificationCode(@Validated @RequestBody LoginSmsReqVO vo) {
loginService.sendSmsVerificationCode(vo.getPhoneNumber(), true, vo.getCode(), vo.getUuid());
boolean captchaPass = aliAuthManager.validate(vo.getLot_number(), vo.getCaptcha_output(),
vo.getPass_token(), vo.getGen_time());
if (!captchaPass) {
return R.fail("验证失败");
}
loginService.sendSmsVerificationCode(vo.getPhoneNumber(), false, null, null);
return R.ok();
}

View File

@ -15,6 +15,7 @@ import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.web.controller.system.vo.LoginSmsReqVO;
import com.ruoyi.web.controller.system.vo.RegisterBySmsCodeVO;
import com.ruoyi.web.controller.xkt.vo.PhoneNumberVO;
import com.ruoyi.xkt.manager.AliAuthManager;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
@ -45,6 +46,9 @@ public class SysRegisterController extends BaseController {
@Autowired
private ISysUserService userService;
@Autowired
private AliAuthManager aliAuthManager;
@ApiOperation(value = "档口供应商注册")
@PostMapping("/registerStore")
public AjaxResult registerStore(@Validated @RequestBody RegisterBySmsCodeVO vo) {
@ -90,7 +94,12 @@ public class SysRegisterController extends BaseController {
@ApiOperation(value = "发送登录短信验证码")
@PostMapping("/sendSmsVerificationCode")
public R sendSmsVerificationCode(@Validated @RequestBody LoginSmsReqVO vo) {
loginService.sendSmsVerificationCode(vo.getPhoneNumber(), true, vo.getCode(), vo.getUuid());
boolean captchaPass = aliAuthManager.validate(vo.getLot_number(), vo.getCaptcha_output(),
vo.getPass_token(), vo.getGen_time());
if (!captchaPass) {
return R.fail("验证失败");
}
loginService.sendSmsVerificationCode(vo.getPhoneNumber(), false, null, null);
return R.ok();
}

View File

@ -1,8 +1,11 @@
package com.ruoyi.web.controller.system.vo;
import com.ruoyi.web.controller.xkt.vo.AliCaptchaAuthReqVO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotEmpty;
@ -12,7 +15,9 @@ import javax.validation.constraints.NotEmpty;
*/
@ApiModel
@Data
public class LoginByUsernameVO {
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class LoginByUsernameVO extends AliCaptchaAuthReqVO {
/**
*
*/
@ -26,16 +31,4 @@ public class LoginByUsernameVO {
@NotEmpty(message = "用户密码不能为空")
@ApiModelProperty(value = "用户密码", required = true)
private String password;
/**
*
*/
@ApiModelProperty("图形验证码")
private String code;
/**
*
*/
@ApiModelProperty("图形验证码唯一标识")
private String uuid;
}

View File

@ -1,8 +1,11 @@
package com.ruoyi.web.controller.system.vo;
import com.ruoyi.web.controller.xkt.vo.AliCaptchaAuthReqVO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
@ -13,16 +16,12 @@ import javax.validation.constraints.Pattern;
*/
@ApiModel
@Data
public class LoginSmsReqVO {
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class LoginSmsReqVO extends AliCaptchaAuthReqVO {
@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,33 @@
package com.ruoyi.web.controller.xkt.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
/**
* @author liangyq
* @date 2025-07-22
*/
@ApiModel
@Data
public class AliCaptchaAuthReqVO {
@NotEmpty(message = "lot_number不能为空")
@ApiModelProperty("lot_number图像验证参数")
private String lot_number;
@NotEmpty(message = "captcha_output不能为空")
@ApiModelProperty("captcha_output图像验证参数")
private String captcha_output;
@NotEmpty(message = "pass_token不能为空")
@ApiModelProperty("pass_token图像验证参数")
private String pass_token;
@NotEmpty(message = "gen_time不能为空")
@ApiModelProperty("gen_time图像验证参数")
private String gen_time;
}

View File

@ -220,3 +220,9 @@ alipay:
signType: RSA2
charset: UTF-8
gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do
# 阿里认证
aliauth:
captcha:
appId: 386ec134856c9fe7d47ce2a34ee91038
appKey: 1b4e3ff703ca1ea0fb802d188079a25c

View File

@ -25,8 +25,4 @@ public class LoginCredential {
private String smsVerificationCode;
private String imgUuid;
private String imgVerificationCode;
}

View File

@ -62,8 +62,7 @@ public class SysLoginService {
LoginUser loginUser;
switch (loginCredential.getLoginType()) {
case USERNAME:
loginUser = loginByUsername(loginCredential.getUsername(), loginCredential.getPassword(),
loginCredential.getImgVerificationCode(), loginCredential.getImgUuid());
loginUser = loginByUsername(loginCredential.getUsername(), loginCredential.getPassword());
break;
case SMS_VERIFICATION_CODE:
loginUser = loginBySmsVerificationCode(loginCredential.getPhoneNumber(),
@ -115,14 +114,12 @@ public class SysLoginService {
*
* @param username
* @param password
* @param code
* @param uuid
* @return
*/
private LoginUser loginByUsername(String username, String password, String code, String uuid) {
private LoginUser loginByUsername(String username, String password) {
// 验证码校验
validateCaptcha(username, code, uuid);
// validateCaptcha(username, code, uuid);
// 登录前置校验
loginPreCheck(username, password);

View File

@ -0,0 +1,58 @@
package com.ruoyi.xkt.manager;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author liangyq
* @date 2025-07-22
*/
@Slf4j
@Component
public class AliAuthManager {
private static final String CAPTCHA_URL = "https://captcha.alicaptcha.com/validate?captcha_id=%s";
@Value("${aliauth.captcha.appId:}")
private String captchaId;
@Value("${aliauth.captcha.appKey:}")
private String captchaKey;
public boolean validate(String lotNumber, String captchaOutput, String passToken, String genTime) {
if (StrUtil.isEmpty(lotNumber)
|| StrUtil.isEmpty(captchaOutput)
|| StrUtil.isEmpty(passToken)
|| StrUtil.isEmpty(genTime)) {
log.warn("图形认证参数异常: {}, {}, {}, {}", lotNumber, captchaOutput, passToken, genTime);
return false;
}
try {
// 生成签名使用标准的hmac算法使用用户当前完成验证的流水号lot_number作为原始消息message使用客户验证私钥作为key
// 采用sha256散列算法将message和key进行单向散列生成最终的签名
String signToken = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, captchaKey).hmacHex(lotNumber);
// 上传校验参数到验证服务二次验证接口, 校验用户验证状态
// captcha_id 参数建议放在 url 后面, 方便请求异常时可以在日志中根据id快速定位到异常请求
String url = String.format(CAPTCHA_URL, captchaId);
HttpRequest httpRequest = HttpUtil.createPost(url);
httpRequest.header("Content-Type", "application/x-www-form-urlencoded");
httpRequest.form("lot_number", lotNumber);
httpRequest.form("captcha_output", captchaOutput);
httpRequest.form("pass_token", passToken);
httpRequest.form("gen_time", genTime);
httpRequest.form("sign_token", signToken);
String resBody = httpRequest.execute().body();
log.info("图形认证结果: {}", resBody);
return "success".equals(JSON.parseObject(resBody).getString("result"));
} catch (Exception e) {
log.error("图形认证失败", e);
}
return false;
}
}