mirror of https://github.com/certd/certd
107 lines
3.3 KiB
TypeScript
107 lines
3.3 KiB
TypeScript
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;
|
||
};
|
||
@Provide()
|
||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||
export class OpenKeyService extends BaseService<OpenKeyEntity> {
|
||
@InjectEntityModel(OpenKeyEntity)
|
||
repository: Repository<OpenKeyEntity>;
|
||
|
||
//@ts-ignore
|
||
getRepository() {
|
||
return this.repository;
|
||
}
|
||
|
||
async page(pageReq: PageReq<OpenKeyEntity>) {
|
||
return await super.page(pageReq);
|
||
}
|
||
|
||
async add(bean: OpenKeyEntity) {
|
||
return await this.generate(bean.userId);
|
||
}
|
||
|
||
async generate(userId: number) {
|
||
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;
|
||
await this.repository.save(entity);
|
||
return entity;
|
||
}
|
||
|
||
async getByKeyId(keyId: string) {
|
||
return this.repository.findOne({ where: { keyId } });
|
||
}
|
||
|
||
async verifyOpenKey(openKey: string): Promise<OpenKey> {
|
||
// 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,
|
||
};
|
||
}
|
||
|
||
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');
|
||
}
|
||
}
|