4 changed files with 165 additions and 217 deletions
@ -1,4 +1,113 @@ |
|||
package com.zdxt.enforcementcode.controller.app; |
|||
|
|||
public class LoginController { |
|||
import cn.dev33.satoken.stp.StpUtil; |
|||
import cn.dev33.satoken.stp.parameter.SaLoginParameter; |
|||
import cn.hutool.crypto.digest.DigestUtil; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.dromara.common.core.domain.R; |
|||
import org.dromara.common.core.domain.model.LoginUser; |
|||
import org.dromara.common.satoken.utils.LoginHelper; |
|||
|
|||
import javax.crypto.Cipher; |
|||
import javax.crypto.spec.IvParameterSpec; |
|||
import javax.crypto.spec.SecretKeySpec; |
|||
import java.nio.charset.StandardCharsets; |
|||
import java.util.Base64; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* App端登录公共基类 |
|||
* 提供AES解密、参数校验、密码校验、SaToken登录等公共方法 |
|||
*/ |
|||
@Slf4j |
|||
public abstract class LoginController { |
|||
|
|||
/** |
|||
* AES密钥(与前端一致) |
|||
*/ |
|||
protected static final String AES_KEY = "1234567898745621"; |
|||
|
|||
/** |
|||
* 校验登录参数非空 |
|||
* |
|||
* @return 校验失败返回错误R,校验通过返回null |
|||
*/ |
|||
protected R<Map<String, Object>> validateLoginParams(String secretName, String secretKey) { |
|||
if (secretName == null || secretName.isBlank()) { |
|||
return R.fail("用户名不能为空"); |
|||
} |
|||
if (secretKey == null || secretKey.isBlank()) { |
|||
return R.fail("密码不能为空"); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* AES解密前端传来的密码 |
|||
* |
|||
* @return 解密后的明文密码,失败返回null |
|||
*/ |
|||
protected String decryptPassword(String secretKey) { |
|||
try { |
|||
return decryptByAes(secretKey); |
|||
} catch (Exception e) { |
|||
log.error("密码解密失败", e); |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* MD5加盐校验密码: MD5(loginName + password + salt) |
|||
*/ |
|||
protected boolean verifyPassword(String loginName, String decryptedPassword, String salt, String storedPassword) { |
|||
String md5Password = DigestUtil.md5Hex(loginName + decryptedPassword + salt); |
|||
return md5Password.equals(storedPassword); |
|||
} |
|||
|
|||
/** |
|||
* 构建LoginUser并执行SaToken登录 |
|||
* |
|||
* @return SaToken令牌值 |
|||
*/ |
|||
protected String doSaTokenLogin(Long userId, String userName, String nickName, String deptId, |
|||
String userType, String deviceType, String clientId) { |
|||
LoginUser loginUser = new LoginUser(); |
|||
loginUser.setUserId(userId); |
|||
loginUser.setUsername(userName); |
|||
loginUser.setNickname(nickName); |
|||
loginUser.setUserType(userType); |
|||
loginUser.setDeptId(deptId); |
|||
|
|||
SaLoginParameter model = new SaLoginParameter(); |
|||
model.setDeviceType(deviceType); |
|||
model.setExtra("clientid", clientId); |
|||
LoginHelper.login(loginUser, model); |
|||
|
|||
return StpUtil.getTokenValue(); |
|||
} |
|||
|
|||
/** |
|||
* AES/CBC/ZeroPadding 解密(与前端CryptoJS对应) |
|||
* 前端使用 CryptoJS.pad.ZeroPadding,Java中用NoPadding后手动去除尾部0字节 |
|||
*/ |
|||
private String decryptByAes(String encryptedBase64) throws Exception { |
|||
byte[] keyBytes = AES_KEY.getBytes(StandardCharsets.UTF_8); |
|||
byte[] ivBytes = AES_KEY.getBytes(StandardCharsets.UTF_8); |
|||
|
|||
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES"); |
|||
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes); |
|||
|
|||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); |
|||
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); |
|||
|
|||
byte[] encrypted = Base64.getDecoder().decode(encryptedBase64); |
|||
byte[] decrypted = cipher.doFinal(encrypted); |
|||
|
|||
// 去除ZeroPadding尾部的0字节
|
|||
int len = decrypted.length; |
|||
while (len > 0 && decrypted[len - 1] == 0) { |
|||
len--; |
|||
} |
|||
return new String(decrypted, 0, len, StandardCharsets.UTF_8); |
|||
} |
|||
} |
|||
|
|||
Loading…
Reference in new issue