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 new file mode 100644 index 00000000..de13846f --- /dev/null +++ b/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/LoginController.java @@ -0,0 +1,4 @@ +package com.zdxt.enforcementcode.controller.app; + +public class LoginController { +} 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 01c827e9..c0946182 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,12 +1,33 @@ 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 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; +import org.dromara.system.service.ISysUserService; +import org.springframework.web.bind.annotation.PostMapping; +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; + /** - * 企业端-用户登录 + * 监督端-用户登录 */ @Slf4j @RequiredArgsConstructor @@ -14,9 +35,116 @@ import org.springframework.web.bind.annotation.RestController; @RequestMapping("/app-sup/login") public class AppSupLoginController { + private final ISysUserService sysUserService; + private final ISysDeptService sysDeptService; + /** * AES密钥(与前端一致) */ private static final String AES_KEY = "1234567898745621"; + /** + * 监督端用户登录 + * + * @param params 包含 secretName(用户名)和 secretKey(AES加密后的密码) + * @return 登录结果(token + 用户信息) + */ + @SaIgnore + @PostMapping("/do") + public R> validate(@RequestBody Map params) { + 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. 根据用户名查询 sys_user 表 + SysUserVo user = sysUserService.selectUserByUserName(secretName); + if (user == null) { + return R.fail("用户不存在"); + } + + // 2. 检查用户状态(0正常 1停用) + if ("1".equals(user.getStatus())) { + return R.fail("该用户已被停用"); + } + + // 3. AES解密前端传来的密码 + String decryptedPassword; + try { + decryptedPassword = decryptByAes(secretKey); + } catch (Exception e) { + log.error("密码解密失败", e); + return R.fail("密码解密失败"); + } + + // 4. MD5加盐校验密码: MD5(username + password + salt) + String md5Password = DigestUtil.md5Hex(user.getUserName() + decryptedPassword + user.getSalt()); + if (!md5Password.equals(user.getPassword())) { + return R.fail("用户名或密码错误"); + } + + // 5. 查询部门信息 + String deptName = ""; + if (user.getDeptId() != null && !user.getDeptId().isBlank()) { + SysDeptVo dept = sysDeptService.selectDeptById(user.getDeptId()); + if (dept != null) { + deptName = dept.getDeptName(); + } + } + + // 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()); + + SaLoginParameter model = new SaLoginParameter(); + model.setDeviceType("supervision_app"); + model.setExtra("clientid", "supervision"); + LoginHelper.login(loginUser, model); + + // 7. 构造返回数据 + Map data = new HashMap<>(); + data.put("token", StpUtil.getTokenValue()); + data.put("userId", user.getUserId()); + data.put("userName", user.getUserName()); + data.put("nickName", user.getNickName()); + data.put("phonenumber", user.getPhonenumber() != null ? user.getPhonenumber() : ""); + data.put("deptId", user.getDeptId() != null ? user.getDeptId() : ""); + data.put("deptName", deptName); + + 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/zdxt-web-client/zdxt-enforcement-code-app/src/api/supervision/index.js b/zdxt-web-client/zdxt-enforcement-code-app/src/api/supervision/index.js new file mode 100644 index 00000000..fe02f286 --- /dev/null +++ b/zdxt-web-client/zdxt-enforcement-code-app/src/api/supervision/index.js @@ -0,0 +1,10 @@ +import { _get, _post, _form, _delete } from "../../http/index"; + +// region 监督端接口 + +let supervisionAppBaseUrl = "/app-sup"; + +// 监督端用户登录 +export const supervisionLogin = (params, success, error) => _post(supervisionAppBaseUrl + "/login/do", params, success, error); + +// endregion diff --git a/zdxt-web-client/zdxt-enforcement-code-app/src/common/globalExpandRouter.js b/zdxt-web-client/zdxt-enforcement-code-app/src/common/globalExpandRouter.js index cf8b3256..adb4a6e4 100644 --- a/zdxt-web-client/zdxt-enforcement-code-app/src/common/globalExpandRouter.js +++ b/zdxt-web-client/zdxt-enforcement-code-app/src/common/globalExpandRouter.js @@ -4,7 +4,7 @@ const $globalRouterModules = (routes) => { let pathArr = []; let nameArr = []; const modulesRouter = import.meta.glob( - ["../router/enterprise-app/ent-main-router.js", "../router/demo/demo-main-router.js", "../router/enforcement-app/enf-main-router.js"] + ["../router/enterprise-app/ent-main-router.js", "../router/demo/demo-main-router.js", "../router/enforcement-app/enf-main-router.js", "../router/supervision-app/sup-main-router.js"] ,{ eager: true, }); diff --git a/zdxt-web-client/zdxt-enforcement-code-app/src/router/supervision-app/sup-main-router.js b/zdxt-web-client/zdxt-enforcement-code-app/src/router/supervision-app/sup-main-router.js new file mode 100644 index 00000000..65587e27 --- /dev/null +++ b/zdxt-web-client/zdxt-enforcement-code-app/src/router/supervision-app/sup-main-router.js @@ -0,0 +1,23 @@ + +const routes = [ + // 监督端登录 + { + path: "/supervision/login", + name: "supervisionLogin", + component: () => import("@/views/supervision/login.vue"), + meta: { + isKeepAlive: false, + }, + }, + // 监督端首页 + { + path: "/supervision/supervisionHome", + name: "supervisionHome", + component: () => import("@/views/supervision/supervisionHomeV2.vue"), + meta: { + isKeepAlive: false, + }, + }, +]; + +export {routes}; diff --git a/zdxt-web-client/zdxt-enforcement-code-app/src/views/enterprise/login.vue b/zdxt-web-client/zdxt-enforcement-code-app/src/views/enterprise/login.vue index b27f431d..9931663a 100644 --- a/zdxt-web-client/zdxt-enforcement-code-app/src/views/enterprise/login.vue +++ b/zdxt-web-client/zdxt-enforcement-code-app/src/views/enterprise/login.vue @@ -52,13 +52,13 @@ const showTestPicker = ref(false); const testEnterprises = [ - { account: 'weil45', name: '深圳农牧美益肉业有限公司', password: '123456' }, - { account: 'DGD440881199506180622', name: '深圳市深水龙岗污水处理有限公司', password: '123456' }, - { account: 'smiledzb1982', name: '深圳康泰生物制品股份有限公司', password: '123456' }, - { account: 'A7271888', name: '深圳市里程食品有限公司', password: '123456' }, - { account: 'qintongzhen138', name: '欧姆龙电子部件(深圳)有限公司', password: '123456' }, - { account: 'XP073215', name: '深圳市强达电路股份有限公司', password: '123456' }, - { account: '33ef1970fd449676fa4bc197', name: '吉田拉链(深圳)有限公司公明工厂', password: '123456' }, + { account: 'weil45', name: '深圳农牧美益肉业有限公司', password: 'szsf@zfjd' }, + { account: 'DGD440881199506180622', name: '深圳市深水龙岗污水处理有限公司', password: 'szsf@zfjd' }, + { account: 'smiledzb1982', name: '深圳康泰生物制品股份有限公司', password: 'szsf@zfjd' }, + { account: 'A7271888', name: '深圳市里程食品有限公司', password: 'szsf@zfjd' }, + { account: 'qintongzhen138', name: '欧姆龙电子部件(深圳)有限公司', password: 'szsf@zfjd' }, + { account: 'XP073215', name: '深圳市强达电路股份有限公司', password: 'szsf@zfjd' }, + { account: '33ef1970fd449676fa4bc197', name: '吉田拉链(深圳)有限公司公明工厂', password: 'szsf@zfjd' }, ]; const selectTestEnterprise = (item) => { diff --git a/zdxt-web-client/zdxt-enforcement-code-app/src/views/supervision/login.vue b/zdxt-web-client/zdxt-enforcement-code-app/src/views/supervision/login.vue index 562470d1..2e6433fb 100644 --- a/zdxt-web-client/zdxt-enforcement-code-app/src/views/supervision/login.vue +++ b/zdxt-web-client/zdxt-enforcement-code-app/src/views/supervision/login.vue @@ -1,11 +1,134 @@ + -