perf: 授权配置支持加密

原本已经添加的授权配置,再次编辑保存即变成加密配置
This commit is contained in:
xiaojunnuo
2024-08-27 13:46:19 +08:00
parent d6bb9f6af4
commit 42a56b581d
35 changed files with 338 additions and 80 deletions
@@ -3,9 +3,9 @@ import { logger } from '../../utils/logger.js';
import { UserService } from '../authority/service/user-service.js';
import { SysSettingsService } from '../system/service/sys-settings-service.js';
import { nanoid } from 'nanoid';
import { SysInstallInfo, SysLicenseInfo } from '../system/service/models.js';
import { SysInstallInfo, SysLicenseInfo, SysPrivateSettings } from '../system/service/models.js';
import { verify } from '@certd/pipeline';
import crypto from 'crypto';
export type InstallInfo = {
installTime: number;
instanceId?: string;
@@ -23,6 +23,7 @@ export class AutoInitSite {
@Init()
async init() {
logger.info('初始化站点开始');
//安装信息
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
if (!installInfo.siteId) {
installInfo.siteId = nanoid();
@@ -33,6 +34,19 @@ export class AutoInitSite {
await this.sysSettingsService.saveSetting(installInfo);
}
//private信息
const privateInfo = await this.sysSettingsService.getSetting<SysPrivateSettings>(SysPrivateSettings);
if (!privateInfo.jwtKey) {
privateInfo.jwtKey = nanoid();
await this.sysSettingsService.saveSetting(privateInfo);
}
if (!privateInfo.encryptSecret) {
const secretKey = crypto.randomBytes(32);
privateInfo.encryptSecret = secretKey.toString('base64');
await this.sysSettingsService.saveSetting(privateInfo);
}
// 授权许可
const licenseInfo: SysLicenseInfo = await this.sysSettingsService.getSetting(SysLicenseInfo);
const req = {
@@ -4,6 +4,8 @@ import jwt from 'jsonwebtoken';
import { CommonException } from '../../../basic/exception/common-exception.js';
import { RoleService } from '../../authority/service/role-service.js';
import { UserEntity } from '../../authority/entity/user.js';
import { SysSettingsService } from '../../system/service/sys-settings-service.js';
import { SysPrivateSettings } from '../../system/service/models.js';
/**
* 系统用户
@@ -17,6 +19,9 @@ export class LoginService {
@Config('auth.jwt')
private jwt: any;
@Inject()
sysSettingsService: SysSettingsService;
/**
* login
*/
@@ -47,7 +52,11 @@ export class LoginService {
roles: roleIds,
};
const expire = this.jwt.expire;
const token = jwt.sign(tokenInfo, this.jwt.secret, {
const setting = await this.sysSettingsService.getSetting<SysPrivateSettings>(SysPrivateSettings);
const jwtSecret = setting.jwtKey;
const token = jwt.sign(tokenInfo, jwtSecret, {
expiresIn: expire,
});
@@ -1,12 +1,4 @@
import {
ALL,
Body,
Controller,
Inject,
Post,
Provide,
Query,
} from '@midwayjs/core';
import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
import { CrudController } from '../../../basic/crud-controller.js';
import { AccessService } from '../service/access-service.js';
import { Constants } from '../../../basic/constants.js';
@@ -28,7 +20,7 @@ export class AccessController extends CrudController<AccessService> {
async page(@Body(ALL) body) {
body.query = body.query ?? {};
body.query.userId = this.ctx.user.id;
return super.page(body);
return await super.page(body);
}
@Post('/list', { summary: Constants.per.authOnly })
@@ -15,9 +15,12 @@ export class AccessEntity {
@Column({ comment: '类型', length: 100 })
type: string;
@Column({ name: 'setting', comment: '设置', length: 1024, nullable: true })
@Column({ name: 'setting', comment: '设置', length: 10240, nullable: true })
setting: string;
@Column({ name: 'encrypt_setting', comment: '已加密设置', length: 10240, nullable: true })
encryptSetting: string;
@Column({
name: 'create_time',
comment: '创建时间',
@@ -1,33 +1,127 @@
import { Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { BaseService } from '../../../basic/base-service.js';
import { AccessEntity } from '../entity/access.js';
import { accessRegistry, IAccessService } from '@certd/pipeline';
import { AccessDefine, accessRegistry, IAccessService } from '@certd/pipeline';
import { EncryptService } from './encrypt-service.js';
import { ValidateException } from '../../../basic/exception/validation-exception.js';
/**
* 授权
*/
@Provide()
@Scope(ScopeEnum.Singleton)
export class AccessService
extends BaseService<AccessEntity>
implements IAccessService
{
export class AccessService extends BaseService<AccessEntity> implements IAccessService {
@InjectEntityModel(AccessEntity)
repository: Repository<AccessEntity>;
@Inject()
encryptService: EncryptService;
getRepository() {
return this.repository;
}
async page(query, page = { offset: 0, limit: 20 }, order, buildQuery) {
const res = await super.page(query, page, order, buildQuery);
res.records = res.records.map(item => {
delete item.encryptSetting;
return item;
});
return res;
}
async add(param) {
this.encryptSetting(param, null);
return await super.add(param);
}
encryptSetting(param: any, oldSettingEntity?: AccessEntity) {
const accessType = param.type;
const accessDefine: AccessDefine = accessRegistry.getDefine(accessType);
if (!accessDefine) {
throw new ValidateException(`授权类型${accessType}不存在`);
}
const setting = param.setting;
if (!setting) {
return;
}
const json = JSON.parse(setting);
let oldSetting = {};
let encryptSetting = {};
const firstEncrypt = !oldSettingEntity.encryptSetting || oldSettingEntity.encryptSetting === '{}';
if (oldSettingEntity) {
oldSetting = JSON.parse(oldSettingEntity.setting || '{}');
encryptSetting = JSON.parse(oldSettingEntity.encryptSetting || '{}');
}
for (const key in json) {
//加密
const value = json[key];
const accessInputDefine = accessDefine.input[key];
if (!accessInputDefine) {
throw new ValidateException(`授权类型${accessType}不存在字段${key}`);
}
if (!accessInputDefine.encrypt || !value || typeof value !== 'string') {
//定义无需加密、value为空、不是字符串 这些不需要加密
encryptSetting[key] = {
value: value,
encrypt: false,
};
continue;
}
if (firstEncrypt || oldSetting[key] !== value) {
//星号保护
const length = value.length;
const subIndex = Math.min(2, length);
const starLength = length - subIndex * 2;
const starString = '*'.repeat(starLength);
json[key] = value.substring(0, subIndex) + starString + value.substring(value.length - subIndex);
encryptSetting[key] = {
value: this.encryptService.encrypt(value),
encrypt: true,
};
}
//未改变情况下,不做修改
}
param.encryptSetting = JSON.stringify(encryptSetting);
param.setting = JSON.stringify(json);
}
/**
* 修改
* @param param 数据
*/
async update(param) {
const oldEntity = await this.info(param.id);
if (oldEntity == null) {
throw new ValidateException('该授权配置不存在,请确认是否已被删除');
}
this.encryptSetting(param, oldEntity);
return await super.update(param);
}
async getById(id: any): Promise<any> {
const entity = await this.info(id);
if (entity == null) {
throw new Error(`该授权配置不存在,请确认是否已被删除:id=${id}`);
}
// const access = accessRegistry.get(entity.type);
const setting = JSON.parse(entity.setting);
let setting = {};
if (entity.encryptSetting && entity.encryptSetting !== '{}') {
setting = JSON.parse(entity.encryptSetting);
for (const key in setting) {
//解密
const encryptValue = setting[key];
let value = encryptValue.value;
if (encryptValue.encrypt) {
value = this.encryptService.decrypt(value);
}
setting[key] = value;
}
} else if (entity.setting) {
setting = JSON.parse(entity.setting);
}
return {
id: entity.id,
...setting,
@@ -1,7 +1,7 @@
import { Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { Provide } from '@midwayjs/core';
import { dnsProviderRegistry } from '@certd/plugin-cert';
@Provide()
@Scope(ScopeEnum.Singleton)
export class DnsProviderService {
getList() {
return dnsProviderRegistry.getDefineList();
@@ -0,0 +1,44 @@
import { Init, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import crypto from 'crypto';
import { SysSettingsService } from '../../system/service/sys-settings-service.js';
import { SysPrivateSettings } from '../../system/service/models.js';
/**
* 授权
*/
@Provide()
@Scope(ScopeEnum.Singleton)
export class EncryptService {
secretKey: Buffer;
@Inject()
sysSettingService: SysSettingsService;
@Init()
async init() {
const privateInfo: SysPrivateSettings = await this.sysSettingService.getSetting(SysPrivateSettings);
this.secretKey = Buffer.from(privateInfo.encryptSecret, 'base64');
}
// 加密函数
encrypt(text: string) {
const iv = crypto.randomBytes(16); // 初始化向量
// const secretKey = crypto.randomBytes(32);
// const key = Buffer.from(secretKey);
const cipher = crypto.createCipheriv('aes-256-cbc', this.secretKey, iv);
let encrypted = cipher.update(text);
encrypted = Buffer.concat([encrypted, cipher.final()]);
return iv.toString('hex') + ':' + encrypted.toString('hex');
}
// 解密函数
decrypt(encryptedText: string) {
const textParts = encryptedText.split(':');
const iv = Buffer.from(textParts.shift(), 'hex');
const encrypted = Buffer.from(textParts.join(':'), 'hex');
const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(this.secretKey), iv);
let decrypted = decipher.update(encrypted);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString();
}
}
@@ -19,6 +19,8 @@ export class SysPrivateSettings extends BaseSettings {
static __title__ = '系统私有设置';
static __access__ = 'private';
static __key__ = 'sys.private';
jwtKey?: string;
encryptSecret?: string;
}
export class SysInstallInfo extends BaseSettings {