add jwt
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
import type { LicenseInfo, LicenseCheckResult } from '@/types/license';
|
||||
import i18n from '@/i18n';
|
||||
import { LicenseStorage } from '@/utils/LicenseStorage';
|
||||
import { LicenseStorage } from '@/utils/license-storage';
|
||||
import { PermissionService } from '@/utils/permissions';
|
||||
import { Notification } from 'kintone-ui-component/lib/notification';
|
||||
import { MobileNotification } from 'kintone-ui-component/lib/mobile/notification';
|
||||
import manifestJson from '@/manifest.json';
|
||||
import { KJUR } from 'jsrsasign';
|
||||
import rsaPublicKey from '../../rsa_public.pem?raw';
|
||||
import rsaPrivateKey from '../../rsa_private.pem?raw';
|
||||
|
||||
const { t: $t } = i18n.global;
|
||||
|
||||
@@ -22,6 +25,16 @@ export class LicenseService {
|
||||
return window.location.hostname;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件名
|
||||
*/
|
||||
static getPluginName(): string {
|
||||
const pluginName = manifestJson.name as Record<string, string>;
|
||||
const userLang = kintone.getLoginUser().language;
|
||||
const plugin = pluginName[userLang] || pluginName.ja;
|
||||
return plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件ID
|
||||
*/
|
||||
@@ -31,37 +44,50 @@ export class LicenseService {
|
||||
|
||||
/**
|
||||
* 检查许可证是否有效
|
||||
* 已付费/当日获取是 true
|
||||
* 参数是解码后的JWT结构,验证签名和payload进行原有检查
|
||||
*/
|
||||
static checkLicenseAvailable(license: LicenseInfo) {
|
||||
const domain = this.getDomain()
|
||||
const pluginId = this.getPluginId()
|
||||
|
||||
// TODO jwt null
|
||||
|
||||
// 检查域名和插件ID是否与当前环境一致
|
||||
if (license.domain !== domain || license.pluginId !== pluginId) {
|
||||
static checkLicenseAvailable(jwtString: string, decodedJWT: KJUR.jws.JWS.JWSResult): boolean {
|
||||
try {
|
||||
// 验证完整的JWT
|
||||
const result = KJUR.jws.JWS.verifyJWT(jwtString, rsaPublicKey.trim(), {alg: ['RS256']});
|
||||
if (!result) {
|
||||
console.warn('JWT signature verification failed');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否付费
|
||||
if (license.isPaid) {
|
||||
return true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('JWT verification or parsing failed:', error);
|
||||
return false;
|
||||
}
|
||||
// JWT验证通过,从payloadPP解析license进行原有检查
|
||||
const license: LicenseInfo = JSON.parse(decodedJWT.payloadPP);
|
||||
|
||||
// 检查存储是否过期(是否同一天)
|
||||
if (!this.isToday(license.fetchTime)) {
|
||||
// 不是同一天,已过期
|
||||
return false;
|
||||
}
|
||||
const domain = this.getDomain()
|
||||
const pluginId = this.getPluginId()
|
||||
|
||||
// 检查试用是否到期
|
||||
const expiredTime = new Date(license.expiredTime);
|
||||
if (expiredTime < new Date()) {
|
||||
return false;
|
||||
}
|
||||
// 检查域名和插件ID是否与当前环境一致
|
||||
if (license.domain !== domain || license.pluginId !== pluginId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true
|
||||
// 检查是否付费
|
||||
if (license.isPaid) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查存储是否过期(是否同一天)
|
||||
if (!this.isToday(license.fetchTime)) {
|
||||
// 不是同一天,已过期
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查试用是否到期
|
||||
const expiredTime = new Date(license.expiredTime);
|
||||
if (expiredTime < new Date()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static isToday(timestamp: number): boolean {
|
||||
@@ -89,14 +115,29 @@ export class LicenseService {
|
||||
*/
|
||||
static async checkLicenseRemote(): Promise<LicenseCheckResult> {
|
||||
try {
|
||||
// 这里应该是实际的API调用,暂时模拟创建试用许可证
|
||||
// 这里应该是实际的API调用,暂时模拟创建加密的试用许可证
|
||||
const response = await this.mockRemoteCheck(this.getDomain(), this.getPluginId());
|
||||
|
||||
const license = response.license!;
|
||||
LicenseStorage.saveLicense(license);
|
||||
if (!response.success || !response.jwt) {
|
||||
return {
|
||||
isLicenseValid: false,
|
||||
isRemote: true,
|
||||
};
|
||||
}
|
||||
|
||||
const jwt = response.jwt;
|
||||
|
||||
// 保存 JWT 到本地存储,获取解码后的结构
|
||||
const decodedJWT = LicenseStorage.saveLicense(jwt);
|
||||
|
||||
// 从解码后的JWT提取许可证信息
|
||||
const license: LicenseInfo = JSON.parse(decodedJWT.payloadPP);
|
||||
|
||||
// 使用解码后的JWT进行验证
|
||||
const isValid = this.checkLicenseAvailable(jwt, decodedJWT);
|
||||
|
||||
return {
|
||||
isLicenseValid: this.checkLicenseAvailable(license),
|
||||
isLicenseValid: isValid,
|
||||
license,
|
||||
isRemote: true,
|
||||
};
|
||||
@@ -104,7 +145,8 @@ export class LicenseService {
|
||||
} catch (error) {
|
||||
console.error($t('license.error.fetchFailed', { e: error }));
|
||||
return {
|
||||
isLicenseValid: true,
|
||||
isLicenseValid: false,
|
||||
isRemote: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -146,7 +188,7 @@ export class LicenseService {
|
||||
*/
|
||||
static async showNotification(license: LicenseInfo | undefined, isWarning: boolean) {
|
||||
let message;
|
||||
const plugin = manifestJson.name[kintone.getLoginUser().language];
|
||||
const plugin = this.getPluginName();
|
||||
if (isWarning) {
|
||||
// 尚未到期
|
||||
const remainingDays = this.getDaysRemaining(license!.expiredTime);
|
||||
@@ -228,7 +270,15 @@ export class LicenseService {
|
||||
* 获取许可证信息(用于显示)
|
||||
*/
|
||||
static getLocalLicenseInfo(): LicenseInfo | null {
|
||||
return LicenseStorage.getLicense(this.getPluginId());
|
||||
const decodedJWT = LicenseStorage.getLicense(this.getPluginId());
|
||||
if (!decodedJWT) return null;
|
||||
|
||||
try {
|
||||
return JSON.parse(decodedJWT.payloadPP) as LicenseInfo;
|
||||
} catch (error) {
|
||||
console.error('Failed to parse payloadPP from decoded JWT:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,14 +310,36 @@ export class LicenseService {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成JWT token(内部方法)
|
||||
*/
|
||||
private static generateJWT(licenseInfo: LicenseInfo): string {
|
||||
const header = {
|
||||
alg: 'RS256',
|
||||
typ: 'JWT'
|
||||
};
|
||||
|
||||
const payload = {
|
||||
...licenseInfo,
|
||||
iat: Math.floor(Date.now() / 1000)
|
||||
};
|
||||
|
||||
const sHeader = JSON.stringify(header);
|
||||
const sPayload = JSON.stringify(payload);
|
||||
|
||||
const jwt = KJUR.jws.JWS.sign(null, sHeader, sPayload, rsaPrivateKey.trim());
|
||||
return jwt;
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟远程验证(生产环境中会被真实API替换)
|
||||
*/
|
||||
private static async mockRemoteCheck(domain: string, pluginId: string): Promise<{ success: boolean; license?: LicenseInfo }> {
|
||||
// 模拟API调用,这里总是返回试用许可证
|
||||
private static async mockRemoteCheck(domain: string, pluginId: string): Promise<{ success: boolean; jwt?: string }> {
|
||||
// 模拟API调用,这里总是返回加密的试用许可证
|
||||
// 生产环境这里会调用后端API: POST /api/license/check
|
||||
|
||||
const license = this.mockCreateTrialLicense();
|
||||
const jwt = this.generateJWT(license);
|
||||
|
||||
// 模拟有时创建失败的情况(1%概率)
|
||||
if (Math.random() < 0.01) {
|
||||
@@ -276,7 +348,7 @@ export class LicenseService {
|
||||
|
||||
return {
|
||||
success: true,
|
||||
license,
|
||||
jwt,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user