环球快看:使用 IdentityServer 保护 Vue 前端
2022-12-19 07:25:49
前情提要

《使用 IdentityServer 保护 Web 应用(AntD Pro 前端 + SpringBoot 后端)》中记录了使用 IdentityServer 保护前后端的过程,其中的前端工程是以 UMI Js 为例。今天,再来记录一下使用 IdentityServer 保护 Vue 前端的过程,和 UMI Js 项目使用 umi plugin 的方式不同,本文没有使用 Vue 相关的插件,而是直接使用了 oidc-client js。


【资料图】

另外,我对 Vue 这个框架非常不熟,在 vue-router 这里稍微卡住了一段时间,后来瞎试居然又成功了。针对这个问题,我还去 StackOverflow 上问了,但并没有收到有效的回复:https://stackoverflow.com/questions/74769607/how-to-access-vues-methods-from-navigation-guard

准备工作

首先,需要在 IdentityServer 服务器端注册该 Vue 前端应用,仍然以代码写死这个客户端为例:

new Client{ClientId = "vue-client",ClientSecrets = { new Secret("vue-client".Sha256()) },ClientName = "vue client",AllowedGrantTypes = GrantTypes.Implicit,AllowAccessTokensViaBrowser = true,RequireClientSecret = false,RequirePkce = true,RedirectUris ={"http://localhost:8080/callback","http://localhost:8080/static/silent-renew.html",},AllowedCorsOrigins = { "http://localhost:8080" },AllowedScopes = { "openid", "profile", "email" },AllowOfflineAccess = true,AccessTokenLifetime = 90,AbsoluteRefreshTokenLifetime = 0,RefreshTokenUsage = TokenUsage.OneTimeOnly,RefreshTokenExpiration = TokenExpiration.Sliding,UpdateAccessTokenClaimsOnRefresh = true,RequireConsent = false,};

在 Vue 工程里安装 oidc-client

yarn add oidc-client

在 Vue 里配置 IdentityServer 服务器信息

在项目里添加一个 src/security/security.js文件:

import Oidc from "oidc-client"function getIdPUrl() {return "https://id6.azurewebsites.net";}Oidc.Log.logger = console;Oidc.Log.level = Oidc.Log.DEBUG;const mgr = new Oidc.UserManager({authority: getIdPUrl(),client_id: "vue-client",redirect_uri: window.location.origin + "/callback",response_type: "id_token token",scope: "openid profile email",post_logout_redirect_uri: window.location.origin + "/logout",userStore: new Oidc.WebStorageStateStore({store: window.localStorage}),automaticSilentRenew: true,silent_redirect_uri: window.location.origin + "/silent-renew.html",accessTokenExpiringNotificationTime: 10,})export default mgr

在 main.js 里注入登录相关的数据和方法数据

不借助任何状态管理包,直接将相关的数据添加到 Vue 的 app 对象上:

import mgr from "@/security/security";const globalData = {isAuthenticated: false,user: "",mgr: mgr}

方法

const globalMethods = {async authenticate(returnPath) {console.log("authenticate")const user = await this.$root.getUser();if (user) {this.isAuthenticated = true;this.user = user} else {await this.$root.signIn(returnPath)}},async getUser() {try {return await this.mgr.getUser();} catch (err) {console.error(err);}},signIn(returnPath) {returnPath ? this.mgr.signinRedirect({state: returnPath}) : this.mgr.signinRedirect();}}

修改 Vue 的实例化代码

new Vue({router,data: globalData,methods: globalMethods,render: h => h(App),}).$mount("#app")

修改 router

在 src/router/index.js中,给需要登录的路由添加 meta 字段:

Vue.use(VueRouter)const router = new VueRouter({{path: "/private",name: "private page",component: resolve => require(["@/pages/private.vue"], resolve),meta: {requiresAuth: true}}});export default router

接着,正如在配置中体现出来的,需要一个回调页面来接收登录后的授权信息,这可以通过添加一个 src/views/CallbackPage.vue文件来实现:

