Commit 6f71b59f by 胡锦波

1. init 添加获取companytoken的办法

parent 6cb5f2d6
......@@ -223,7 +223,7 @@ export default abstract class BaseService {
protected async buildAppToken2Config(config?: AxiosRequestConfig) {
config = this.buildRequestConfig(config);
if (config.headers) {
config.headers[this.authKey] = await TokenManager.getAppToken(this.onceAppId || this.getEnterpriseToken4RegisterId());
config.headers[this.authKey] = await TokenManager.getCompanyToken(this.onceAppId || this.getEnterpriseToken4RegisterId());
}
this.onceAppId = 0;
return this.checkContentType(config);
......
......@@ -5,10 +5,6 @@ import config from "./config";
import qs from 'qs';
import { popupService } from '@/apppackage/element-upgrades/popup';
import { Envir, Uuid } from './env';
// import logger from './logger';
import BaseService from '@/api/core';
// import { isQianKunInstalled, qianKunGlobal } from './envir';
export interface Token {
access_token: string;
......@@ -141,7 +137,6 @@ async function tryLogin(): Promise<void> {
});
}
export const enum TokenEvent {
UserChanegd = 1,
}
......@@ -303,77 +298,18 @@ class UserController {
}
}
interface AppToken extends Token {
interface CompanyToken extends Token {
appId: string | number;
}
export class TokenManager {
private static anonymousTokenCache: Token;
private static readonly appTokenKey = 'oidc.app';
private static companyTokenKey = "oidc.company"
private static userTokenCache: Token;
private static tokenFailed = false;
private static userTokenChangedEventHandlers: { key: string; action: Function }[] = [];
private static wrap(token: Token) {
return {
access_token: token.access_token,
expires_in: token.expires_in,
token_type: token.token_type,
expires_at: (Date.now() + token.expires_in * 1000) / 1000
};
}
public static isExpired(token: Token) {
return token.expires_at * 1000 <= Date.now();
}
public static removeAppTokenCache() {
localStorage.removeItem(this.appTokenKey);
}
/**
* 格式化token
* @param token token载体
* @param full 是否使用标准格式化(type+空格+token),为 false 时只返回 token 字符串
*/
public static format2String(token: Token, full = true) {
if (full) {
return `${token.token_type} ${token.access_token}`;
}
return token.access_token;
}
public static registerOnUserTokenChanged(key: string, action: (token: string) => void) {
const t = TokenManager.userTokenChangedEventHandlers.find(i => i.key === key);
if (t) {
t.action = action;
return;
}
TokenManager.userTokenChangedEventHandlers.push({ key, action });
}
public static unregisterOnUserTokenChanged(key: string) {
const t = TokenManager.userTokenChangedEventHandlers.find(i => i.key === key);
if (t) {
const index = TokenManager.userTokenChangedEventHandlers.indexOf(t);
TokenManager.userTokenChangedEventHandlers.splice(index, 1);
}
}
private static raiseOnUserTokenChanged(token: string) {
for (const item of TokenManager.userTokenChangedEventHandlers) {
item.action(token);
}
}
public static getUserTokenStringCache() {
if (TokenManager.userTokenCache) {
return TokenManager.format2String(TokenManager.userTokenCache);
}
return '';
}
public static async getUserTokenString(): Promise<string> {
if (TokenManager.userTokenCache) {
if (!TokenManager.isExpired(TokenManager.userTokenCache)) {
......@@ -400,11 +336,20 @@ export class TokenManager {
return "";
}
public static async getAppToken(id?: string | number, force?: boolean) {
private static wrap(token: Token) {
return {
access_token: token.access_token,
expires_in: token.expires_in,
token_type: token.token_type,
expires_at: (Date.now() + token.expires_in * 1000) / 1000
};
}
public static async getCompanyToken(id: string | number, force?: boolean) {
let key = id;
if (key) {
if (!force) {
const cache = this.reloadAppTokenFromCache(key);
const cache = this.reloadCompanyTokenFromCache(key);
if (cache) {
if (+cache.appId === +key) {
if (cache.expires_at * 1000 > new Date().valueOf()) {
......@@ -414,25 +359,25 @@ export class TokenManager {
}
}
await this.getUserTokenString();
return this.format2String(await this.reloadAppToken(key, TokenManager.userTokenCache?.access_token));
await TokenManager.getUserTokenString();
return this.format2String(await this.reloadCompanyToken(key, TokenManager.userTokenCache?.access_token));
}
return '';
}
private static async reloadAppToken(id: string | number, token: string): Promise<AppToken> {
private static async reloadCompanyToken(id: string | number, token: string): Promise<CompanyToken> {
if (!id || +id === 0) {
return Promise.reject(new Error(`App id cannot be empty or 0`));
return Promise.reject(new Error(`Company id cannot be empty or 0`));
}
return new Promise((resolve, reject) => {
Axios.post<Token>(`${config.oidc.authority}/connect/token`, qs.stringify({
client_id: config.api.clientId,
client_secret: config.api.clientSecret,
grant_type: "application",
grant_type: "company",
scope: config.oidc.scope,
token: token || "",
app_register_id: id
comid: id,
token: token
}))
.then(r => {
if (r.data) {
......@@ -454,10 +399,22 @@ export class TokenManager {
});
}
private static reloadAppTokenFromCache(id: number | string) {
const local = localStorage.getItem(this.appTokenKey);
private static writeToken2Cache(id: number | string, data: CompanyToken) {
const local = localStorage.getItem(this.companyTokenKey);
if (local) {
const list = JSON.parse(local) as { id: string | number; data: CompanyToken }[];
_.remove(list, i => +i.id === +id);
list.push({ id, data });
localStorage.setItem(this.companyTokenKey, JSON.stringify(list));
} else {
localStorage.setItem(this.companyTokenKey, JSON.stringify([{ id, data }]));
}
}
private static reloadCompanyTokenFromCache(id: number | string) {
const local = localStorage.getItem(this.companyTokenKey);
if (local) {
const list = JSON.parse(local) as { id: string | number; data: AppToken }[];
const list = JSON.parse(local) as { id: string | number; data: CompanyToken }[];
const t = _.find(list, i => +i.id === +id);
if (t) {
return t.data;
......@@ -466,81 +423,43 @@ export class TokenManager {
return null;
}
private static writeToken2Cache(id: number | string, data: AppToken) {
const local = localStorage.getItem(this.appTokenKey);
if (local) {
const list = JSON.parse(local) as { id: string | number; data: AppToken }[];
_.remove(list, i => +i.id === +id);
list.push({ id, data });
localStorage.setItem(this.appTokenKey, JSON.stringify(list));
} else {
localStorage.setItem(this.appTokenKey, JSON.stringify([{ id, data }]));
/**
* 格式化token
* @param token token载体
* @param full 是否使用标准格式化(type+空格+token),为 false 时只返回 token 字符串
*/
public static format2String(token: Token, full = true) {
if (full) {
return `${token.token_type} ${token.access_token}`;
}
return token.access_token;
}
private static b64DecodeUnicode(str: string) {
return decodeURIComponent(atob(str).replace(/(.)/g, function(m, p) {
let code = p.charCodeAt(0).toString(16).toUpperCase();
if (code.length < 2) {
code = '0' + code;
}
return '%' + code;
}));
}
private static base64_url_decode(str: string) {
let output = str.replace(/-/g, "+").replace(/_/g, "/");
switch (output.length % 4) {
case 0:
break;
case 2:
output += "==";
break;
case 3:
output += "=";
break;
default:
throw "Illegal base64url string!";
public static registerOnUserTokenChanged(key: string, action: (token: string) => void) {
const t = TokenManager.userTokenChangedEventHandlers.find(i => i.key === key);
if (t) {
t.action = action;
return;
}
TokenManager.userTokenChangedEventHandlers.push({ key, action });
}
try {
return this.b64DecodeUnicode(output);
} catch {
return atob(output);
public static unregisterOnUserTokenChanged(key: string) {
const t = TokenManager.userTokenChangedEventHandlers.find(i => i.key === key);
if (t) {
const index = TokenManager.userTokenChangedEventHandlers.indexOf(t);
TokenManager.userTokenChangedEventHandlers.splice(index, 1);
}
};
public static decode(token: string) {
return JSON.parse(this.base64_url_decode(token.replace('Bearer ', '').split('.')[1]));
};
}
public static async getAnonymousTokenString(): Promise<string> {
if (TokenManager.anonymousTokenCache) {
if (!TokenManager.isExpired(TokenManager.anonymousTokenCache)) {
return TokenManager.format2String(TokenManager.anonymousTokenCache);
}
private static raiseOnUserTokenChanged(token: string) {
for (const item of TokenManager.userTokenChangedEventHandlers) {
item.action(token);
}
const r = await TokenManager.getAnonymousToken();
TokenManager.anonymousTokenCache = r;
return TokenManager.format2String(TokenManager.anonymousTokenCache);
}
public static getAnonymousToken(): Promise<Token> {
return new Promise((resolve, reject) => {
Axios.post<Token>(`${config.oidc.authority}/connect/token`, qs.stringify({
scope: config.api.scope,
client_id: config.api.clientId,
client_secret: config.api.clientSecret,
grant_type: "anonymous",
}))
.then(r => {
if (r.data) {
return resolve(TokenManager.wrap(r.data));
}
return reject("Cannot revoke the token.");
})
.catch(reject);
});
public static isExpired(token: Token) {
return token.expires_at * 1000 <= Date.now();
}
}
......
/* eslint-disable */
import Axios from 'axios';
import { UserManager, Log, WebStorageStateStore, UserManagerSettings, User } from "oidc-client";
import config from "./config";
import qs from 'qs';
import { popupService } from '@/apppackage/element-upgrades/popup';
import { Envir, Uuid } from './env';
// import logger from './logger';
import BaseService from '@/api/core';
// import { isQianKunInstalled, qianKunGlobal } from './envir';
export interface Token {
access_token: string;
expires_in: number;
token_type: string;
/**
* 过期于,单位为s,如果要与 js 时间戳作比对,请将此值乘以1000
*/
expires_at: number;
}
let oidcInstance: UserManager | null = null;
function buildOidcClient() {
const options = {
...config.oidc,
monitorSession: false,
automaticSilentRenew: false,
userStore: new WebStorageStateStore({ store: window.localStorage })
} as UserManagerSettings;
return new UserManager(options);
}
function isChrome() {
const ua = window.navigator.userAgent;
if (ua) {
const lower = ua.toLowerCase();
if (lower.indexOf('chrome') > -1) {
const reg = /chrome\/(\d)+/i;
const m = reg.exec(lower);
if (m && m[0]) {
return +m[0].replace('chrome/', '')
}
}
}
return false;
}
export function isCurrentBrowserSafari() {
const ua = window.navigator.userAgent;
if (ua) {
const lower = ua.toLowerCase();
const safari = lower.indexOf('safari') > -1 && lower.indexOf('chrome') === -1;
if (safari) {
return safari;
}
return lower.indexOf('wxwork') > -1;
}
return false;
}
function setupOidcManager() {
if (!oidcInstance) {
Log.logger = console;
oidcInstance = buildOidcClient();
Log.logger.info(`oidc client setup`);
oidcInstance.events.addUserLoaded(function() {
Log.logger.info(`用户已登入`);
});
oidcInstance.events.addAccessTokenExpiring(function() {
Log.logger.error(`用户Token即将过期`);
});
oidcInstance.events.addAccessTokenExpired(function() {
Log.logger.error(`用户Token已过期,为其主动登出`);
const logoutAction = () => {
oidcInstance?.signoutRedirect()
.then(() => Log.info(`已登出`))
.catch(e => Log.error(`登出失败,${e}`));
}
oidcInstance?.signinSilent().then(t => {
if (!t) {
logoutAction();
}
}).catch(logoutAction);
});
oidcInstance.events.addSilentRenewError(() => {
Log.logger.error(`静默刷新失败`);
});
oidcInstance.getUser().then(u => {
if (u) {
const v = isChrome();
if (v && v >= 90) {
return oidcInstance?.signinSilent();
}
if (!isCurrentBrowserSafari() && !Envir.isDev()) {
oidcInstance?.events.addUserSignedOut(function() {
const logoutAction = () => {
oidcInstance?.signoutRedirect()
.then(() => (window.location.href = config.api.root))
.catch(Log.logger.error);
};
oidcInstance?.signinSilent().then(t => {
if (!t) {
logoutAction();
}
}).catch(logoutAction);
});
}
}
});
}
return oidcInstance;
}
function login() {
window.location.href = `${config.api.root}/login?redirect=${btoa(
encodeURIComponent(window.location.href)
)}`;
}
async function tryLogin(): Promise<void> {
const instance = setupOidcManager();
return UserController.getUser().then(r => {
if (!UserController.checkAutoNav()) {
if (!r) {
instance.signinRedirect({ state: { returnUrl: UserController.getReturnUrl() || window.location.href } });
} else if (TokenManager.isExpired(r)) {
instance.signinRedirect({ state: { returnUrl: UserController.getReturnUrl() || window.location.href } });
}
}
});
}
export const enum TokenEvent {
UserChanegd = 1,
}
class UserController {
private static lastRenewTicks = 0;
public static readonly uid = Uuid.execute();
private static returnUrl = ""
private static readonly autoNav = 'autoNav';
public static readonly userTokenRenew = 'user-token-renew';
private static readonly untrimProperty = ['id_token', 'access_token', 'expires_at', 'profile', 'token_type'];
public static async tryLogin() {
return tryLogin();
}
public static setReturnUrl(url: string) {
this.returnUrl = url;
}
public static getReturnUrl() {
return this.returnUrl
}
public static async isUserLogined() {
const user = await UserController.getUser();
if (user) {
return !TokenManager.isExpired(user);
}
return false;
}
public static async getUser() {
const instance = setupOidcManager();
return await instance.getUser();
}
public static async getCurrentUserTokenHasBearer() {
const user = await UserController.getUser();
if (user) {
if (TokenManager.isExpired(user)) {
await UserController.tryLogin();
return "";
}
return "Bearer " + user.access_token;
}
return "";
}
private static trimUser(user: User) {
const copy = Object.create(null);
for (const item of this.untrimProperty) {
copy[item] = (user as any)[item];
}
return copy;
}
public static trimUser2String(user: User) {
return JSON.stringify(this.trimUser(user));
}
public static async getUsername(): Promise<string> {
const user = await UserController.getUser();
if (user) {
return user.profile.name || user.profile.nickname || user.profile.realname || user.profile.mobile || user.profile.email || '[未设置名称]';
}
return '';
}
public static async getUserId(): Promise<string> {
const user = await UserController.getUser();
if (user) {
return user.profile.sub;
}
return '';
}
public static async getUserAvatar(): Promise<string> {
const user = await UserController.getUser();
if (user) {
return user.profile.avatar_url;
}
return '';
}
public static checkAutoNav() {
const auto = localStorage.getItem(this.autoNav) as string;
if (auto) {
localStorage.removeItem(this.autoNav);
setTimeout(() => window.location.href = auto);
return true;
}
return false;
}
public static async signout() {
const instance = setupOidcManager();
return instance.signoutRedirect();
}
public static removeUserCacheWithoutRedirect() {
localStorage.clear();
window.location.href = config.api.root;
}
public static removeUserCache(redirect?: string) {
localStorage.setItem(this.autoNav, redirect || window.location.href);
return this.signout();
}
public static async signin(params: any) {
const instance = setupOidcManager();
if (params) {
params = Object.assign(
{ state: { returnUrl: UserController.removeQueryParameter(window.location.href, 'authcode') } },
params
);
}
return instance.signinRedirect(params);
}
public static async getCurrentUserToken() {
const user = await UserController.getUser();
if (user) {
if (TokenManager.isExpired(user)) {
await UserController.tryLogin();
return '';
}
return "Bearer " + user.access_token;
}
return '';
}
public static async renewUserToken() {
const instance = setupOidcManager();
return instance.signinSilent({ silentRequestTimeout: 20 * 1000 });
}
public static removeQueryParameter(url: string, key: string) {
const split = url.split('?');
if (split.length <= 1) {
return url;
}
const search = split[split.length - 1];
const params = new URLSearchParams(search);
if (params.has(key)) {
params.delete(key);
}
const query = params.toString();
return `${split[0]}${query ? '?' + query : ''}`;
}
public static buildLoginKey() {
return `oidc.user:${config.oidc.authority}:${config.oidc.client_id}`;
}
}
interface AppToken extends Token {
appId: string | number;
}
export class TokenManager {
private static anonymousTokenCache: Token;
private static readonly appTokenKey = 'oidc.app';
private static userTokenCache: Token;
private static tokenFailed = false;
private static userTokenChangedEventHandlers: { key: string; action: Function }[] = [];
private static wrap(token: Token) {
return {
access_token: token.access_token,
expires_in: token.expires_in,
token_type: token.token_type,
expires_at: (Date.now() + token.expires_in * 1000) / 1000
};
}
public static isExpired(token: Token) {
return token.expires_at * 1000 <= Date.now();
}
public static removeAppTokenCache() {
localStorage.removeItem(this.appTokenKey);
}
/**
* 格式化token
* @param token token载体
* @param full 是否使用标准格式化(type+空格+token),为 false 时只返回 token 字符串
*/
public static format2String(token: Token, full = true) {
if (full) {
return `${token.token_type} ${token.access_token}`;
}
return token.access_token;
}
public static registerOnUserTokenChanged(key: string, action: (token: string) => void) {
const t = TokenManager.userTokenChangedEventHandlers.find(i => i.key === key);
if (t) {
t.action = action;
return;
}
TokenManager.userTokenChangedEventHandlers.push({ key, action });
}
public static unregisterOnUserTokenChanged(key: string) {
const t = TokenManager.userTokenChangedEventHandlers.find(i => i.key === key);
if (t) {
const index = TokenManager.userTokenChangedEventHandlers.indexOf(t);
TokenManager.userTokenChangedEventHandlers.splice(index, 1);
}
}
private static raiseOnUserTokenChanged(token: string) {
for (const item of TokenManager.userTokenChangedEventHandlers) {
item.action(token);
}
}
public static getUserTokenStringCache() {
if (TokenManager.userTokenCache) {
return TokenManager.format2String(TokenManager.userTokenCache);
}
return '';
}
public static async getUserTokenString(): Promise<string> {
if (TokenManager.userTokenCache) {
if (!TokenManager.isExpired(TokenManager.userTokenCache)) {
return TokenManager.format2String(TokenManager.userTokenCache);
}
}
const user = await UserController.getUser();
if (user) {
if (TokenManager.isExpired(user)) {
await UserController.renewUserToken();
const silentUser = await UserController.getUser();
if (silentUser) {
TokenManager.userTokenCache = silentUser;
const token = TokenManager.format2String(TokenManager.userTokenCache);
TokenManager.raiseOnUserTokenChanged(token);
return token;
}
throw new Error(`用户Token获取失败,请求已终止。`);
}
TokenManager.userTokenCache = user;
return TokenManager.getUserTokenString();
}
UserController.tryLogin();
return "";
}
public static async getCompanyToken() {
await this.getUserTokenString();
const token = TokenManager.userTokenCache?.access_token;
}
public static async getAppToken(id?: string | number, force?: boolean) {
let key = id;
if (key) {
if (!force) {
const cache = this.reloadAppTokenFromCache(key);
if (cache) {
if (+cache.appId === +key) {
if (cache.expires_at * 1000 > new Date().valueOf()) {
return this.format2String(cache);
}
}
}
}
await this.getUserTokenString();
return this.format2String(await this.reloadAppToken(key, TokenManager.userTokenCache?.access_token));
}
return '';
}
private static async reloadAppToken(id: string | number, token: string): Promise<AppToken> {
if (!id || +id === 0) {
return Promise.reject(new Error(`App id cannot be empty or 0`));
}
return new Promise((resolve, reject) => {
Axios.post<Token>(`${config.oidc.authority}/connect/token`, qs.stringify({
client_id: config.api.clientId,
client_secret: config.api.clientSecret,
grant_type: "application",
scope: config.oidc.scope,
token: token || "",
app_register_id: id
}))
.then(r => {
if (r.data) {
const t = TokenManager.wrap(r.data);
const app = { ...t, appId: id };
this.writeToken2Cache(id, app);
resolve(app);
}
reject();
})
.catch(() => {
if (!this.tokenFailed) {
popupService.toast.warn('应用授权失败');
this.tokenFailed = true;
setTimeout(() => this.tokenFailed = false, 300);
}
reject(403);
});
});
}
private static reloadAppTokenFromCache(id: number | string) {
const local = localStorage.getItem(this.appTokenKey);
if (local) {
const list = JSON.parse(local) as { id: string | number; data: AppToken }[];
const t = _.find(list, i => +i.id === +id);
if (t) {
return t.data;
}
}
return null;
}
private static writeToken2Cache(id: number | string, data: AppToken) {
const local = localStorage.getItem(this.appTokenKey);
if (local) {
const list = JSON.parse(local) as { id: string | number; data: AppToken }[];
_.remove(list, i => +i.id === +id);
list.push({ id, data });
localStorage.setItem(this.appTokenKey, JSON.stringify(list));
} else {
localStorage.setItem(this.appTokenKey, JSON.stringify([{ id, data }]));
}
}
private static b64DecodeUnicode(str: string) {
return decodeURIComponent(atob(str).replace(/(.)/g, function(m, p) {
let code = p.charCodeAt(0).toString(16).toUpperCase();
if (code.length < 2) {
code = '0' + code;
}
return '%' + code;
}));
}
private static base64_url_decode(str: string) {
let output = str.replace(/-/g, "+").replace(/_/g, "/");
switch (output.length % 4) {
case 0:
break;
case 2:
output += "==";
break;
case 3:
output += "=";
break;
default:
throw "Illegal base64url string!";
}
try {
return this.b64DecodeUnicode(output);
} catch {
return atob(output);
}
};
public static decode(token: string) {
return JSON.parse(this.base64_url_decode(token.replace('Bearer ', '').split('.')[1]));
};
public static async getAnonymousTokenString(): Promise<string> {
if (TokenManager.anonymousTokenCache) {
if (!TokenManager.isExpired(TokenManager.anonymousTokenCache)) {
return TokenManager.format2String(TokenManager.anonymousTokenCache);
}
}
const r = await TokenManager.getAnonymousToken();
TokenManager.anonymousTokenCache = r;
return TokenManager.format2String(TokenManager.anonymousTokenCache);
}
public static getAnonymousToken(): Promise<Token> {
return new Promise((resolve, reject) => {
Axios.post<Token>(`${config.oidc.authority}/connect/token`, qs.stringify({
scope: config.api.scope,
client_id: config.api.clientId,
client_secret: config.api.clientSecret,
grant_type: "anonymous",
}))
.then(r => {
if (r.data) {
return resolve(TokenManager.wrap(r.data));
}
return reject("Cannot revoke the token.");
})
.catch(reject);
});
}
}
export { setupOidcManager, UserController };
File mode changed
File mode changed
<template>
<div>AddressBook</div>
<div>
AddressBook
<button @click="test">asdfasdf</button>
</div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import { TokenManager } from "@/api/token";
import EnterpriseHost from "@/views/service/enterprise-host";
@Component({ components: {} })
export default class AddressBook extends Vue {}
export default class AddressBook extends Vue {
private test() {
const comid = EnterpriseHost.getPoid();
TokenManager.getCompanyToken(comid);
}
}
</script>
<style lang="less" scoped>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment