
实现细节
RDC的统一身份认证中心使用的是 identity4,前端对接的js框架是 oidc-client-js,而这个框架默认是不支持 VUE 使用的 ES6 的模块,所以需要手动修改,在 oidc-client.min.js 文件的最后添加:
|
|
这样才能在 VUE 项目中通过 import 引入:
|
|
RDC 是通过在主框架 layout.vue 组件的 beforeRouteEnter 路由守护函数中检测是否在身份认证中心登录来处理进一步的路由跳转,而不是 identify4 官方示例中的 callback.html 跳转页面切换:
- beforeRouteEnter (to, from, next) {
- // 已经登录了
- if (getToken()) {
- // 直接进入到访问页面
- toNext(next)
- } else {
- // 调用统一身份认证库的方法,获取用户登录信息
- new Oidc.UserManager({response_mode: 'query'}).signinRedirectCallback().then((user) => {
- // 已经登录
- if (user) {
- let url = user.state
-
- // 缓存统一身份认证的用户信息
- localStorage.setItem('rdc.identity.user', JSON.stringify(user))
- // 创建 Oidc.UserManager 实例
- $mgr = new Oidc.UserManager(DEV_CONFIG)
-
- // 登录跳转
- signIn(user, next, url)
- } else {
- $mgr = new Oidc.UserManager(DEV_CONFIG)
- checkSignIn(next)
- }
- }).catch((err) => {
- console.log('认证错误信息:', err)
- // 跳转到登录页面
- let currentPath = `${location.pathname + location.search}`
- next(`/login?returnUrl=${currentPath}`)
- })
- }
- }
其中 DEV_CONFIG 是通过在RDC后台的认证授权添加授权应用获取到的,获取到类似的配置:
- export const DEV_CONFIG0 = {
- // 配置中心的地址
- 'authority': 'http://localhost:5000',
- // 注册的客户ID
- 'client_id': 'rdc-localhost',
- // 接入认证站点在认证中心登录有跳转的地址
- 'redirect_uri': 'http://localhost:5001/console',
- 'response_type': 'code',
- // 授权的 API 或者服务
- 'scope': 'openid profile defaultApi rdc-file-service rdc-resourcecenter-service',
- // 接入站点未登录的跳转地址
- 'post_logout_redirect_uri': 'http://localhost:5001/login',
- 'response_mode': 'query'
- }
post_logout_redirect_uri(http://localhost:5001/login) 页面的处理方式:
- <div>div>
-
- <script>
- // isString,
- import {getQuery} from '../../utils/utils'
- import Oidc from '../../utils/oidc-client.min'
- import {DEV_CONFIG} from '../../identity-config'
-
- export default {
- name: 'login',
- created () {
- let query = getQuery()
- let url = query && query.returnUrl ? query.returnUrl : '/'
-
- localStorage.setItem('rdc.return.url', url)
-
- this.loadData()
- },
- methods: {
- loadConfig () {
- let query = getQuery()
- let url = query && query.returnUrl ? query.returnUrl : '/'
-
- this.$mgr = new Oidc.UserManager(DEV_CONFIG)
- this.$mgr.signinRedirect({
- state: url
- })
- },
- loadData () {
- return this.loadConfig()
- }
- }
- }
- script>
这个页面是一个空白页,主要是用来获取配置,并调用 signinRedirect 跳转到认证中心。只是使用了一个hack技巧,使用 state 参数传递用户当前访问的页面,以便用户在认证中心页面登录后,直接进入用户要访问的目标页面:
- // 获取地址栏的query信息
- let query = getQuery()
- // 获取跳转登录前用户正在访问的页面地址
- let url = query && query.returnUrl ? query.returnUrl : '/'
-
- this.$mgr = new Oidc.UserManager(DEV_CONFIG)
- this.$mgr.signinRedirect({
- // 通过 state 传递地址
- state: url
- })
与这里的 state 相关对应的就是登录成功页面这样的处理方式:
- // 调用统一身份认证库的方法,获取用户登录信息
- new Oidc.UserManager({ response_mode: 'query' }).signinRedirectCallback().then((user) => {
- // 已经登录
- if (user) {
- // 登陆有后,用户信息的 state 会返回登陆前要访问的目标地址
- let url = user.state
-
-
- // 缓存统一身份认证的用户信息
- localStorage.setItem('rdc.identity.user', JSON.stringify(user))
- // 创建 Oidc.UserManager 实例
- $mgr = new Oidc.UserManager(DEV_CONFIG)
-
- // 登录跳转
- signIn(user, next, url)
- } else {
- $mgr = new Oidc.UserManager(DEV_CONFIG)
- checkSignIn(next)
- }
- }).catch((err) => {
- console.log('认证错误信息:', err)
- // 跳转到登录页面
- let currentPath = `${location.pathname + location.search}`
- next(`/login?returnUrl=${currentPath}`)
- })
IE11 兼容问题
oidc-client-js 框架默认扩展了ES6方法,注册了 babel-polyfill,而之前处理IE兼容问题也单独引入过 babel-polyfill,同时引入两个 babel-polyfill 就会报错,不允许这么处理。需要去掉单独引入的 polyfill,或者拉取 oidc-client.js 的代码,移除 babel-polyfill 引用,RDC 采用直接移除单独引入的(移除 oidc-client.js 比较麻烦)。
移除单独引入的 polyfill 还有问题,因为 oidc-client.js 的 polyfill 是通过闭包函数传递的,算是一个私有的对象,VUE 项目中是无法引用扩展的方法的。而 RDC 引入的另外一个处理日期JS库 luxon.js 调用了ES6的 Object.assign() 方法,为了兼容IE11(IE11没有该方法),我们需要全局扩展 Object.assign() 方法,在index.html文件中添加内联的JS代码块。
- // 代码来源 MDN:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
- if (typeof Object.assign !== 'function') {
- // Must be writable: true, enumerable: false, configurable: true
- Object.defineProperty(Object, 'assign', {
- value: function assign (target, varArgs) { // .length of function is 2
- 'use strict'
- if (target === null || target === undefined) {
- throw new TypeError('Cannot convert undefined or null to object')
- }
-
- let to = Object(target)
-
- for (let index = 1; index < arguments.length; index++) {
- let nextSource = arguments[index]
-
- if (nextSource !== null && nextSource !== undefined) {
- for (let nextKey in nextSource) {
- // Avoid bugs when hasOwnProperty is shadowed
- if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
- to[nextKey] = nextSource[nextKey]
- }
- }
- }
- }
- return to
- },
- writable: true,
- configurable: true
- })
- }