<script>export default {async created() {try {const result = await this.$root.mgr.signinRedirectCallback();const returnUrl = result.state ?? "/";await this.$router.push({path: returnUrl})}catch(e){await this.$router.push({name: "Unauthorized"})}}}</script>

然后,需要在路由里配置好这个回调页面:

import CallbackPage from "@/views/CallbackPage.vue";Vue.use(VueRouter)const router = new VueRouter({routes: {path: "/private",name: "private page",component: resolve => require(["@/pages/private.vue"], resolve),meta: {requiresAuth: true}},{path: "/callback",name: "callback",component: CallbackPage}});export default router

同时,在这个 router 里添加一个所谓的“全局前置守卫”(https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%85%A8%E5%B1%80%E5%89%8D%E7%BD%AE%E5%AE%88%E5%8D%AB),注意就是这里,我碰到了问题,并且在 StackOverflow 上提了这个问题。在需要调用前面定义的认证方法时,不能使用 router.app.authenticate,而要使用 router.apps[1].authenticate,这是我通过 inspect router发现的:

...router.beforeEach(async function (to, from, next) {let app = router.app.$data || {isAuthenticated: false}if(app.isAuthenticated) {next()} else if (to.matched.some(record => record.meta.requiresAuth)) {router.apps[1].authenticate(to.path).then(()=>{next()})}else {next()}})export default router

到了这一步,应用就可以跑起来了,在访问 /private 时,浏览器会跳转到 IdentityServer 服务器的登录页面,在登录完成后再跳转回来。

添加 silent-renew.html

注意 security.js,我们启用了 automaticSilentRenew,并且配置了 silent_redirect_uri的路径为 silent-renew.html。它是一个独立的引用了 oidc-client js 的 html 文件,不依赖 Vue,这样方便移植到任何前端项目。

oidc-client.min.js

首先,将我们安装好的 oidc-client 包下的 node_modules/oidc-client/dist/oidc-client.min.js文件,复制粘贴到 public/static目录下。

然后,在这个目录下添加 public/static/silent-renew.html文件。

Silent Renew Token<script src="oidc-client.min.js"></script><script>console.log("renewing tokens");new Oidc.UserManager({userStore: new Oidc.WebStorageStateStore({ store: window.localStorage })}).signinSilentCallback();</script>

给 API 请求添加认证头

