add jwt
This commit is contained in:
@@ -13,6 +13,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kintone/rest-api-client": "^5.7.5",
|
"@kintone/rest-api-client": "^5.7.5",
|
||||||
|
"jsrsasign": "^11.1.0",
|
||||||
|
"jsrsasign-util": "^1.0.5",
|
||||||
"kintone-ui-component": "1.22.0",
|
"kintone-ui-component": "1.22.0",
|
||||||
"rollup-plugin-css-only": "^4.5.2",
|
"rollup-plugin-css-only": "^4.5.2",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
@@ -23,6 +25,7 @@
|
|||||||
"@kintone/dts-gen": "^8.1.1",
|
"@kintone/dts-gen": "^8.1.1",
|
||||||
"@kintone/plugin-packer": "^8.1.3",
|
"@kintone/plugin-packer": "^8.1.3",
|
||||||
"@kintone/plugin-uploader": "^9.1.2",
|
"@kintone/plugin-uploader": "^9.1.2",
|
||||||
|
"@types/jsrsasign": "^10.5.15",
|
||||||
"@types/node-rsa": "^1.1.4",
|
"@types/node-rsa": "^1.1.4",
|
||||||
"@vitejs/plugin-vue": "^5.2.1",
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
"@vue/tsconfig": "^0.7.0",
|
"@vue/tsconfig": "^0.7.0",
|
||||||
|
|||||||
28
rsa_private.pem
Normal file
28
rsa_private.pem
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDOAqDRabJn6vga
|
||||||
|
YXnWuYKPomQVNV0R0HPmuN1N4qo7OMDkFH2mln86w68/9/bHMb7TcVaFGxNblpe2
|
||||||
|
tIjr/u/XfnS2+NK6ckTWOhmPUrqLG6A3a4v49dymvFdfyjAYOCBTlKzrdQTjdFAX
|
||||||
|
5EUE007n/G+RxYiEAm0NXpVqa+aw/Yvm5+Lr7GrKpyamAAygu5jYDE3rateyfJ1h
|
||||||
|
P2fEXEqR4RPD5pgyKD+4ZVbygaYqAw4rZu4OOZ4QtPCtksSenn94Yx9UUlex3TNW
|
||||||
|
oTBrFp+8z9wjg+oF6/5Ai/PsXLDpbkIusnQo1yYvrULwaAHBvL6wgj1FOL+g6Tmp
|
||||||
|
P6MhU7EbAgMBAAECggEAAyia0vXQv0jPgAa26a9ozI9RWIU7xZOmy0anV1Peh0+X
|
||||||
|
SYf05efQez6tLQkTbA9xB73o9TEqlVC+8mqx32hxFffsOIf3zIBcWiqEN3nYaVya
|
||||||
|
6BlKiUkqa8C0guqkh80zKwTlt9XQpYokK5GblfeFcgd2pctbt1Ea30XFewm80kGH
|
||||||
|
OrHP4bi25KYkS+6PL5wUI+OqfaR/LsWevwt+cLlZPGmDDgE6+2Bvcg0JWnKKwk1k
|
||||||
|
jhKpSAek6zUkweaysxx1BscNBvGtDnRRRy0+2jMK66ad80Cg0G8xLQ/tu8kBwfml
|
||||||
|
T/9ViSRUjUrMdcfSVkrdOV3xAsOXdWspz1aljJAwcQKBgQDxebBimIBgUhNJ+2/9
|
||||||
|
27OQXsHLBYbqPurR4fKCM7a5qAIpXJcgth0YAuNvJT+rxEHTPypQCTF1fvvJuU+v
|
||||||
|
w1HjUz1CHzGYWdj0xibQDdtgkPlKqQQeT3Cnpwy3RkT4IF6FCH9YfKcUzYdamVZy
|
||||||
|
S8kFqLzEStwWIoaQX2HoBZLoUQKBgQDaZtZJoIHr+d0fLz+7rAoB8WEBA8lOaEhU
|
||||||
|
/OHwrUbuAez6k7zEDsTXgLuB8N34p1yMSaOqdGJ8rj3Pg8AlrXGELfXOF7Fe9zG0
|
||||||
|
LxujNmftmJKE8XTIALPpSBJgF5LDnh13LcNMd4b6w0DDjF7wCA2T3DTLjjqi3fo7
|
||||||
|
nDW9WQyTqwKBgEXlbXL8pZw75a1yhHY80/skEoBLt0OytpHODz407d1LjmSekng7
|
||||||
|
fqxmmaKga4+ynUMic4L7Rj+2Y/d+FlzP8rIUdBThpp9s0mn3uWBbwnZvQFmmFrUX
|
||||||
|
VYqRxhJ+2pPf+rwTO5lHa62P2HAXFni7CxMCRrGi4ZXepIjBsztP8bghAoGBAMj5
|
||||||
|
Bsmb1NJkDBGNNhWpm0/sYbpAVLc9CQqD5hnGKdYMmZh/6J11hbdVM7bAAlK1F1nU
|
||||||
|
zbGmBZb7888ISwGg2Cus61tpvANKb0eCbelDwGEIHBQP6Mm+s8/ATYB1UM2Hq0+n
|
||||||
|
Iec0ulX45JjNi/NPRcdBRKfnypdissi111HVJtifAoGADrT/t2j1bMHd1zY76Jv5
|
||||||
|
M/BWhOAgHwBo683ROEL3p6G98Xla2kd7hP6NEdo6OLJnlfK3RfoPWnotLZKEsfQM
|
||||||
|
Wog9GAcYtQbDDaYWxNBgyscSdVousY1EXs+t5vkt3XSIwUwLcEVJOu7/Fvt+YXpn
|
||||||
|
nR7aD8F62ARYxI+bv+bQR5w=
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
9
rsa_public.pem
Normal file
9
rsa_public.pem
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzgKg0WmyZ+r4GmF51rmC
|
||||||
|
j6JkFTVdEdBz5rjdTeKqOzjA5BR9ppZ/OsOvP/f2xzG+03FWhRsTW5aXtrSI6/7v
|
||||||
|
1350tvjSunJE1joZj1K6ixugN2uL+PXcprxXX8owGDggU5Ss63UE43RQF+RFBNNO
|
||||||
|
5/xvkcWIhAJtDV6VamvmsP2L5ufi6+xqyqcmpgAMoLuY2AxN62rXsnydYT9nxFxK
|
||||||
|
keETw+aYMig/uGVW8oGmKgMOK2buDjmeELTwrZLEnp5/eGMfVFJXsd0zVqEwaxaf
|
||||||
|
vM/cI4PqBev+QIvz7Fyw6W5CLrJ0KNcmL61C8GgBwby+sII9RTi/oOk5qT+jIVOx
|
||||||
|
GwIDAQAB
|
||||||
|
-----END PUBLIC KEY-----
|
||||||
@@ -21,9 +21,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, computed, shallowRef } from 'vue';
|
import { ref, onMounted, computed, shallowRef } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { LicenseService } from '@/services/LicenseService';
|
import { LicenseService } from '@/services/license-service';
|
||||||
import type { LicenseInfo } from '@/types/license';
|
import type { LicenseInfo } from '@/types/license';
|
||||||
import { LicenseStorage } from '@/utils/LicenseStorage';
|
import { LicenseStorage } from '@/utils/license-storage';
|
||||||
|
|
||||||
const { t: $t } = useI18n();// 配置国际化
|
const { t: $t } = useI18n();// 配置国际化
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import i18n from '@/i18n';
|
import i18n from '@/i18n';
|
||||||
import type { Setting } from '@/types';
|
import type { Setting } from '@/types';
|
||||||
import { LicenseService } from '@/services/LicenseService';
|
import { LicenseService } from '@/services/license-service';
|
||||||
import client from '@/plugins/kintoneClient.ts'
|
import client from '@/plugins/kintoneClient.ts'
|
||||||
import { Button } from 'kintone-ui-component/lib/button';
|
import { Button } from 'kintone-ui-component/lib/button';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import i18n from '@/i18n';
|
import i18n from '@/i18n';
|
||||||
import type { Setting } from '@/types';
|
import type { Setting } from '@/types';
|
||||||
import { LicenseService } from '@/services/LicenseService';
|
import { LicenseService } from '@/services/license-service';
|
||||||
import client from '@/plugins/kintoneClient.ts'
|
import client from '@/plugins/kintoneClient.ts'
|
||||||
import { MobileButton } from 'kintone-ui-component/lib/mobile/button';
|
import { MobileButton } from 'kintone-ui-component/lib/mobile/button';
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import type { LicenseInfo, LicenseCheckResult } from '@/types/license';
|
import type { LicenseInfo, LicenseCheckResult } from '@/types/license';
|
||||||
import i18n from '@/i18n';
|
import i18n from '@/i18n';
|
||||||
import { LicenseStorage } from '@/utils/LicenseStorage';
|
import { LicenseStorage } from '@/utils/license-storage';
|
||||||
import { PermissionService } from '@/utils/permissions';
|
import { PermissionService } from '@/utils/permissions';
|
||||||
import { Notification } from 'kintone-ui-component/lib/notification';
|
import { Notification } from 'kintone-ui-component/lib/notification';
|
||||||
import { MobileNotification } from 'kintone-ui-component/lib/mobile/notification';
|
import { MobileNotification } from 'kintone-ui-component/lib/mobile/notification';
|
||||||
import manifestJson from '@/manifest.json';
|
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;
|
const { t: $t } = i18n.global;
|
||||||
|
|
||||||
@@ -22,6 +25,16 @@ export class LicenseService {
|
|||||||
return window.location.hostname;
|
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
|
* 获取插件ID
|
||||||
*/
|
*/
|
||||||
@@ -31,37 +44,50 @@ export class LicenseService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查许可证是否有效
|
* 检查许可证是否有效
|
||||||
* 已付费/当日获取是 true
|
* 参数是解码后的JWT结构,验证签名和payload进行原有检查
|
||||||
*/
|
*/
|
||||||
static checkLicenseAvailable(license: LicenseInfo) {
|
static checkLicenseAvailable(jwtString: string, decodedJWT: KJUR.jws.JWS.JWSResult): boolean {
|
||||||
const domain = this.getDomain()
|
try {
|
||||||
const pluginId = this.getPluginId()
|
// 验证完整的JWT
|
||||||
|
const result = KJUR.jws.JWS.verifyJWT(jwtString, rsaPublicKey.trim(), {alg: ['RS256']});
|
||||||
// TODO jwt null
|
if (!result) {
|
||||||
|
console.warn('JWT signature verification failed');
|
||||||
// 检查域名和插件ID是否与当前环境一致
|
|
||||||
if (license.domain !== domain || license.pluginId !== pluginId) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否付费
|
} catch (error) {
|
||||||
if (license.isPaid) {
|
console.error('JWT verification or parsing failed:', error);
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
// JWT验证通过,从payloadPP解析license进行原有检查
|
||||||
|
const license: LicenseInfo = JSON.parse(decodedJWT.payloadPP);
|
||||||
|
|
||||||
// 检查存储是否过期(是否同一天)
|
const domain = this.getDomain()
|
||||||
if (!this.isToday(license.fetchTime)) {
|
const pluginId = this.getPluginId()
|
||||||
// 不是同一天,已过期
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查试用是否到期
|
// 检查域名和插件ID是否与当前环境一致
|
||||||
const expiredTime = new Date(license.expiredTime);
|
if (license.domain !== domain || license.pluginId !== pluginId) {
|
||||||
if (expiredTime < new Date()) {
|
return false;
|
||||||
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 {
|
static isToday(timestamp: number): boolean {
|
||||||
@@ -89,14 +115,29 @@ export class LicenseService {
|
|||||||
*/
|
*/
|
||||||
static async checkLicenseRemote(): Promise<LicenseCheckResult> {
|
static async checkLicenseRemote(): Promise<LicenseCheckResult> {
|
||||||
try {
|
try {
|
||||||
// 这里应该是实际的API调用,暂时模拟创建试用许可证
|
// 这里应该是实际的API调用,暂时模拟创建加密的试用许可证
|
||||||
const response = await this.mockRemoteCheck(this.getDomain(), this.getPluginId());
|
const response = await this.mockRemoteCheck(this.getDomain(), this.getPluginId());
|
||||||
|
|
||||||
const license = response.license!;
|
if (!response.success || !response.jwt) {
|
||||||
LicenseStorage.saveLicense(license);
|
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 {
|
return {
|
||||||
isLicenseValid: this.checkLicenseAvailable(license),
|
isLicenseValid: isValid,
|
||||||
license,
|
license,
|
||||||
isRemote: true,
|
isRemote: true,
|
||||||
};
|
};
|
||||||
@@ -104,7 +145,8 @@ export class LicenseService {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error($t('license.error.fetchFailed', { e: error }));
|
console.error($t('license.error.fetchFailed', { e: error }));
|
||||||
return {
|
return {
|
||||||
isLicenseValid: true,
|
isLicenseValid: false,
|
||||||
|
isRemote: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,7 +188,7 @@ export class LicenseService {
|
|||||||
*/
|
*/
|
||||||
static async showNotification(license: LicenseInfo | undefined, isWarning: boolean) {
|
static async showNotification(license: LicenseInfo | undefined, isWarning: boolean) {
|
||||||
let message;
|
let message;
|
||||||
const plugin = manifestJson.name[kintone.getLoginUser().language];
|
const plugin = this.getPluginName();
|
||||||
if (isWarning) {
|
if (isWarning) {
|
||||||
// 尚未到期
|
// 尚未到期
|
||||||
const remainingDays = this.getDaysRemaining(license!.expiredTime);
|
const remainingDays = this.getDaysRemaining(license!.expiredTime);
|
||||||
@@ -228,7 +270,15 @@ export class LicenseService {
|
|||||||
* 获取许可证信息(用于显示)
|
* 获取许可证信息(用于显示)
|
||||||
*/
|
*/
|
||||||
static getLocalLicenseInfo(): LicenseInfo | null {
|
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替换)
|
* 模拟远程验证(生产环境中会被真实API替换)
|
||||||
*/
|
*/
|
||||||
private static async mockRemoteCheck(domain: string, pluginId: string): Promise<{ success: boolean; license?: LicenseInfo }> {
|
private static async mockRemoteCheck(domain: string, pluginId: string): Promise<{ success: boolean; jwt?: string }> {
|
||||||
// 模拟API调用,这里总是返回试用许可证
|
// 模拟API调用,这里总是返回加密的试用许可证
|
||||||
// 生产环境这里会调用后端API: POST /api/license/check
|
// 生产环境这里会调用后端API: POST /api/license/check
|
||||||
|
|
||||||
const license = this.mockCreateTrialLicense();
|
const license = this.mockCreateTrialLicense();
|
||||||
|
const jwt = this.generateJWT(license);
|
||||||
|
|
||||||
// 模拟有时创建失败的情况(1%概率)
|
// 模拟有时创建失败的情况(1%概率)
|
||||||
if (Math.random() < 0.01) {
|
if (Math.random() < 0.01) {
|
||||||
@@ -276,7 +348,7 @@ export class LicenseService {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
license,
|
jwt,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,24 @@
|
|||||||
// 本地存储加密工具类
|
// 本地存储加密工具类
|
||||||
import { LicenseService } from '@/services/LicenseService';
|
import { LicenseService } from '@/services/license-service';
|
||||||
import type { LicenseInfo, LicenseSetting } from '@/types/license';
|
import type { LicenseInfo, LicenseSetting } from '@/types/license';
|
||||||
|
import { KJUR } from 'jsrsasign';
|
||||||
|
|
||||||
export class LicenseStorage {
|
export class LicenseStorage {
|
||||||
private static readonly STORAGE_KEY_PREFIX = 'alicorns_plugin_';
|
private static readonly STORAGE_KEY_PREFIX = 'alicorns_plugin_';
|
||||||
private static readonly STORAGE_SETTING_KEY_PREFIX = this.STORAGE_KEY_PREFIX + 'setting_';
|
private static readonly STORAGE_SETTING_KEY_PREFIX = this.STORAGE_KEY_PREFIX + 'setting_';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解码JWT(不验证)
|
||||||
|
*/
|
||||||
|
private static parseJWT(jwt: string) {
|
||||||
|
try {
|
||||||
|
return KJUR.jws.JWS.parse(jwt);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('JWT parsing failed:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成存储key
|
* 生成存储key
|
||||||
*/
|
*/
|
||||||
@@ -14,13 +27,20 @@ export class LicenseStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存许可证信息到本地存储
|
* 保存 JWT 到本地存储
|
||||||
*/
|
*/
|
||||||
static saveLicense(licenseInfo: LicenseInfo): void {
|
static saveLicense(jwt: string) {
|
||||||
try {
|
try {
|
||||||
// 直接存储LicenseInfo对象(其中已包含fetchTime)
|
// 从 JWT 中提取 pluginId 以生成存储key
|
||||||
|
const decoded = this.parseJWT(jwt);
|
||||||
|
if (!decoded) {
|
||||||
|
throw new Error('Failed to parse JWT');
|
||||||
|
}
|
||||||
|
const licenseInfo = JSON.parse(decoded.payloadPP) as LicenseInfo;
|
||||||
const key = this.generateStorageKey(licenseInfo.pluginId);
|
const key = this.generateStorageKey(licenseInfo.pluginId);
|
||||||
localStorage.setItem(key, JSON.stringify(licenseInfo));
|
localStorage.setItem(key, jwt);
|
||||||
|
|
||||||
|
return decoded;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save license:', error);
|
console.error('Failed to save license:', error);
|
||||||
throw error;
|
throw error;
|
||||||
@@ -30,23 +50,29 @@ export class LicenseStorage {
|
|||||||
/**
|
/**
|
||||||
* 从本地存储获取许可证信息
|
* 从本地存储获取许可证信息
|
||||||
*/
|
*/
|
||||||
static getLicense(pluginId: string): LicenseInfo | null {
|
static getLicense(pluginId: string) {
|
||||||
try {
|
try {
|
||||||
const key = this.generateStorageKey(pluginId);
|
const key = this.generateStorageKey(pluginId);
|
||||||
const storedData = localStorage.getItem(key);
|
const storedJWT = localStorage.getItem(key);
|
||||||
|
|
||||||
if (!storedData) return null;
|
if (!storedJWT) return null;
|
||||||
|
|
||||||
const parsedData: LicenseInfo = JSON.parse(storedData);
|
// 解码JWT
|
||||||
|
const decodedJWT = this.parseJWT(storedJWT);
|
||||||
|
if (!decodedJWT) {
|
||||||
|
// JWT解析失败,清理存储
|
||||||
|
this.clearLicense(pluginId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const isValid = LicenseService.checkLicenseAvailable(parsedData);
|
const isValid = LicenseService.checkLicenseAvailable(storedJWT, decodedJWT);
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
// 获取许可证信息失败,清理存储
|
// 获取许可证信息失败,清理存储
|
||||||
this.clearLicense(pluginId);
|
this.clearLicense(pluginId);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsedData;
|
return decodedJWT;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to get license:', error);
|
console.error('Failed to get license:', error);
|
||||||
// 如果获取失败,清空本地存储
|
// 如果获取失败,清空本地存储
|
||||||
Reference in New Issue
Block a user