390 lines
14 KiB
JavaScript
390 lines
14 KiB
JavaScript
"use strict";
|
||
Object.defineProperty(exports, "__esModule", { value: true });
|
||
exports.Token = void 0;
|
||
const Feature_1 = require("oak-frontend-base/es/types/Feature");
|
||
const Exception_1 = require("oak-domain/lib/types/Exception");
|
||
const Projection_1 = require("../types/Projection");
|
||
const Exception_2 = require("../types/Exception");
|
||
const constants_1 = require("../config/constants");
|
||
const lodash_1 = require("oak-domain/lib/utils/lodash");
|
||
class Token extends Feature_1.Feature {
|
||
tokenValue;
|
||
environment;
|
||
cache;
|
||
storage;
|
||
application;
|
||
ignoreExceptionList = [Exception_1.OakNetworkException, Exception_1.OakServerProxyException, Exception_1.OakRequestTimeoutException, Exception_1.OakClockDriftException, Exception_2.OakApplicationLoadingException];
|
||
async loadSavedToken() {
|
||
this.tokenValue = await this.storage.load(constants_1.LOCAL_STORAGE_KEYS.token);
|
||
await this.refreshTokenData(this.tokenValue);
|
||
this.publish();
|
||
}
|
||
constructor(cache, storage, environment, application) {
|
||
super();
|
||
this.cache = cache;
|
||
this.storage = storage;
|
||
this.environment = environment;
|
||
this.application = application;
|
||
this.tokenValue = ''; // 置个空字符串代表还在load storage缓存的数据
|
||
// this.loadSavedToken();
|
||
application.subscribe(() => this.loadSavedToken());
|
||
if (process.env.OAK_PLATFORM === 'web' && (process.env.NODE_ENV !== 'development' || process.env.OAK_DEV_MODE === 'server')) {
|
||
// 纯前台模式 多窗口时不监听storage
|
||
// 在web下可能多窗口,一个窗口更新了token,其它窗口应跟着变
|
||
window.addEventListener('storage', async (e) => {
|
||
if (e.key === constants_1.LOCAL_STORAGE_KEYS.token) {
|
||
this.tokenValue = e.newValue ? JSON.parse(e.newValue) : undefined;
|
||
await this.refreshTokenData(this.tokenValue);
|
||
this.publish();
|
||
}
|
||
});
|
||
}
|
||
}
|
||
checkNeedSetPassword() {
|
||
const user = this.getUserInfo();
|
||
const { system } = this.application.getApplication();
|
||
const { Security } = system.config;
|
||
if (Security?.level === 'strong' && user && !user.hasPassword) {
|
||
return Promise.reject(new Exception_2.OakPasswordUnset());
|
||
}
|
||
}
|
||
async refreshTokenData(tokenValue) {
|
||
if (!tokenValue) {
|
||
this.tokenValue = undefined;
|
||
return;
|
||
}
|
||
const env = await this.environment.getEnv();
|
||
try {
|
||
const applicationId = this.application.getApplicationId();
|
||
const { result } = await this.cache.exec('refreshToken', {
|
||
tokenValue,
|
||
env,
|
||
applicationId,
|
||
}, undefined, true, true);
|
||
if (tokenValue !== result) {
|
||
// 如果返回空字符串,token被disabled,tokenValue置为undefined
|
||
if (result) {
|
||
this.tokenValue = result;
|
||
await this.storage.save(constants_1.LOCAL_STORAGE_KEYS.token, result);
|
||
this.checkNeedSetPassword();
|
||
}
|
||
else {
|
||
this.removeToken(true);
|
||
}
|
||
}
|
||
else {
|
||
this.checkNeedSetPassword();
|
||
}
|
||
}
|
||
catch (err) {
|
||
// refresh出了任何错都无视(排除网络异常),直接放弃此token
|
||
console.warn(err);
|
||
// if (
|
||
// err instanceof OakNetworkException ||
|
||
// err instanceof OakServerProxyException ||
|
||
// err instanceof OakRequestTimeoutException ||
|
||
// err instanceof OakClockDriftException
|
||
// ) {
|
||
// return;
|
||
// }
|
||
if (this.ignoreExceptionList.some((clazz) => {
|
||
return (0, Exception_1.isOakException)(err, clazz);
|
||
})) {
|
||
return;
|
||
}
|
||
this.removeToken(true);
|
||
}
|
||
}
|
||
async loginByMobile(mobile, captcha, disableRegister) {
|
||
const env = await this.environment.getEnv();
|
||
const { result } = await this.cache.exec('loginByMobile', {
|
||
mobile,
|
||
captcha,
|
||
disableRegister,
|
||
env,
|
||
}, undefined, true);
|
||
this.tokenValue = result;
|
||
await this.storage.save(constants_1.LOCAL_STORAGE_KEYS.token, result);
|
||
this.publish();
|
||
this.checkNeedSetPassword();
|
||
}
|
||
async loginByEmail(email, captcha, disableRegister) {
|
||
const env = await this.environment.getEnv();
|
||
const { result } = await this.cache.exec('loginByEmail', {
|
||
email,
|
||
captcha,
|
||
disableRegister,
|
||
env,
|
||
}, undefined, true);
|
||
this.tokenValue = result;
|
||
await this.storage.save(constants_1.LOCAL_STORAGE_KEYS.token, result);
|
||
this.publish();
|
||
this.checkNeedSetPassword();
|
||
}
|
||
async loginByAccount(account, password) {
|
||
const env = await this.environment.getEnv();
|
||
const { result } = await this.cache.exec('loginByAccount', {
|
||
account,
|
||
password,
|
||
env,
|
||
}, undefined, true);
|
||
this.tokenValue = result;
|
||
await this.storage.save(constants_1.LOCAL_STORAGE_KEYS.token, result);
|
||
this.publish();
|
||
this.checkNeedSetPassword();
|
||
}
|
||
async bindByMobile(mobile, captcha) {
|
||
const env = await this.environment.getEnv();
|
||
await this.cache.exec('bindByMobile', {
|
||
mobile,
|
||
captcha,
|
||
env,
|
||
}, undefined, true);
|
||
this.publish();
|
||
}
|
||
async bindByEmail(email, captcha) {
|
||
const env = await this.environment.getEnv();
|
||
await this.cache.exec('bindByEmail', {
|
||
email,
|
||
captcha,
|
||
env,
|
||
}, undefined, true);
|
||
this.publish();
|
||
}
|
||
async loginWebByMpToken(mpToken) {
|
||
const env = await this.environment.getEnv();
|
||
const { result } = await this.cache.exec('loginWebByMpToken', {
|
||
mpToken,
|
||
env,
|
||
}, undefined, true);
|
||
this.tokenValue = result;
|
||
await this.storage.save(constants_1.LOCAL_STORAGE_KEYS.token, result);
|
||
this.publish();
|
||
this.checkNeedSetPassword();
|
||
}
|
||
async loginByWechatInWebEnv(wechatLoginId) {
|
||
const env = await this.environment.getEnv();
|
||
const { result } = await this.cache.exec('loginByWechat', {
|
||
env: env,
|
||
wechatLoginId,
|
||
});
|
||
this.tokenValue = result;
|
||
await this.storage.save(constants_1.LOCAL_STORAGE_KEYS.token, result);
|
||
this.publish();
|
||
this.checkNeedSetPassword();
|
||
}
|
||
async loginByOAuth(code, state) {
|
||
const env = await this.environment.getEnv();
|
||
const { result } = await this.cache.exec('loginByOauth', {
|
||
env: env,
|
||
code,
|
||
state,
|
||
});
|
||
this.tokenValue = result;
|
||
await this.storage.save(constants_1.LOCAL_STORAGE_KEYS.token, result);
|
||
this.publish();
|
||
this.checkNeedSetPassword();
|
||
}
|
||
async loginWechat(code, params) {
|
||
const env = await this.environment.getEnv();
|
||
const { result } = await this.cache.exec('loginWechat', {
|
||
code,
|
||
env: env,
|
||
wechatLoginId: params?.wechatLoginId,
|
||
});
|
||
this.tokenValue = result;
|
||
await this.storage.save(constants_1.LOCAL_STORAGE_KEYS.token, result);
|
||
this.publish();
|
||
this.checkNeedSetPassword();
|
||
}
|
||
async loginWechatMp(params) {
|
||
const { code } = await wx.login();
|
||
const env = await this.environment.getEnv();
|
||
const { result } = await this.cache.exec('loginWechatMp', {
|
||
code,
|
||
env: env,
|
||
wechatLoginId: params?.wechatLoginId,
|
||
});
|
||
this.tokenValue = result;
|
||
await this.storage.save(constants_1.LOCAL_STORAGE_KEYS.token, result);
|
||
this.publish();
|
||
this.checkNeedSetPassword();
|
||
}
|
||
async loginWechatNative(code) {
|
||
const env = await this.environment.getEnv();
|
||
const { result } = await this.cache.exec('loginWechatNative', {
|
||
code,
|
||
env: env,
|
||
});
|
||
this.tokenValue = result;
|
||
await this.storage.save(constants_1.LOCAL_STORAGE_KEYS.token, result);
|
||
this.publish();
|
||
this.checkNeedSetPassword();
|
||
}
|
||
async syncUserInfoWechatMp() {
|
||
// 2.27.1及以上版本不再支持getUserProfile(返回头像为【灰色头像】,昵称为【微信用户】)
|
||
const info = await wx.getUserProfile({
|
||
desc: '同步微信昵称和头像信息',
|
||
});
|
||
const { userInfo: { nickName: nickname, avatarUrl }, encryptedData, signature, iv, } = info;
|
||
await this.cache.exec('syncUserInfoWechatMp', {
|
||
nickname,
|
||
avatarUrl,
|
||
encryptedData,
|
||
signature,
|
||
iv,
|
||
});
|
||
this.publish();
|
||
}
|
||
async logout(dontPublish) {
|
||
await this.cache.exec('logout', {
|
||
tokenValue: this.tokenValue,
|
||
}, undefined, undefined, true);
|
||
this.removeToken(dontPublish);
|
||
}
|
||
removeToken(dontPublish) {
|
||
this.tokenValue = undefined;
|
||
this.storage.remove(constants_1.LOCAL_STORAGE_KEYS.token);
|
||
if (!dontPublish) {
|
||
this.publish();
|
||
}
|
||
}
|
||
getTokenValue() {
|
||
if (this.tokenValue === '') {
|
||
throw new Exception_2.OakUserInfoLoadingException();
|
||
}
|
||
return this.tokenValue;
|
||
}
|
||
getToken(allowUnloggedIn) {
|
||
if (this.tokenValue === '') {
|
||
throw new Exception_2.OakUserInfoLoadingException();
|
||
}
|
||
if (this.tokenValue) {
|
||
const token = this.cache.get('token', {
|
||
data: (0, lodash_1.cloneDeep)(Projection_1.tokenProjection),
|
||
filter: {
|
||
value: this.tokenValue,
|
||
},
|
||
})[0];
|
||
if (!token) {
|
||
if (allowUnloggedIn) {
|
||
return undefined;
|
||
}
|
||
throw new Exception_2.OakUserInfoLoadingException();
|
||
}
|
||
return token;
|
||
}
|
||
if (allowUnloggedIn) {
|
||
return undefined;
|
||
}
|
||
throw new Exception_1.OakUnloggedInException();
|
||
}
|
||
getUserId(allowUnloggedIn) {
|
||
const token = this.getToken(allowUnloggedIn);
|
||
if (token?.userId) {
|
||
return token.userId;
|
||
}
|
||
}
|
||
// getUserInfo 不要求登录
|
||
getUserInfo() {
|
||
const token = this.getToken(true);
|
||
if (token?.user) {
|
||
return token.user;
|
||
}
|
||
}
|
||
isRoot() {
|
||
const token = this.getToken(true);
|
||
return !!token?.user?.isRoot;
|
||
}
|
||
/**
|
||
* 这个是指token的player到底是不是root
|
||
* @returns
|
||
*/
|
||
isReallyRoot() {
|
||
const token = this.getToken(true);
|
||
return !!token?.player?.isRoot;
|
||
}
|
||
isSelf() {
|
||
const token = this.getToken();
|
||
return token?.playerId === token?.userId;
|
||
}
|
||
async sendCaptcha(origin, content, type) {
|
||
const env = await this.environment.getEnv();
|
||
if (origin === 'mobile') {
|
||
const { result } = await this.cache.exec('sendCaptchaByMobile', {
|
||
mobile: content,
|
||
env: env,
|
||
type,
|
||
});
|
||
return result;
|
||
}
|
||
else {
|
||
const { result } = await this.cache.exec('sendCaptchaByEmail', {
|
||
email: content,
|
||
env: env,
|
||
type,
|
||
});
|
||
return result;
|
||
}
|
||
}
|
||
async switchTo(userId) {
|
||
const currentUserId = this.getUserId();
|
||
if (currentUserId === userId) {
|
||
throw new Exception_1.OakPreConditionUnsetException('您已经是当前用户');
|
||
}
|
||
await this.cache.exec('switchTo', {
|
||
userId,
|
||
});
|
||
this.publish();
|
||
}
|
||
async refreshWechatPublicUserInfo() {
|
||
await this.cache.exec('refreshWechatPublicUserInfo', {});
|
||
}
|
||
async getWechatMpUserPhoneNumber(code) {
|
||
const env = await this.environment.getEnv();
|
||
await this.cache.exec('getWechatMpUserPhoneNumber', {
|
||
code,
|
||
env: env,
|
||
});
|
||
}
|
||
async wakeupParasite(id) {
|
||
const env = await this.environment.getEnv();
|
||
const { result } = await this.cache.exec('wakeupParasite', {
|
||
id,
|
||
env: env,
|
||
});
|
||
this.tokenValue = result;
|
||
await this.storage.save(constants_1.LOCAL_STORAGE_KEYS.token, result);
|
||
this.publish();
|
||
}
|
||
needVerifyPassword() {
|
||
const user = this.getUserInfo();
|
||
const { system } = this.application.getApplication();
|
||
const { Security } = system.config;
|
||
if (Security && ['strong', 'medium'].includes(Security.level)) {
|
||
// 对于安全要求中高的系统,需要检查其验证密码时间
|
||
const stamp = Date.now() - Security.passwordVerifyGap;
|
||
return user?.hasPassword && user.verifyPasswordAt < stamp;
|
||
}
|
||
}
|
||
async verifyPassword(password) {
|
||
const env = this.environment.getEnv();
|
||
await this.cache.exec('verifyPassword', {
|
||
password,
|
||
env,
|
||
});
|
||
}
|
||
/**
|
||
* 添加一个异常到忽略列表,如果refreshToken时出现这个异常,不会强制用户登出
|
||
*/
|
||
addIgnoreException(clazz) {
|
||
if (!this.ignoreExceptionList.includes(clazz)) {
|
||
this.ignoreExceptionList.push(clazz);
|
||
}
|
||
}
|
||
removeIgnoreException(clazz) {
|
||
this.ignoreExceptionList = this.ignoreExceptionList.filter((c) => c !== clazz);
|
||
}
|
||
}
|
||
exports.Token = Token;
|