最后,给 API 请求添加上认证头。前提是,后端接口也使用同样的 IdentityServer 来保护(如果是 SpringBoot 项目,可以参考《[使用 IdentityServer 保护 Web 应用(AntD Pro 前端 + SpringBoot 后端) - Jeff Tian的文章 - 知乎](https://zhuanlan.zhihu.com/p/533197284) 》);否则,如果 API 是公开的,就不需要这一步了。

对于使用 axios 的 API 客户端,可以利用其 request interceptors,来统一添加这个认证头,比如:

import router from "../router"import Vue from "vue";const v = new Vue({router})const service = axios.create({// 公共接口--这里注意后面会讲baseURL: process.env.BASE_API,// 超时时间 单位是ms,这里设置了3s的超时时间timeout: 20 * 1000});service.interceptors.request.use(config => {const user = v.$root.user;if(user) {const authToken = user.access_token;if(authToken){config.headers.Authorization = `Bearer ${authToken}`;}}return config;}, Promise.reject)export default service

环球快看:使用 IdentityServer 保护 Vue 前端

2022-12-19 07:25:49

前11月新疆喀什综保区进出口额逾230亿元

2022-12-18 21:04:56

世界即时:海南:加快推进自由贸易港建设

2022-12-17 23:41:16

福昕软件(688095)12月16日主力资金净卖出492.44万元

2022-12-17 08:22:53

【播资讯】昂立教育: 昂立教育第十一届董事会第五次会议决议公告

2022-12-16 19:04:58

Microchip MPLAB Harmony 世界热消息

2022-12-16 12:40:42

当前快看:合同续签申请该怎样写呢?应该注意哪些?

2022-12-16 05:08:54

吴佩慈:力压大S跻身“芭比娃娃”,6年生4胎,只为嫁纪晓波

2022-12-15 17:58:00

盈峰环境董秘回复:截止目前公司尚未开展碳交易业务,未持有碳交易所股权

2022-12-15 12:10:52

公司前线|丰立智能新增“注册制次新股”概念 天天最新

2022-12-15 05:25:36

中直股份: 中航直升机股份有限公司第八届监事会第十六次会议决议公告-世界百事通

2022-12-14 15:58:26

天天热讯:车之家2021年最新汽车报价

2022-12-14 10:33:14

极目锐评|孕妇怕感染在公司外的车里办公,对职场孕妈的保护不能悬空 每日看点

2022-12-13 21:36:03

世界快资讯丨马斯克旗下SpaceX拟每股77美元出售内部股 估值约1400亿美元

2022-12-13 13:46:43

电信盒子怎么连接usb 中国电信机顶盒usb怎么用-全球最资讯

2022-12-13 07:17:16

全球时讯:招商积余选举聂黎明为公司董事长 陈海照为总经理

2022-12-12 16:25:02

麦格理:维持申洲国际(02313.HK)“中性”评级 目标价降至80港元

2022-12-12 12:57:13

天天微头条丨华大智造(688114):独立董事提名人声明(武丽波)

2022-12-09 17:31:48

关爱你我他(她)· 温暖千万家丨西峡法院:慰问一线送物资 共筑防疫“安全线”-环球关注

2022-12-08 16:50:14

机器人(300024)新增【核酸采样】概念

2022-12-07 14:50:59

独家资金:早盘主力进散户逃前10股 天天消息

2022-12-06 11:00:50

南京市建邺区:持续加大财政投入助力职业教育创新发展

2022-07-07 08:29:45

经营证券期货业务许可证获批 安徽金融业七大类牌照齐全

2022-03-20 14:38:08

多措并举全面排查 咸阳全力做好市第八次党代会安全保障工作

2022-03-20 14:30:43

便民办税:灵源镇大王村为失联多年的特殊村民办理医保

2022-03-20 14:28:01

践行新使命忠诚保大庆:八旬老人“丢失”钱财 彬州民警帮找回

2022-03-20 14:26:38

创新集聚新动能 宁国着力构建汽车零部件产业龙头

2022-03-20 14:22:29

碑林区司法局:护航消费普法先行

2022-03-19 15:55:13

桑树坪矿“四项举措”筑牢矿井环保防线

2022-03-19 15:40:54

两岸专家:美日澳频打“台湾牌”加剧台海局势紧张

2022-01-11 10:33:28

人民网评:金色的盾牌,无悔的坚守

2022-01-11 10:33:28

数字政通:步入无人驾驶新赛道 与主线科技签署战略合作

2022-01-11 10:33:27

珠海高新区新政揽才 最高600万元住房补

2022-01-11 10:33:27

台胞在西安:“抗击疫情需要我们每一个人的力量”

2022-01-11 10:33:26

在京台生体验冰雪乐趣

2022-01-11 10:33:26

澳门未来发展有了新的时间表

2022-01-11 10:33:24

最高检:法治副校长应指导学校落实未成年人保护责任

2022-01-11 10:33:24

检察官担任法治副校长有了“指挥棒”

2022-01-11 10:33:22

全国首份《家庭教育令》来了!督促家长“依法带娃”

2022-01-11 10:33:22

俄军装甲车辆将具备隐身能力

2022-01-11 10:33:21

俄美双边对话 欧盟处境尴尬

2022-01-11 10:33:21

国资委:决战决胜国企改革三年行动 更好发挥国有经济战

2022-01-11 10:33:19

美海岸警卫队盯上濒海战斗舰

2022-01-11 10:33:19

证监会发布标准:统一金融行业对移动互联网应用程序的安

2022-01-11 10:33:17

日本声称以电磁炮加强防御

2022-01-11 10:33:17

2022-01-29 14:39:05

江苏南京:上万块显示屏支撑征兵宣传

2022-01-11 10:33:16

2022-01-29 14:39:05

广东省汕头市组织军地海上联合搜救演练

2022-01-11 10:33:14

2022-01-29 14:37:35

山东省枣庄军分区组织两级首长机关野营拉练

2022-01-11 10:33:08

2022-01-29 14:37:35

83年后,“故乡土”撒在烈士墓前

2022-01-11 10:33:06

岛与岸的守望:你的岛,我的岸

2022-01-11 10:33:04

2022-01-29 14:37:35

长武县税务局:聚焦企业需求 精准精细服务

2022-01-11 10:33:02

集安组织将就哈局势举行视频峰会

2022-01-11 10:33:02

美俄新一轮战略稳定对话前景不容乐观

2022-01-11 10:33:01

质效双优赋能奋进 咸阳市城管执法局召开项目推进会

2022-01-11 10:33:01

第二轮第五批中央生态环境保护督察全面完成督察进驻工作

2022-01-11 10:33:00

小小菜花架起保供连心桥——汉中桃心岛商贸公司在行动

2022-01-11 10:33:00

江秋莲诉刘暖曦生命权纠纷案一审宣判

2022-01-11 10:32:58

2022-01-29 14:37:21

新疆军区某团侦察连官兵边关巡逻

2022-01-11 10:32:54

第78集团军某旅实战化演练设强对手设难情况

2022-01-11 10:32:54

军队医疗待遇保障新规定如期落地有序施行

2022-01-11 10:32:52

2022-01-29 14:37:21

2022-01-29 14:37:21

荣盛发展:2021年签约金额1345.58亿元 同比增长5.87%

2022-01-11 10:32:50

2022-01-29 14:37:09

2022-01-29 14:37:09

证监会发布《关于北京证券交易所上市公司转板的指导意见

2022-01-11 10:32:47

2022-01-29 14:37:09

2022-01-29 14:37:09

奥密克戎高速传播 美国医疗物资供应无法满足病毒检测需求

2022-01-11 10:32:44

原住民控诉美政府在其家园进行“900次核试验”:这是种族

2022-01-11 10:32:42

2022-01-29 14:37:09

2022-01-29 14:37:09

2022-01-29 14:37:09

奥密克戎肆虐 美国公共服务受重创

2022-01-11 10:32:38

公安部1月10日举行新闻发布会

2022-01-11 10:32:38

詹姆斯·韦伯太空望远镜顺利“完全部署”

2022-01-11 10:32:37

日美联合委员会发布声明:驻日美军离开基地将仅限于必要

2022-01-11 10:32:37

【暖心警事】周至警方帮助走失母亲平安回家

2022-01-11 10:32:35

《我们北京见》 来自55位世界冠军的邀约

2022-01-11 10:32:35

世界冠军进校园 小小少年感受运动乐趣

2022-01-11 10:32:34

2022-01-29 14:36:57

时政Vlog丨带你体验冬奥运动员的备战日常

2022-01-11 10:32:32

伊朗制裁51名参与暗杀高级将领苏莱曼尼的美国人

2022-01-11 10:32:32

今年要有序推动绿色低碳发展

2022-01-11 10:32:30

加强绿色合作 助力共同发展

2022-01-11 10:32:28

电力碳减排要开好局(点睛)

2022-01-11 10:32:28

风光互补 绿能满满

2022-01-11 10:32:26

推动能源转型 赋能绿色发展

2022-01-11 10:32:26

今日腊八:两乡侈各健,一粥喜遥同

2022-01-11 10:32:25

2021年生态环境领域8项约束性指标顺利完成

2022-01-11 10:32:25

欧盟推进塑料回收再利用

2022-01-11 10:32:22

国家植物园 让保护体系更完整(美丽中国)

2022-01-11 10:32:22

2022-01-29 14:36:20

借力外脑,河南打出农业工程技术创新“组合拳”

2022-01-11 10:32:21