背景
目前单位系统常用 Keycloak 作为认证系统后端,而前端之前写的也比较随意,这次用 Vue 3 插件以及 Ref 服务器托管网响应式来编写这个模块。另外,这个可能是全网唯一使用 keycloak 的 OIDC 原生更新密码流的介绍代码。
设计
依赖库选择
OIDC 客户端,这里选择 oidc-client-ts 来提供 OIDC 相关的服务,根据目前的调研这个算是功能比较齐全、兼容性比较好的 OIDC 客户端了。像 keycloak.js,其实也没有修改密码和自动刷新 token 的功能。另外像 Auth0 Vue SDK 则只能用于 Auth0,但他设计上还是不错的,也是通过 Vue 3 原生的插件功能实现的。
具体设计
根据 Vue 3 的官方插件文档,主要需要两部分组成,一个是需要定义一个 Plugin
并在里面使用 provide
来提供对象,另一个则是需要定义一个方法使用 inject
来接收提供的对象。
这里给原本的 oidc-client-ts 里的 UserManager
来个套娃,外层这个套一层,叫 AuthManager
。这样就可以将一些初始化时加载 LocalStorage 里的 token 等等逻辑封装在这里面,同时也可以对外暴露一些 Ref 让其他组件可以监听变化。
代码
废话不多说了,咱还是老样子,直接上代码
auth-manager.ts
import { UserManager, UserManagerSettings } from 'oidc-client-ts';
import { Plugin, inject, ref } from 'vue';
/**
* 用于注入的 key
*/
const PROVIDE_KEY = Symbol('oidc-provider');
/**
* 用户信息
*/
interface UserInfo {
/**
* 用户 id
*/
userId: string;
/**
* 用户名
*/
username: string;
/**
* token
*/
token: string;
/**
* 姓
*/
lastName: string;
/**
* 名
*/
firstName: string;
/**
* 邮箱
*/
email: string;
/**
* 认证时间
*/
authTime: number;
/**
* 角色
*/
roles: Array;
}
/**
* 认证管理器
*/
class AuthManager {
/**
* token
*/
accessToken = ref('');
/**
* 用户信息
*/
userInfo = ref();
/**
* oidc 客户端
*/
private oidc: UserManager;
/**
* 构造函数
* @param settings oidc 客户端配置
*/
constructor(settings: UserManagerSettings) {
this.oidc = new UserManager(settings);
// 当用户登录时,更新 token 和用户信息
this.oidc.events.addUserLoaded((user) => {
this.accessToken.value = user.access_token;
this.userInfo.value = {
userId: user.profile.sub,
username: user.profile.preferred_username || '',
token: user.access_token,
lastName: '',
firstName: '',
email: user.profile.email || '',
authTime: user.profile.auth_time || +new Date(),
roles: (user.profile.roles as Array) || [],
};
// 开启静默刷新,清除过期状态
this.oidc.startSilentRenew();
this.oidc.clearStaleState();
});
// 当更新 token 失败时,退出登录
this.oidc.events.addSilentRenewError(() => {
this.logout();
});
// 当 token 过期时,退出登录
this.oidc.events.addAccessTokenExpired(() => {
this.logout();
});
// 初始化时加载用户信息
this.loadUser();
}
/**
* 加载用户信息
*/
async loadUser() {
const user = await this.oidc.getUser();
// 如果能加载出来则将信息放到 Ref 里
if (user) {
this.accessToken.value = user.access_token;
this.userInfo.value = {
userId: user.profile.sub,
username: user.profile.preferred_username || '',
token: user.access_token,
lastName: '',
firstName: '',
email: user.profile.email || '',
authTime: user.profile.auth_time || +new Date(),
roles: (user.profile.roles as Array) || [],
};
this.oidc.startSilentRenew();
this.oidc.clearStaleState();
}
}
/**
* 登录
*/
login() {
return this.oidc.signinRedirect();
}
/**
* 检查是否已登录
* @returns 是否已登录
*/
async checkLogin(): Promise {
const user = await this.oidc.getUser();
return user != null && !user.expired;
}
/**
* 退出登录
*/
logout() {
this.oidc.stopSilentRenew();
this.accessToken.value = '';
this.userInfo.value = undefined;
return this.oidc.signoutRedirect();
}
/**
* 刷新 token
* @param force 是否强制刷新
*/
async refresh(force?: boolean) {
// 如果不是强制刷新,则先检查用户可用,如果用户可用则不刷新
if (!force) {
const user = await this.oidc.getUser();
if (user != null && !user.expired) {
return user;
}
}
return this.oidc.signinSilent();
}
/**
* 登录回调
*/
loginCallback() {
return this.oidc.signinCallback();
}
/**
* 重置密码
*/
resetPassword() {
// 这里使用 keycloak 登录流中的更新密码流实现
this.oidc.signinRedirect({
scope: 'openid',
extraQueryParams: {
// 这里设置额外参数时,带上 keycloak 的更新密码流
kc_actio服务器托管网n: 'UPDATE_PASSWORD',
},
});
}
}
/**
* 认证插件
*/
const authPlugin: Plugin = {
install: (app, options) => {
const auth = new AuthManager(options);
app.provide(PROVIDE_KEY, auth);
},
};
/**
* 使用认证管理器
* @returns 认证管理器
*/
const useAuthManager = () => {
return inject(PROVIDE_KEY);
};
export { authPlugin, useAuthManager };
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
系列文章 Nomad 系列文章 Nomad 重要术语 Nomad 安装设置相关术语 agent – 代理。Agent 是在 Server(服务器) 或 Client(客户端) 模式下运行的 Nomad 进程。 client – 客户端。Nomad 客户端负责运…