feat: 登录信息加密传输

This commit is contained in:
2026-06-23 16:00:47 +08:00
parent 03ad011f64
commit e56f043483
33 changed files with 2273 additions and 113 deletions

View File

@@ -2,15 +2,22 @@ package tech.easyflow.admin.controller.auth;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson2.JSONObject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.easyflow.auth.entity.LoginDTO;
import tech.easyflow.auth.entity.LoginVO;
import tech.easyflow.auth.entity.*;
import tech.easyflow.auth.service.AuthCredentialKeyService;
import tech.easyflow.auth.service.AuthService;
import tech.easyflow.common.captcha.tainai.CaptchaVerificationService;
import tech.easyflow.common.constant.Constants;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.common.web.jsonbody.JsonBody;
import tech.easyflow.log.annotation.LogRecord;
import javax.annotation.Resource;
import java.util.List;
@@ -21,9 +28,23 @@ public class AuthController {
@Resource
private AuthService authService;
@Resource
private AuthCredentialKeyService credentialKeyService;
@Resource
private CaptchaVerificationService captchaVerificationService;
@GetMapping("credential-key")
public Result<CredentialKeyVO> credentialKey() {
return Result.ok(credentialKeyService.getCurrentCredentialKey());
}
@PostMapping("login")
public Result<LoginVO> login(@JsonBody LoginDTO loginDTO) {
public Result<LoginVO> login(@JsonBody EncryptedCredentialDTO encryptedCredential) {
JSONObject payload = credentialKeyService.decryptPayload(encryptedCredential);
if (!captchaVerificationService.verify(payload.getString("validToken"))) {
return captchaVerificationService.failureResult();
}
LoginDTO loginDTO = toLoginDTO(payload);
LoginVO res = authService.login(loginDTO);
return Result.ok(res);
}
@@ -45,4 +66,23 @@ public class AuthController {
List<String> permissionList = StpUtil.getPermissionList();
return Result.ok(permissionList);
}
@PostMapping("credential-key/rotate")
@LogRecord("轮换认证传输密钥")
public Result<Void> rotateCredentialKey(@JsonBody CredentialKeyRotateDTO rotateDTO) {
StpUtil.checkLogin();
LoginAccount loginAccount = SaTokenUtil.getLoginAccount();
if (loginAccount == null || !Constants.SUPER_ADMIN_ID.equals(loginAccount.getId())) {
throw new BusinessException("仅超级管理员可轮换认证密钥");
}
credentialKeyService.rotate(rotateDTO != null && Boolean.TRUE.equals(rotateDTO.getEmergency()));
return Result.ok();
}
private LoginDTO toLoginDTO(JSONObject payload) {
LoginDTO loginDTO = new LoginDTO();
loginDTO.setAccount(payload.getString("account"));
loginDTO.setPassword(payload.getString("password"));
return loginDTO;
}
}

View File

@@ -3,6 +3,7 @@ package tech.easyflow.admin.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.alibaba.fastjson2.JSONObject;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.servlet.http.HttpServletResponse;
@@ -12,6 +13,8 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import tech.easyflow.auth.entity.EncryptedCredentialDTO;
import tech.easyflow.auth.service.AuthCredentialKeyService;
import tech.easyflow.common.constant.enums.EnumAccountType;
import tech.easyflow.common.constant.enums.EnumDataStatus;
import tech.easyflow.common.domain.Result;
@@ -27,12 +30,13 @@ import tech.easyflow.system.entity.vo.SysAccountImportResultVo;
import tech.easyflow.system.service.SysAccountService;
import tech.easyflow.system.util.SysPasswordPolicy;
import java.net.URLEncoder;
import java.io.Serializable;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* 用户表 控制层。
@@ -43,8 +47,11 @@ import java.util.List;
@RestController("sysAccountController")
@RequestMapping("/api/v1/sysAccount")
public class SysAccountController extends BaseCurdController<SysAccountService, SysAccount> {
public SysAccountController(SysAccountService service) {
private final AuthCredentialKeyService credentialKeyService;
public SysAccountController(SysAccountService service, AuthCredentialKeyService credentialKeyService) {
super(service);
this.credentialKeyService = credentialKeyService;
}
@Override
@@ -67,7 +74,7 @@ public class SysAccountController extends BaseCurdController<SysAccountService,
if (count > 0) {
return Result.fail(1, "用户名已存在");
}
String password = entity.getPassword();
String password = decryptInitialPassword(entity.getPasswordCredential());
if (!StringUtil.hasText(password)) {
return Result.fail(1, "密码不能为空");
}
@@ -143,14 +150,14 @@ public class SysAccountController extends BaseCurdController<SysAccountService,
/**
* 修改密码,用于修改用户自己的密码
*
* @param password 用户的旧密码
* @param newPassword 新密码
* @param confirmPassword 确认密码
* @param encryptedCredential 加密后的旧密码、新密码与确认密码
*/
@PostMapping("/updatePassword")
public Result<Void> updatePassword(@JsonBody(value = "password", required = true) String password,
@JsonBody(value = "newPassword", required = true) String newPassword,
@JsonBody(value = "confirmPassword", required = true) String confirmPassword) {
public Result<Void> updatePassword(@JsonBody EncryptedCredentialDTO encryptedCredential) {
JSONObject payload = credentialKeyService.decryptPayload(encryptedCredential);
String password = payload.getString("password");
String newPassword = payload.getString("newPassword");
String confirmPassword = payload.getString("confirmPassword");
BigInteger loginAccountId = SaTokenUtil.getLoginAccount().getId();
SysAccount record = service.getById(loginAccountId);
if (record == null) {
@@ -174,6 +181,28 @@ public class SysAccountController extends BaseCurdController<SysAccountService,
return Result.ok();
}
private String decryptInitialPassword(Map<String, Object> passwordCredential) {
if (passwordCredential == null || passwordCredential.isEmpty()) {
return null;
}
JSONObject payload = credentialKeyService.decryptPayload(toEncryptedCredential(passwordCredential));
return payload.getString("password");
}
private EncryptedCredentialDTO toEncryptedCredential(Map<String, Object> passwordCredential) {
EncryptedCredentialDTO encryptedCredential = new EncryptedCredentialDTO();
encryptedCredential.setKeyId(asString(passwordCredential.get("keyId")));
encryptedCredential.setEncryptedKey(asString(passwordCredential.get("encryptedKey")));
encryptedCredential.setIv(asString(passwordCredential.get("iv")));
encryptedCredential.setCiphertext(asString(passwordCredential.get("ciphertext")));
encryptedCredential.setNonce(asString(passwordCredential.get("nonce")));
return encryptedCredential;
}
private String asString(Object value) {
return value == null ? null : String.valueOf(value);
}
@PostMapping("/resetPassword")
@SaCheckPermission("/api/v1/sysAccount/save")
public Result<Void> resetPassword(@JsonBody(value = "id", required = true) BigInteger id) {