feat : 新增sm4加密

This commit is contained in:
bin
2025-11-28 16:28:54 +08:00
parent 7c8a91eb2d
commit d154196321
7 changed files with 308 additions and 186 deletions

View File

@@ -1,147 +1,148 @@
{ {
"name": "ant-design-pro", "name": "ant-design-pro",
"version": "6.0.0", "version": "6.0.0",
"private": true, "private": true,
"description": "An out-of-box UI solution for enterprise applications", "description": "An out-of-box UI solution for enterprise applications",
"scripts": { "scripts": {
"dev": "npm run start:dev", "dev": "npm run start:dev",
"build": "max build", "build": "max build",
"deploy": "npm run build && npm run gh-pages", "deploy": "npm run build && npm run gh-pages",
"preview": "npm run build && max preview --port 8000", "preview": "npm run build && max preview --port 8000",
"serve": "umi-serve", "serve": "umi-serve",
"start": "cross-env UMI_ENV=dev max dev", "start": "cross-env UMI_ENV=dev max dev",
"start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev max dev", "start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev max dev",
"start:no-mock": "cross-env MOCK=none UMI_ENV=dev max dev", "start:no-mock": "cross-env MOCK=none UMI_ENV=dev max dev",
"start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev max dev", "start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev max dev",
"start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev max dev", "start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev max dev",
"test": "jest", "test": "jest",
"test:coverage": "npm run jest -- --coverage", "test:coverage": "npm run jest -- --coverage",
"test:update": "npm run jest -- -u", "test:update": "npm run jest -- -u",
"docker-hub:build": "docker build -f Dockerfile.hub -t ant-design-pro ./", "docker-hub:build": "docker build -f Dockerfile.hub -t ant-design-pro ./",
"docker-prod:build": "docker-compose -f ./docker/docker-compose.yml build", "docker-prod:build": "docker-compose -f ./docker/docker-compose.yml build",
"docker-prod:dev": "docker-compose -f ./docker/docker-compose.yml up", "docker-prod:dev": "docker-compose -f ./docker/docker-compose.yml up",
"docker:build": "docker-compose -f ./docker/docker-compose.dev.yml build", "docker:build": "docker-compose -f ./docker/docker-compose.dev.yml build",
"docker:dev": "docker-compose -f ./docker/docker-compose.dev.yml up", "docker:dev": "docker-compose -f ./docker/docker-compose.dev.yml up",
"docker:push": "npm run docker-hub:build && npm run docker:tag && docker push antdesign/ant-design-pro", "docker:push": "npm run docker-hub:build && npm run docker:tag && docker push antdesign/ant-design-pro",
"docker:tag": "docker tag ant-design-pro antdesign/ant-design-pro", "docker:tag": "docker tag ant-design-pro antdesign/ant-design-pro",
"analyze": "cross-env ANALYZE=1 max build", "analyze": "cross-env ANALYZE=1 max build",
"gh-pages": "gh-pages -d dist", "gh-pages": "gh-pages -d dist",
"i18n-remove": "pro i18n-remove --locale=zh-CN --write", "i18n-remove": "pro i18n-remove --locale=zh-CN --write",
"postinstall": "max setup", "postinstall": "max setup",
"jest": "jest", "jest": "jest",
"lint": "npm run lint:js && npm run lint:prettier && npm run tsc", "lint": "npm run lint:js && npm run lint:prettier && npm run tsc",
"lint-staged": "lint-staged", "lint-staged": "lint-staged",
"lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ", "lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ",
"lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src ", "lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src ",
"lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src", "lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src",
"lint:prettier": "prettier -c --write \"**/**.{js,jsx,tsx,ts,less,md,json}\" --end-of-line auto", "lint:prettier": "prettier -c --write \"**/**.{js,jsx,tsx,ts,less,md,json}\" --end-of-line auto",
"openapi": "max openapi", "openapi": "max openapi",
"prepare": "cd .. && husky install", "prepare": "cd .. && husky install",
"prettier": "prettier -c --write \"**/**.{js,jsx,tsx,ts,less,md,json}\"", "prettier": "prettier -c --write \"**/**.{js,jsx,tsx,ts,less,md,json}\"",
"tsc": "tsc --noEmit", "tsc": "tsc --noEmit",
"record": "cross-env NODE_ENV=development REACT_APP_ENV=test max record --scene=login" "record": "cross-env NODE_ENV=development REACT_APP_ENV=test max record --scene=login"
}, },
"lint-staged": { "lint-staged": {
"**/*.{js,jsx,ts,tsx}": "npm run lint-staged:js", "**/*.{js,jsx,ts,tsx}": "npm run lint-staged:js",
"**/*.{js,jsx,tsx,ts,less,md,json}": [ "**/*.{js,jsx,tsx,ts,less,md,json}": [
"prettier --write" "prettier --write"
] ]
}, },
"browserslist": [ "browserslist": [
"> 1%", "> 1%",
"last 2 versions", "last 2 versions",
"not ie <= 10" "not ie <= 10"
], ],
"dependencies": { "dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1", "@amap/amap-jsapi-loader": "^1.0.1",
"@ant-design/charts": "^2.3.0", "@ant-design/charts": "^2.3.0",
"@ant-design/icons": "^5.5.0", "@ant-design/icons": "^5.5.0",
"@ant-design/maps": "^1.0.0", "@ant-design/maps": "^1.0.0",
"@ant-design/plots": "^2.3.2", "@ant-design/plots": "^2.3.2",
"@ant-design/pro-components": "^2.8.7", "@ant-design/pro-components": "^2.8.7",
"@ant-design/use-emotion-css": "1.0.4", "@ant-design/use-emotion-css": "1.0.4",
"@testing-library/dom": "^10.4.0", "@testing-library/dom": "^10.4.0",
"@umijs/route-utils": "^4.0.1", "@umijs/route-utils": "^4.0.1",
"ant-design-pro": "file:", "ant-design-pro": "file:",
"antd": "^5.21.1", "antd": "^5.21.1",
"antd-style": "^3.6.2", "antd-style": "^3.6.2",
"classnames": "^2.5.1", "classnames": "^2.5.1",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"echarts": "^5.6.0", "echarts": "^5.6.0",
"fabric": "^6.4.0", "fabric": "^6.4.0",
"highlight.js": "^11.10.0", "highlight.js": "^11.10.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"moment": "^2.30.1", "moment": "^2.30.1",
"omit.js": "^2.0.2", "omit.js": "^2.0.2",
"query-string": "^9.1.0", "query-string": "^9.1.0",
"rc-menu": "^9.15.0", "rc-menu": "^9.15.0",
"rc-util": "^5.43.0", "rc-util": "^5.43.0",
"react": "^18.3.0", "react": "^18.3.0",
"react-cropper": "^2.3.3", "react-cropper": "^2.3.3",
"react-dev-inspector": "^2.0.1", "react-dev-inspector": "^2.0.1",
"react-dom": "^18.3.0", "react-dom": "^18.3.0",
"react-helmet-async": "^2.0.0", "react-helmet-async": "^2.0.0",
"react-highlight": "^0.15.0" "react-highlight": "^0.15.0",
}, "sm-crypto": "^0.3.13"
"devDependencies": { },
"@ant-design/pro-cli": "^3.3.0", "devDependencies": {
"@testing-library/react": "^16.0.1", "@ant-design/pro-cli": "^3.3.0",
"@types/classnames": "^2.3.1", "@testing-library/react": "^16.0.1",
"@types/express": "^4.17.21", "@types/classnames": "^2.3.1",
"@types/history": "^4.7.11", "@types/express": "^4.17.21",
"@types/jest": "^29.5.12", "@types/history": "^4.7.11",
"@types/lodash": "^4.17.4", "@types/jest": "^29.5.12",
"@types/react": "^18.3.0", "@types/lodash": "^4.17.4",
"@types/react-dom": "^18.3.0", "@types/react": "^18.3.0",
"@types/react-helmet": "^6.1.11", "@types/react-dom": "^18.3.0",
"@umijs/fabric": "^2.14.1", "@types/react-helmet": "^6.1.11",
"@umijs/lint": "^4.2.9", "@umijs/fabric": "^2.14.1",
"@umijs/max": "^4.2.9", "@umijs/lint": "^4.2.9",
"cross-env": "^7.0.3", "@umijs/max": "^4.2.9",
"eslint": "^9.11.0", "cross-env": "^7.0.3",
"express": "^4.21.0", "eslint": "^9.11.0",
"gh-pages": "^6.1.0", "express": "^4.21.0",
"husky": "^9.1.3", "gh-pages": "^6.1.0",
"jest": "^29.7.0", "husky": "^9.1.3",
"jest-environment-jsdom": "^29.7.0", "jest": "^29.7.0",
"lint-staged": "^15.2.0", "jest-environment-jsdom": "^29.7.0",
"mockjs": "^1.1.0", "lint-staged": "^15.2.0",
"prettier": "^3.3.0", "mockjs": "^1.1.0",
"swagger-ui-dist": "^5.17.14", "prettier": "^3.3.0",
"ts-node": "^10.9.1", "swagger-ui-dist": "^5.17.14",
"typescript": "^5.6.2", "ts-node": "^10.9.1",
"umi-presets-pro": "^2.0.0" "typescript": "^5.6.2",
}, "umi-presets-pro": "^2.0.0"
"engines": { },
"node": ">=12.0.0" "engines": {
}, "node": ">=12.0.0"
"create-umi": { },
"ignoreScript": [ "create-umi": {
"docker*", "ignoreScript": [
"functions*", "docker*",
"site", "functions*",
"generateMock" "site",
], "generateMock"
"ignoreDependencies": [ ],
"netlify*", "ignoreDependencies": [
"serverless" "netlify*",
], "serverless"
"ignore": [ ],
".dockerignore", "ignore": [
".git", ".dockerignore",
".github", ".git",
".gitpod.yml", ".github",
"CODE_OF_CONDUCT.md", ".gitpod.yml",
"Dockerfile", "CODE_OF_CONDUCT.md",
"Dockerfile.*", "Dockerfile",
"lambda", "Dockerfile.*",
"LICENSE", "lambda",
"netlify.toml", "LICENSE",
"README.*.md", "netlify.toml",
"azure-pipelines.yml", "README.*.md",
"docker", "azure-pipelines.yml",
"CNAME", "docker",
"create-umi" "CNAME",
] "create-umi"
} ]
} }
}

