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.notNull(storeId);
Assert.notEmpty(vo.getPhonenumber(), "用户手机号不能为空"); Assert.notEmpty(vo.getPhonenumber(), "用户手机号不能为空");
Set<Long> subRoleIds = roleService.getSubRoleIdsByStore(storeId); Set<Long> subRoleIds = roleService.getSubRoleIdsByStore(storeId);
CollUtil.emptyIfNull(vo.getRoleIds())
.forEach(roleId -> Assert.isTrue(subRoleIds.contains(roleId), "角色非法"));
UserInfo info = userService.getUserByPhoneNumber(vo.getPhonenumber()); UserInfo info = userService.getUserByPhoneNumber(vo.getPhonenumber());
Assert.notNull(info, "用户不存在"); Assert.notNull(info, "用户不存在");
List<Long> roleIds = new ArrayList<>(); List<Long> roleIds = new ArrayList<>();
@ -153,8 +155,8 @@ public class StoreUserController extends BaseController {
} }
UserInfoEdit dto = BeanUtil.toBean(info, UserInfoEdit.class); UserInfoEdit dto = BeanUtil.toBean(info, UserInfoEdit.class);
roleIds.addAll(CollUtil.emptyIfNull(vo.getRoleIds())); roleIds.addAll(CollUtil.emptyIfNull(vo.getRoleIds()));
dto.setUserName(vo.getUserName());
dto.setRoleIds(roleIds); dto.setRoleIds(roleIds);
dto.setUserName(vo.getUserName());
Long userId = userService.updateUser(dto); Long userId = userService.updateUser(dto);
// 清除用户缓存(退出登录) // 清除用户缓存(退出登录)
tokenService.deleteCacheUser(userId); 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.ISysRoleService;
import com.ruoyi.system.service.ISysUserService; import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.web.controller.system.vo.*; import com.ruoyi.web.controller.system.vo.*;
import com.ruoyi.xkt.manager.AliAuthManager;
import com.ruoyi.xkt.service.IStoreService; import com.ruoyi.xkt.service.IStoreService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
@ -59,6 +60,8 @@ public class SysLoginController {
private TokenService tokenService; private TokenService tokenService;
@Autowired @Autowired
private RedisCache redisCache; private RedisCache redisCache;
@Autowired
private AliAuthManager aliAuthManager;
/** /**
* *
@ -69,14 +72,17 @@ public class SysLoginController {
@ApiOperation(value = "用户名密码登录") @ApiOperation(value = "用户名密码登录")
@PostMapping("/loginByUname") @PostMapping("/loginByUname")
public AjaxResult login(@Validated @RequestBody LoginByUsernameVO loginBody) { 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(); AjaxResult ajax = AjaxResult.success();
// 生成令牌 // 生成令牌
LoginCredential credential = LoginCredential.builder() LoginCredential credential = LoginCredential.builder()
.loginType(ELoginType.USERNAME) .loginType(ELoginType.USERNAME)
.username(loginBody.getUsername()) .username(loginBody.getUsername())
.password(loginBody.getPassword()) .password(loginBody.getPassword())
.imgUuid(loginBody.getUuid())
.imgVerificationCode(loginBody.getCode())
.build(); .build();
String token = loginService.login(credential); String token = loginService.login(credential);
ajax.put(Constants.TOKEN, token); ajax.put(Constants.TOKEN, token);
@ -101,7 +107,12 @@ public class SysLoginController {
@ApiOperation(value = "发送登录短信验证码") @ApiOperation(value = "发送登录短信验证码")
@PostMapping("/sendSmsVerificationCode") @PostMapping("/sendSmsVerificationCode")
public R sendSmsVerificationCode(@Validated @RequestBody LoginSmsReqVO vo) { 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(); 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.LoginSmsReqVO;
import com.ruoyi.web.controller.system.vo.RegisterBySmsCodeVO; import com.ruoyi.web.controller.system.vo.RegisterBySmsCodeVO;
import com.ruoyi.web.controller.xkt.vo.PhoneNumberVO; import com.ruoyi.web.controller.xkt.vo.PhoneNumberVO;
import com.ruoyi.xkt.manager.AliAuthManager;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -45,6 +46,9 @@ public class SysRegisterController extends BaseController {
@Autowired @Autowired
private ISysUserService userService; private ISysUserService userService;
@Autowired
private AliAuthManager aliAuthManager;
@ApiOperation(value = "档口供应商注册") @ApiOperation(value = "档口供应商注册")
@PostMapping("/registerStore") @PostMapping("/registerStore")
public AjaxResult registerStore(@Validated @RequestBody RegisterBySmsCodeVO vo) { public AjaxResult registerStore(@Validated @RequestBody RegisterBySmsCodeVO vo) {
@ -90,7 +94,12 @@ public class SysRegisterController extends BaseController {
@ApiOperation(value = "发送登录短信验证码") @ApiOperation(value = "发送登录短信验证码")
@PostMapping("/sendSmsVerificationCode") @PostMapping("/sendSmsVerificationCode")
public R sendSmsVerificationCode(@Validated @RequestBody LoginSmsReqVO vo) { 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(); return R.ok();
} }

View File

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

View File

@ -1,8 +1,11 @@
package com.ruoyi.web.controller.system.vo; package com.ruoyi.web.controller.system.vo;
import com.ruoyi.web.controller.xkt.vo.AliCaptchaAuthReqVO;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern; import javax.validation.constraints.Pattern;
@ -13,16 +16,12 @@ import javax.validation.constraints.Pattern;
*/ */
@ApiModel @ApiModel
@Data @Data
public class LoginSmsReqVO { @EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class LoginSmsReqVO extends AliCaptchaAuthReqVO {
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
@NotEmpty(message = "手机号不能为空") @NotEmpty(message = "手机号不能为空")
@ApiModelProperty("手机号") @ApiModelProperty("手机号")
private String phoneNumber; 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 signType: RSA2
charset: UTF-8 charset: UTF-8
gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do 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 smsVerificationCode;
private String imgUuid;
private String imgVerificationCode;
} }

View File

@ -62,8 +62,7 @@ public class SysLoginService {
LoginUser loginUser; LoginUser loginUser;
switch (loginCredential.getLoginType()) { switch (loginCredential.getLoginType()) {
case USERNAME: case USERNAME:
loginUser = loginByUsername(loginCredential.getUsername(), loginCredential.getPassword(), loginUser = loginByUsername(loginCredential.getUsername(), loginCredential.getPassword());
loginCredential.getImgVerificationCode(), loginCredential.getImgUuid());
break; break;
case SMS_VERIFICATION_CODE: case SMS_VERIFICATION_CODE:
loginUser = loginBySmsVerificationCode(loginCredential.getPhoneNumber(), loginUser = loginBySmsVerificationCode(loginCredential.getPhoneNumber(),
@ -115,14 +114,12 @@ public class SysLoginService {
* *
* @param username * @param username
* @param password * @param password
* @param code
* @param uuid
* @return * @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); 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;
}
}