import { Provide, Scope, ScopeEnum } from '@midwayjs/core'; import { BaseService, Constants, CodeException, PageReq } from '@certd/lib-server'; import { InjectEntityModel } from '@midwayjs/typeorm'; import { Repository } from 'typeorm'; import { OpenKeyEntity } from '../entity/open-key.js'; import { utils } from '@certd/basic'; import crypto from 'crypto'; import dayjs from 'dayjs'; export type OpenKey = { userId: number; keyId: string; keySecret: string; encrypt: boolean; scope: string; }; @Provide() @Scope(ScopeEnum.Request, { allowDowngrade: true }) export class OpenKeyService extends BaseService { @InjectEntityModel(OpenKeyEntity) repository: Repository; //@ts-ignore getRepository() { return this.repository; } async page(pageReq: PageReq) { return await super.page(pageReq); } async add(bean: OpenKeyEntity) { return await this.generate(bean.userId, bean.scope); } async generate(userId: number, scope: string) { const keyId = utils.id.simpleNanoId(18) + '_key'; const secretKey = crypto.randomBytes(32); const keySecret = Buffer.from(secretKey).toString('hex'); const entity = new OpenKeyEntity(); entity.userId = userId; entity.keyId = keyId; entity.keySecret = keySecret; entity.scope = scope ?? 'open'; await this.repository.save(entity); return entity; } async getByKeyId(keyId: string) { return this.repository.findOne({ where: { keyId } }); } async verifyOpenKey(openKey: string): Promise { // openkey 组成,content = base64({keyId,t,encrypt,signType}) ,sign = md5({keyId,t,encrypt,signType}secret) , key = content.sign const [content, sign] = openKey.split('.'); const contentJson = Buffer.from(content, 'base64').toString(); const { keyId, t, encrypt, signType } = JSON.parse(contentJson); // 正负不超过3分钟 ,timestamps单位为秒 if (Math.abs(Number(t) - Math.floor(Date.now() / 1000)) > 180) { throw new CodeException(Constants.res.openKeyExpiresError); } const entity = await this.getByKeyId(keyId); if (!entity) { throw new Error('openKey不存在'); } const secret = entity.keySecret; let computedSign = ''; if (signType === 'md5') { computedSign = utils.hash.md5(contentJson + secret); } else if (signType === 'sha256') { computedSign = utils.hash.sha256(contentJson + secret); } else { throw new CodeException(Constants.res.openKeySignTypeError); } if (Buffer.from(computedSign).toString('base64') !== sign) { throw new CodeException(Constants.res.openKeySignError); } if (!entity.userId) { throw new CodeException(Constants.res.openKeyError); } return { userId: entity.userId, keyId: entity.keyId, keySecret: entity.keySecret, encrypt: encrypt, scope: entity.scope, }; } async getApiToken(id: number) { const entity = await this.repository.findOne({ where: { id } }); if (!entity) { throw new Error('id不存在'); } const { keyId, keySecret } = entity; const openKey = { keyId, t: dayjs().unix(), encrypt: false, signType: 'md5', }; const content = JSON.stringify(openKey); const sign = utils.hash.md5(content + keySecret); return Buffer.from(content).toString('base64') + '.' + Buffer.from(sign).toString('base64'); } }