View File

@@ -17,6 +17,7 @@ import {
import { PageEnum } from './enums/pagesEnums'; import { PageEnum } from './enums/pagesEnums';
import { stringify } from 'querystring'; import { stringify } from 'querystring';
import { message } from 'antd'; import { message } from 'antd';
import { encrypt, decrypt, needEncrypt } from '@/utils/encrypt';
const isDev = process.env.NODE_ENV === 'development'; const isDev = process.env.NODE_ENV === 'development';
const loginOut = async () => { const loginOut = async () => {
@@ -217,18 +218,20 @@ export async function render(oldRender: () => void) {
const checkRegion = 5 * 60 * 1000; const checkRegion = 5 * 60 * 1000;
export const request = { export const request = {
...errorConfig, ...errorConfig,
// baseURL: process.env.NODE_ENV === 'development' ? '' : 'https://qd.zhaopinzao8dian.com/api', baseURL: process.env.NODE_ENV === 'development' ? '' : 'https://qd.zhaopinzao8dian.com/api',
// baseURL: 'http://39.98.44.136:8080', // baseURL: 'http://39.98.44.136:8080',
baseURL: // baseURL:
process.env.NODE_ENV === 'development' // process.env.NODE_ENV === 'development'
? 'http://10.213.6.207:19010' // ? 'http://10.213.6.207:19010'
: 'http://10.213.6.207:19010/api', // : 'http://10.213.6.207:19010/api',
requestInterceptors: [ requestInterceptors: [
(url: any, options: { headers: any }) => { (url: any, options: { headers: any; data?: any; params?: any; method?: string }) => {
const headers = options.headers ? options.headers : []; const headers = options.headers ? options.headers : {};
console.log('request ====>:', url); console.log('request ====>:', url);
const authHeader = headers['Authorization']; const authHeader = headers['Authorization'];
const isToken = headers['isToken']; const isToken = headers['isToken'];
// 处理认证token
if (!authHeader && isToken !== false) { if (!authHeader && isToken !== false) {
const expireTime = getTokenExpireTime(); const expireTime = getTokenExpireTime();
if (expireTime) { if (expireTime) {
@@ -248,18 +251,82 @@ export const request = {
clearSessionToken(); clearSessionToken();
} }
} }
// 处理SM4加密 - 根据config的isEncrypt来判断
if (needEncrypt(options)) {
console.log('进行SM4加密处理');
let requestData = options.data;
let requestParams = options.params;
// 加密请求数据
if (requestData && Object.keys(requestData).length > 0) {
const jsonData = JSON.stringify(requestData);
const encryptedBody = encrypt(jsonData);
requestData = {
encrypted: true,
encryptedData: encryptedBody,
timestamp: Date.now(),
};
}
// 加密查询参数
if (requestParams && Object.keys(requestParams).length > 0) {
const jsonParams = JSON.stringify(requestParams);
const encryptedParams = encrypt(jsonParams);
requestParams = {
encrypted: true,
encryptedData: encryptedParams,
timestamp: Date.now(),
};
}
// 添加加密标识头
headers['X-Encrypted'] = 'true';
return {
url,
options: {
...options,
headers,
data: requestData,
params: requestParams,
},
};
}
// 处理开发环境API路径
if (process.env.NODE_ENV !== 'development') { if (process.env.NODE_ENV !== 'development') {
if (url.startsWith('/api')) { if (url.startsWith('/api')) {
url = url.replace(/^\/api/, ''); url = url.replace(/^\/api/, '');
} }
} }
return { url, options };
return { url, options: { ...options, headers } };
}, },
], ],
responseInterceptors: [ responseInterceptors: [
(response) => { (response) => {
// 不再需要异步处理读取返回体内容可直接在data中读出部分字段可在 config 中找到 // 不再需要异步处理读取返回体内容可直接在data中读出部分字段可在 config 中找到
const { data = {} as any, config } = response; const { data = {} as any, config } = response;
// 检查是否需要解密
const isEncrypted = data.encrypted;
if (isEncrypted && data.encryptedData) {
console.log('进行SM4解密处理');
try {
// 解密响应数据
const decryptedData = decrypt(data.encryptedData);
response.data =
typeof decryptedData === 'string' ? JSON.parse(decryptedData) : decryptedData;
} catch (error) {
console.error('响应解密失败:', error);
// 如果解密失败,保持原始数据
}
}
// 处理业务状态码
switch (data.code) { switch (data.code) {
case 401: case 401:
loginOut(); loginOut();
@@ -268,8 +335,7 @@ export const request = {
if (data.code !== 200 && data.msg) { if (data.code !== 200 && data.msg) {
message.info(data.msg); message.info(data.msg);
} }
// console.log('data: ', data)
// console.log('config: ', config)
return response; return response;
}, },
], ],

View File

@@ -5,6 +5,7 @@ export async function getCmsAppUserList(params?: API.MobileUser.ListParams) {
return request<API.MobileUser.ListResult>(`/api/cms/appUser/list`, { return request<API.MobileUser.ListResult>(`/api/cms/appUser/list`, {
method: 'GET', method: 'GET',
params: params, params: params,
isEncrypt: true,
}); });
} }

View File

@@ -4,11 +4,13 @@ export async function getResumeList(params?: API.AppUser.ListParams) {
return request<API.AppUser.ResumePageResult>(`/api/cms/appUser/getResumeList`, { return request<API.AppUser.ResumePageResult>(`/api/cms/appUser/getResumeList`, {
method: 'GET', method: 'GET',
params: params, params: params,
isEncrypt: true,
}); });
} }
export async function getResumeDetail(userId: string) { export async function getResumeDetail(userId: string) {
return request<API.AppUser.ResumeDetailResult>(`/api/cms/appUser/getResumeDetail/${userId}`, { return request<API.AppUser.ResumeDetailResult>(`/api/cms/appUser/getResumeDetail/${userId}`, {
method: 'GET', method: 'GET',
isEncrypt: true,
}); });
} }

View File

@@ -22,6 +22,7 @@ export async function login(body: API.LoginParams, options?: Record<string, any>
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
data: body, data: body,
isEncrypt: true,
...(options || {}), ...(options || {}),
}); });
} }

View File

@@ -4,14 +4,18 @@ import { DataNode } from 'antd/es/tree';
import { downLoadXlsx } from '@/utils/downloadfile'; import { downLoadXlsx } from '@/utils/downloadfile';
// 查询用户信息列表 // 查询用户信息列表
export async function getUserList(params?: API.System.UserListParams, options?: { [key: string]: any }) { export async function getUserList(
params?: API.System.UserListParams,
options?: { [key: string]: any },
) {
return request<API.System.UserPageResult>('/api/system/user/list', { return request<API.System.UserPageResult>('/api/system/user/list', {
method: 'GET', method: 'GET',
headers: { headers: {
'Content-Type': 'application/json;charset=UTF-8', 'Content-Type': 'application/json;charset=UTF-8',
}, },
params, params,
...(options || {}) isEncrypt: true,
...(options || {}),
}); });
} }
@@ -19,7 +23,7 @@ export async function getUserList(params?: API.System.UserListParams, options?:
export function getUser(userId: number, options?: { [key: string]: any }) { export function getUser(userId: number, options?: { [key: string]: any }) {
return request<API.System.UserInfoResult>(`/api/system/user/${userId}`, { return request<API.System.UserInfoResult>(`/api/system/user/${userId}`, {
method: 'GET', method: 'GET',
...(options || {}) ...(options || {}),
}); });
} }
@@ -31,7 +35,8 @@ export async function addUser(params: API.System.User, options?: { [key: string]
'Content-Type': 'application/json;charset=UTF-8', 'Content-Type': 'application/json;charset=UTF-8',
}, },
data: params, data: params,
...(options || {}) isEncrypt: true,
...(options || {}),
}); });
} }
@@ -43,7 +48,7 @@ export async function updateUser(params: API.System.User, options?: { [key: stri
'Content-Type': 'application/json;charset=UTF-8', 'Content-Type': 'application/json;charset=UTF-8',
}, },
data: params, data: params,
...(options || {}) ...(options || {}),
}); });
} }
@@ -51,7 +56,7 @@ export async function updateUser(params: API.System.User, options?: { [key: stri
export async function removeUser(ids: string, options?: { [key: string]: any }) { export async function removeUser(ids: string, options?: { [key: string]: any }) {
return request<API.Result>(`/api/system/user/${ids}`, { return request<API.Result>(`/api/system/user/${ids}`, {
method: 'DELETE', method: 'DELETE',
...(options || {}) ...(options || {}),
}); });
} }
@@ -64,74 +69,73 @@ export function exportUser(params?: API.System.UserListParams, options?: { [key:
export function changeUserStatus(userId: number, status: string) { export function changeUserStatus(userId: number, status: string) {
const data = { const data = {
userId, userId,
status status,
} };
return request<API.Result>('/api/system/user/changeStatus', { return request<API.Result>('/api/system/user/changeStatus', {
method: 'put', method: 'put',
data: data data: data,
}) });
} }
// 查询用户个人信息 // 查询用户个人信息
export function getUserProfile() { export function getUserProfile() {
return request('/api/system/user/profile', { return request('/api/system/user/profile', {
method: 'get' method: 'get',
}) });
} }
export function updateUserProfile(data: API.CurrentUser) { export function updateUserProfile(data: API.CurrentUser) {
return request<API.Result>('/api/system/user/profile', { return request<API.Result>('/api/system/user/profile', {
method: 'put', method: 'put',
data: data data: data,
}) });
} }
// 用户密码重置 // 用户密码重置
export function resetUserPwd(userId: number, password: string) { export function resetUserPwd(userId: number, password: string) {
const data = { const data = {
userId, userId,
password password,
} };
return request<API.Result>('/api/system/user/resetPwd', { return request<API.Result>('/api/system/user/resetPwd', {
method: 'put', method: 'put',
data: data data: data,
}) });
} }
// 用户t个人密码重置 // 用户t个人密码重置
export function updateUserPwd(oldPassword: string, newPassword: string) { export function updateUserPwd(oldPassword: string, newPassword: string) {
const data = { const data = {
oldPassword, oldPassword,
newPassword newPassword,
} };
return request<API.Result>('/api/system/user/profile/updatePwd', { return request<API.Result>('/api/system/user/profile/updatePwd', {
method: 'put', method: 'put',
params: data params: data,
}) });
} }
// 用户头像上传 // 用户头像上传
export function uploadAvatar(data: any) { export function uploadAvatar(data: any) {
return request('/api/system/user/profile/avatar', { return request('/api/system/user/profile/avatar', {
method: 'post', method: 'post',
data: data data: data,
}) });
} }
// 查询授权角色 // 查询授权角色
export function getAuthRole(userId: number) { export function getAuthRole(userId: number) {
return request('/system/user/authRole/' + userId, { return request('/system/user/authRole/' + userId, {
method: 'get' method: 'get',
}) });
} }
// 保存授权角色 // 保存授权角色
export function updateAuthRole(data: Record<string, any>) { export function updateAuthRole(data: Record<string, any>) {
return request('/system/user/authRole', { return request('/system/user/authRole', {
method: 'put', method: 'put',
params: data params: data,
}) });
} }
// 获取数据列表 // 获取数据列表

47
src/utils/encrypt.ts Normal file
View File

@@ -0,0 +1,47 @@
import { sm4 } from 'sm-crypto';
const pwdKey = '86C63180C1306ABC4D8F989E0A0BC9F3'; // 32位十六进制密钥与移动端一致
/**
* 加密文本
* @param text 待加密文本
*/
export function encrypt(text: any): string {
if (typeof text !== 'string') {
text = JSON.stringify(text);
}
return sm4.encrypt(text, pwdKey, {
output: 'string',
padding: 'pkcs#5',
});
}
/**
* 解密密文
* @param text 待解密密文
*/
export function decrypt(text: string): any {
try {
const decrypted = sm4.decrypt(text, pwdKey, {
output: 'string',
padding: 'pkcs#5',
});
// 尝试解析为JSON如果不是JSON则直接返回字符串
try {
return JSON.parse(decrypted);
} catch {
return decrypted;
}
} catch (error) {
console.error('SM4解密失败:', error);
return text; // 解密失败返回原文本
}
}
/**
* 判断是否需要加密处理
*/
export function needEncrypt(options: any): boolean {
return options?.isEncrypt === true;
}