diff --git a/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/LoginController.java b/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/LoginController.java index de13846f..4445ea3e 100644 --- a/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/LoginController.java +++ b/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/LoginController.java @@ -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> 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); + } } diff --git a/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfLoginController.java b/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfLoginController.java index 38ba7023..7ac9546a 100644 --- a/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfLoginController.java +++ b/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfLoginController.java @@ -1,14 +1,10 @@ package com.zdxt.enforcementcode.controller.app.enforcement; import cn.dev33.satoken.annotation.SaIgnore; -import cn.dev33.satoken.stp.StpUtil; -import cn.dev33.satoken.stp.parameter.SaLoginParameter; -import cn.hutool.crypto.digest.DigestUtil; +import com.zdxt.enforcementcode.controller.app.LoginController; import lombok.RequiredArgsConstructor; 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 org.dromara.system.domain.vo.SysDeptVo; import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.service.ISysDeptService; @@ -18,11 +14,6 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -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.HashMap; import java.util.Map; @@ -33,16 +24,11 @@ import java.util.Map; @RequiredArgsConstructor @RestController @RequestMapping("/app-enf/login") -public class AppEnfLoginController { +public class AppEnfLoginController extends LoginController { private final ISysUserService sysUserService; private final ISysDeptService sysDeptService; - /** - * AES密钥(与前端一致) - */ - private static final String AES_KEY = "1234567898745621"; - /** * 执法端用户登录 * @@ -55,40 +41,35 @@ public class AppEnfLoginController { String secretName = params.get("secretName"); String secretKey = params.get("secretKey"); - if (secretName == null || secretName.isBlank()) { - return R.fail("用户名不能为空"); - } - if (secretKey == null || secretKey.isBlank()) { - return R.fail("密码不能为空"); + // 1. 参数校验 + R> paramCheck = validateLoginParams(secretName, secretKey); + if (paramCheck != null) { + return paramCheck; } - // 1. 根据用户名查询 sys_user 表 + // 2. 根据用户名查询 sys_user 表 SysUserVo user = sysUserService.selectUserByUserName(secretName); if (user == null) { return R.fail("用户不存在"); } - // 2. 检查用户状态(0正常 1停用) + // 3. 检查用户状态(0正常 1停用) if ("1".equals(user.getStatus())) { return R.fail("该用户已被停用"); } - // 3. AES解密前端传来的密码 - String decryptedPassword; - try { - decryptedPassword = decryptByAes(secretKey); - } catch (Exception e) { - log.error("密码解密失败", e); + // 4. AES解密前端传来的密码 + String decryptedPassword = decryptPassword(secretKey); + if (decryptedPassword == null) { return R.fail("密码解密失败"); } - // 4. MD5加盐校验密码: MD5(username + password + salt) - String md5Password = DigestUtil.md5Hex(user.getUserName() + decryptedPassword + user.getSalt()); - if (!md5Password.equals(user.getPassword())) { + // 5. MD5加盐校验密码 + if (!verifyPassword(user.getUserName(), decryptedPassword, user.getSalt(), user.getPassword())) { return R.fail("用户名或密码错误"); } - // 5. 查询部门信息 + // 6. 查询部门信息 String deptName = ""; if (user.getDeptId() != null && !user.getDeptId().isBlank()) { SysDeptVo dept = sysDeptService.selectDeptById(user.getDeptId()); @@ -97,22 +78,13 @@ public class AppEnfLoginController { } } - // 6. 构建LoginUser并生成SaToken - LoginUser loginUser = new LoginUser(); - loginUser.setUserId(user.getUserId()); - loginUser.setUsername(user.getUserName()); - loginUser.setNickname(user.getNickName()); - loginUser.setUserType("enforcement_user"); - loginUser.setDeptId(user.getDeptId()); + // 7. 构建LoginUser并生成SaToken + String token = doSaTokenLogin(user.getUserId(), user.getUserName(), user.getNickName(), + user.getDeptId(), "enforcement_user", "enforcement_app", "enforcement"); - SaLoginParameter model = new SaLoginParameter(); - model.setDeviceType("enforcement_app"); - model.setExtra("clientid", "enforcement"); - LoginHelper.login(loginUser, model); - - // 7. 构造返回数据 + // 8. 构造返回数据 Map data = new HashMap<>(); - data.put("token", StpUtil.getTokenValue()); + data.put("token", token); data.put("userId", user.getUserId()); data.put("userName", user.getUserName()); data.put("nickName", user.getNickName()); @@ -122,29 +94,4 @@ public class AppEnfLoginController { return R.ok(data); } - - /** - * 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); - } } diff --git a/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enterprise/AppEntLoginController.java b/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enterprise/AppEntLoginController.java index e77c74c2..1b796658 100644 --- a/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enterprise/AppEntLoginController.java +++ b/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enterprise/AppEntLoginController.java @@ -1,24 +1,15 @@ package com.zdxt.enforcementcode.controller.app.enterprise; import cn.dev33.satoken.annotation.SaIgnore; -import cn.dev33.satoken.stp.StpUtil; -import cn.dev33.satoken.stp.parameter.SaLoginParameter; -import cn.hutool.crypto.digest.DigestUtil; +import com.zdxt.enforcementcode.controller.app.LoginController; import com.zdxt.enforcementcode.domain.vo.EnterpriseUserVo; import com.zdxt.enforcementcode.service.IEnterpriseInformationService; import com.zdxt.enforcementcode.service.IEnterpriseUserService; import lombok.RequiredArgsConstructor; 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 org.springframework.web.bind.annotation.*; -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.HashMap; import java.util.Map; @@ -29,16 +20,11 @@ import java.util.Map; @RequiredArgsConstructor @RestController @RequestMapping("/app-ent/login") -public class AppEntLoginController { +public class AppEntLoginController extends LoginController { private final IEnterpriseUserService enterpriseUserService; private final IEnterpriseInformationService enterpriseInformationService; - /** - * AES密钥(与前端一致) - */ - private static final String AES_KEY = "1234567898745621"; - /** * 企业端用户登录 * @@ -51,57 +37,41 @@ public class AppEntLoginController { String secretName = params.get("secretName"); String secretKey = params.get("secretKey"); - if (secretName == null || secretName.isBlank()) { - return R.fail("用户名不能为空"); - } - if (secretKey == null || secretKey.isBlank()) { - return R.fail("密码不能为空"); + // 1. 参数校验 + R> paramCheck = validateLoginParams(secretName, secretKey); + if (paramCheck != null) { + return paramCheck; } - // 1. 根据登录名查询用户 + // 2. 根据登录名查询用户 EnterpriseUserVo user = enterpriseUserService.queryByLoginName(secretName); if (user == null) { return R.fail("用户不存在"); } - // 2. 检查用户状态 + // 3. 检查用户状态 if ("1".equals(user.getStatus())) { return R.fail("该用户已被停用"); } - // 3. AES解密前端传来的密码 - String decryptedPassword; - try { - decryptedPassword = decryptByAes(secretKey); - } catch (Exception e) { - log.error("密码解密失败", e); + // 4. AES解密前端传来的密码 + String decryptedPassword = decryptPassword(secretKey); + if (decryptedPassword == null) { return R.fail("密码解密失败"); } - // 4. MD5加盐校验密码: MD5(username + password + salt) - String md5Password = DigestUtil.md5Hex(user.getLoginName() + decryptedPassword + user.getSalt()); - if (!md5Password.equals(user.getPassword())) { + // 5. MD5加盐校验密码 + if (!verifyPassword(user.getLoginName(), decryptedPassword, user.getSalt(), user.getPassword())) { return R.fail("用户名或密码错误"); } - // 5. 构建LoginUser并生成SaToken - LoginUser loginUser = new LoginUser(); - loginUser.setUserId(user.getUserId()); - loginUser.setUsername(user.getUserName()); - loginUser.setNickname(user.getNickName()); - loginUser.setUserType("enterprise_user"); - loginUser.setDeptId(user.getDeptId()); - - SaLoginParameter model = new SaLoginParameter(); - model.setDeviceType("enterprise_app"); - model.setExtra("clientid", "enterprise"); - LoginHelper.login(loginUser, model); - - + // 6. 构建LoginUser并生成SaToken + String token = doSaTokenLogin(user.getUserId(), user.getUserName(), user.getNickName(), + user.getDeptId(), "enterprise_user", "enterprise_app", "enterprise"); // 7. 构造返回数据 Map data = new HashMap<>(); - data.put("token", StpUtil.getTokenValue()); + data.put("token", token); data.put("userId", user.getUserId()); data.put("userName", user.getUserName()); data.put("nickName", user.getNickName()); @@ -109,29 +79,4 @@ public class AppEntLoginController { return R.ok(data); } - - /** - * 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); - } } diff --git a/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/supervision/AppSupLoginController.java b/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/supervision/AppSupLoginController.java index c0946182..3952a983 100644 --- a/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/supervision/AppSupLoginController.java +++ b/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/supervision/AppSupLoginController.java @@ -1,14 +1,10 @@ package com.zdxt.enforcementcode.controller.app.supervision; import cn.dev33.satoken.annotation.SaIgnore; -import cn.dev33.satoken.stp.StpUtil; -import cn.dev33.satoken.stp.parameter.SaLoginParameter; -import cn.hutool.crypto.digest.DigestUtil; +import com.zdxt.enforcementcode.controller.app.LoginController; import lombok.RequiredArgsConstructor; 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 org.dromara.system.domain.vo.SysDeptVo; import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.service.ISysDeptService; @@ -18,11 +14,6 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -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.HashMap; import java.util.Map; @@ -33,16 +24,11 @@ import java.util.Map; @RequiredArgsConstructor @RestController @RequestMapping("/app-sup/login") -public class AppSupLoginController { +public class AppSupLoginController extends LoginController { private final ISysUserService sysUserService; private final ISysDeptService sysDeptService; - /** - * AES密钥(与前端一致) - */ - private static final String AES_KEY = "1234567898745621"; - /** * 监督端用户登录 * @@ -55,40 +41,35 @@ public class AppSupLoginController { String secretName = params.get("secretName"); String secretKey = params.get("secretKey"); - if (secretName == null || secretName.isBlank()) { - return R.fail("用户名不能为空"); - } - if (secretKey == null || secretKey.isBlank()) { - return R.fail("密码不能为空"); + // 1. 参数校验 + R> paramCheck = validateLoginParams(secretName, secretKey); + if (paramCheck != null) { + return paramCheck; } - // 1. 根据用户名查询 sys_user 表 + // 2. 根据用户名查询 sys_user 表 SysUserVo user = sysUserService.selectUserByUserName(secretName); if (user == null) { return R.fail("用户不存在"); } - // 2. 检查用户状态(0正常 1停用) + // 3. 检查用户状态(0正常 1停用) if ("1".equals(user.getStatus())) { return R.fail("该用户已被停用"); } - // 3. AES解密前端传来的密码 - String decryptedPassword; - try { - decryptedPassword = decryptByAes(secretKey); - } catch (Exception e) { - log.error("密码解密失败", e); + // 4. AES解密前端传来的密码 + String decryptedPassword = decryptPassword(secretKey); + if (decryptedPassword == null) { return R.fail("密码解密失败"); } - // 4. MD5加盐校验密码: MD5(username + password + salt) - String md5Password = DigestUtil.md5Hex(user.getUserName() + decryptedPassword + user.getSalt()); - if (!md5Password.equals(user.getPassword())) { + // 5. MD5加盐校验密码 + if (!verifyPassword(user.getUserName(), decryptedPassword, user.getSalt(), user.getPassword())) { return R.fail("用户名或密码错误"); } - // 5. 查询部门信息 + // 6. 查询部门信息 String deptName = ""; if (user.getDeptId() != null && !user.getDeptId().isBlank()) { SysDeptVo dept = sysDeptService.selectDeptById(user.getDeptId()); @@ -97,22 +78,13 @@ public class AppSupLoginController { } } - // 6. 构建LoginUser并生成SaToken - LoginUser loginUser = new LoginUser(); - loginUser.setUserId(user.getUserId()); - loginUser.setUsername(user.getUserName()); - loginUser.setNickname(user.getNickName()); - loginUser.setUserType("supervision_user"); - loginUser.setDeptId(user.getDeptId()); + // 7. 构建LoginUser并生成SaToken + String token = doSaTokenLogin(user.getUserId(), user.getUserName(), user.getNickName(), + user.getDeptId(), "supervision_user", "supervision_app", "supervision"); - SaLoginParameter model = new SaLoginParameter(); - model.setDeviceType("supervision_app"); - model.setExtra("clientid", "supervision"); - LoginHelper.login(loginUser, model); - - // 7. 构造返回数据 + // 8. 构造返回数据 Map data = new HashMap<>(); - data.put("token", StpUtil.getTokenValue()); + data.put("token", token); data.put("userId", user.getUserId()); data.put("userName", user.getUserName()); data.put("nickName", user.getNickName()); @@ -122,29 +94,4 @@ public class AppSupLoginController { return R.ok(data); } - - /** - * 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); - } }