《使用 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>Sign-in in progress... 正在登录中……
然后,需要在路由里配置好这个回调页面:
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文件。
给 API 请求添加认证头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 请求添加上认证头。前提是,后端接口也使用同样的 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 前端
前情提要《使用IdentityServer保护Web应用(AntDPro前端+SpringBoot后端)》中记录了使用IdentitySer
-
中建环能:中建环能为环境技术产品与解决方案提供商,业务以研发、生产、销售设备为主,订单较为分散_今日热搜
中建环能(300425)12月19日在投资者关系平台上答复了投资者关心的问题。
-
禹州市夏都街道:持续发力 促创“五星”
近一段时期以来,禹州市夏都街道按照既定的“五星”创建目标实现计划,脚踏实地在各社区持续开展“五星”支部创建工作。明确要求街道各包社区
-
【世界时快讯】利拉德:我确实没什么成就 字母哥能带我进总决赛
利拉德:我确实没什么成就字母哥能带我进总决赛,利拉德,阿德托昆博,雄鹿队,波特兰开拓者
-
光华股份董秘回复:公司主要产品为粉末涂料用聚酯树脂
光华股份(001333)12月17日在投资者关系平台上答复了投资者关心的问题。投资者:请问房地产的利好对公司的效益有没有提高?光华股份董秘:您好
-
山东黄金(01787.HK)拟使用不超2.7亿元闲置募集资金暂时补充流动资金-环球新消息
山东黄金(01787 HK)发布公告,因募投项目属于矿山基本建设项目,由公司根据投资计划逐年分步投入,短期内出现募集资金暂时闲置。根据募集资金
-
天天微头条丨美思德(603041.SH)独立董事蒋剑春亲属短线交易公司股票
智通财经APP讯,美思德发布公告,公司于12月15日获悉公司独立董事蒋剑春先生的子女近期买卖公司股票的行为构成短线交易。本次短线交易所获收益
-
千盛集团控股(08475.HK)发布公告,该公司股份将于2022年12月16日上午9时正起恢复买卖_即时
千盛集团控股(08475 HK)发布公告,该公司股份将于2022年12月16日上午9时正起恢复买卖。
-
全球通讯!量化金融分析师考试科目是什么
量化金融分析师考试科目包括正课(六门)和高级选修课(任选两门)。正课课程由量化金融的构建基块、定量风险与回报、股票和货币、数据科学与
-
平顶山卫东区:下足四个“功夫”再掀创文热潮
12月6日,平顶山市卫东区建设路街道办事处出动20余名工作人员,沿街规范非机动车辆停放、排查更新创文宣传版面,以实际行动助力该市创文工作。
-
华宇软件:12月14日融券卖出金额101.04万元,占当日流出金额的1.53%|实时
同花顺数据中心显示,华宇软件12月14日获融资买入1327 40万元,占当日买入金额的27 24%,当前融资余额4 59亿元,占流通市值的8 93%,超过历史5
-
明冠新材(688560.SH):拟将“年产1.2亿平米光伏组件封装用POE胶膜扩建项目”延期至2023年12月14日
格隆汇12月14日丨明冠新材公布,公司于2022年12月14日召开第四届董事会第七次会议和第四届监事会第六次会议,审议通过了《关于上市首发部分募
-
全球实时:市地税局办税服务厅全国“工人先锋号”揭牌仪式举行
6月13日,市地税局办税服务厅全国“工人先锋号”揭牌仪式举行。省地税局党组成员、纪检组长李振明,副市长谭江出席揭牌仪式。据悉,“工人先锋
-
海优新材(688680)12月13日主力资金净卖出3635.01万元
截至2022年12月13日收盘,海优新材(688680)报收于153 98元,下跌4 02%,换手率3 12%,成交量1 61万手,成交额2 5亿元。12月13
-
今亮点!保证期间没有约定或者约定不明怎么办?
《民法典》第六百九十二条保证期间是确定保证人承担保证责任的期间,不发生中止、中断和延长。债权人与保证人可以约定保证期间,但是约定的保
-
全球观速讯丨快讯|最高奖励1000万元!长沙出台“招商新政十二条”
快讯|最高奖励1000万元!长沙出台“招商新政十二条”
-
环球微动态丨西藏珠峰: 第八届董事会提名与考核委员会第六次会议决议
西藏珠峰资源股份有限公司 第八届董事会提名与考核委员会第六次会议决议 西藏珠峰资源股份有限公司 公司第八
-
当前短讯!外源融资是什么?外源融资包括哪些
外源融资指的是企业通过一定方式向企业之外的其它经济主体筹集资金。外源融资包括:发行股票、企业债券、银行贷款等,此外,外源融资的范围还
-
美迪西(688202)10.04万股限售股将于12月12日解禁上市,占总股本0.12%_每日快播
根据市场公开信息整理,美迪西(688202)于12月12日将有10 04万股限售股份解禁上市,为公司股权激励一般股份,占公司总股本0 12%。最近一年内,该股累
-
天天速讯:中信证券:消费电子行业底部明确 建议关注两条主线
(原标题:中信证券:消费电子行业底部明确建议关注两条主线)证券时报网讯,中信证券指出,消费电子行业目前底部明确,伴随宏观波动边际减弱