Angular中的OIDC的使用

相关的概念解释

  • 单点登录

当用户在身份认证服务器上登录一次以后,即可获得访问单点登录系统中其他关联系统和应用软件的权限,用户只需一次登录就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,SSO包括统一的登录和统一的登出这两部分。基于OIDC实现的SSO主要是利用OIDC服务作为用户认证中心作为统一入口,使得所有的需要登录的地方都交给OIDC服务来做。更直白点说就是把需要进行用户认证的客户端中的用户认证这部分都剥离出来交给OIDC认证中心来做。

  • token

就是在用户名密码登陆后,后台生成一个token,然后客户端保存token,考虑到token的通用性和广泛性,因此慢慢的就有了一些token定义的规范,一个目前比较通用的token规范就是jwt。

  • jwt

是json web token的简称,标准的jwt格式token包含了三段,分别是token头、token主题和token签名,jwt只是处理token规范的问题

  • OAuth2.0

授权码模式,OAuth2.0解决的是通过令牌获取某个系统的操作权限,因为有clientId的标识,一次登陆只能对该系统生效,第三方应用的操作用户不是鉴权系统的官方用户,授权权限鉴权中心可以做限制。
标准的oauth2的token同样是遵循jwt标准,不同的是,在普通jwt的token基础上加入了更多的内容。
oauth2标准的token有两个:"access_token和refresh_token"。
access_token用来访问资源时授权,包含基础授权信息,refresh_token的作用是在access_token失效后直接续签access_token。

举例子:微博就是客户端,QQ就是认证服务器,
OAuth2.0就是客户端和认证服务器之间由于相互不信任而产生的一个授权协议

  1. 第一步:在微博官网点击用qq登录
  2. 第二步:跳转到qq登录页面输入用户名密码,然后点授权并登录
  3. 第三步:跳回到微博页面,成功登录
    首先接上一步,QQ服务器在判断登录成功后,使页面重定向到之前微博发来的callback并附上code授权码,即 callback=www.weibo.com/callback

页面接到重定向,发起 http://www.weibo.com/callback 请求

微博服务器收到请求后,再次与QQ沟通,即模拟浏览器发起了两次请求。一个是用拿到的Authorization Code获取Access Token,另一个就是用拿到的token换取用户信息。最后将用户信息储存起来,返回给浏览器其首页的视图。到此OAuth2.0授权结束。

  • oidc

这个技术解决的就是外部第三方交互时的一个身份认证问题。
结合上边所说,认证和授权(authentication 和 authorization)分别属于不同的范畴,解决的也都是不同的问题,因此在这种涉及到第三方,如果再涉及到资源权限的系统中,就会引入一个新的规范,即oidc。
OIDC=(Identity, Authentication) + oauth2,就是openid+oauth2。
具体理解: OIDC在oauth2的基础上又加了一个token,叫做id_token。
id_token同样的遵循jwt规范,只不过里边的内容是能够体现用户身份的信息,OIDC里就会有三个token:access_token、refresh_token和id_token

Angular具体项目的使用

首先angular项目使用到oidc-client第三方库

前端的应用流程

  1. 访问前端地址, 如果没有登录用户, 那么跳转到Authorization Server进行登陆, 同意后, 返回到前端的网站.

  2. 如果前端网站有登录的用户, 那么在用户快过期的时候自动刷新token. 以免登陆过期.

  3. 前端应用访问api时, 自动拦截所有请求, 把登陆用户的access token添加到请求的authorization header, 然后再发送给 web api.

所以这将涉及到AuthService, Auth-token.interceptor,AuthGuard等文件的使用
// 创建AuthService

import { Injectable, NgZone } from '@angular/core';
import { User, UserManager, WebStorageStateStore } from 'oidc-client';
import { ENVIRONMENT } from '@env/environment';
import { SessionStorageService } from '@shared/services';

@Injectable()
export class AuthService {
  public isLoggedIn: boolean = false;
  public manager: UserManager; // UserManager就是oidc-client里面的东西. 我们主要是用它来操作.
  public accessToken: string = '';
  public currentUser: User;
  constructor(private _ngZone: NgZone, private _session: SessionStorageService) {
    const origin = `${window.location.protocol}//${window.location.hostname}${window.location.port ?
      (':' + window.location.port) : ''}`;
    const settings = {
      authority: '',// 就是authorization server(上述的QQ)的地址.
      client_id: '' // 发起认证请求的客户端的唯一标识,这个客户端事先已经在oidc-server.dev这个站点注册过了,
      response_type: 'id_token token'// 区别于oauth2授权请求的一点,必须包含有id_token这一项,
      scope: '' //区别于oauth2授权请求的一点,必须包含有openid这一项,
      redirect_uri: '',// 是登陆成功后跳转回来的地址
      post_logout_redirect_uri: origin, // OIDC / OAuth2注销后重定向UR
      staleStateAge: ,// 一个数字(以秒为单位),指示用于授权被认为已放弃并可以清除的请求的状态的存储状态
      automaticSilentRenew: boolean, //为true是启用自动安静刷新token.
    };
    this._ngZone.runOutsideAngular(() => {
      this.manager = new UserManager(settings);
    });

  }

  public login(): void {
  // 方法里面的signInRedirect()会直接跳转到Authorization Server的登陆窗口
    this.manager.signinRedirect();
  }

  public async getUser(): Promise<User> {
    const user = await this.manager.getUser();
    this.currentUser = user;
    if (user) {
      this.accessToken = user.access_token;
      this.isLoggedIn = true;
    }
    return user;
  }

  public async signinRedirectCallback(): Promise<void> {
    const user = await this.manager.signinRedirectCallback();
  }

  public getToken(): string {
    return this.accessToken;
  }

  public logout(): void {
    this.isLoggedIn = false;
    this._session.clearAll();
    localStorage.clear();
    // 里的signoutRedirect()就会跳转到AuthorizationServer并执行登出.
    this.manager.signoutRedirect({id_token_hint: this.currentUser.id_token});
  }
}

// 创建access-token.interceptor

import { Injectable } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http';
import { AuthService } from './auth.service';
import { Observable } from 'rxjs';

@Injectable()
export class AuthTokenInterceptor implements HttpInterceptor {
  constructor(private auth: AuthService) { }

  public intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const authToken = `Bearer ${this.auth.getToken()}`;
    let req;
     req = request.clone({
        setHeaders: {
             Authorization: authToken,
            },
          });
    return next.handle(req);
  }
}
// 创建auth-guard文件

import { CanActivate, CanLoad, Route, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AuthService } from './auth.service';
import { Injectable } from '@angular/core';

@Injectable()
export class AuthGuard implements CanLoad, CanActivate {

  constructor(private auth: AuthService) { }
// 如果返回true就可以访问这个路由, 否则就不可以访问
// 取当前用户, 如果有用户那么就可以继续访问路由, 否走执行登陆动作.
  public async canLoad(route: Route): Promise<boolean> {
    const user = await this.auth.getUser();
    const isLoggedIn = !!user;
    if (!isLoggedIn) {
      this.auth.login();
      return false;
    }
    return true;
  }

  public async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
    const user = await this.auth.getUser();
    const isLoggedIn = !!user;
    if (!isLoggedIn) {
      this.auth.login();
      return false;
    }
    return true;
  }
}

自动刷新Token:
oidc-client的自动刷新token是只要配置好了, 你就不用再做什么操作了.

不过还是需要建立一个页面, 用于刷新:
<!doctype html>
<html lang="zh">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>test</title>
</head>

<body>
  <p>登录成功,正在跳转页面……</p>
  <script src="/assets/oidc/oidc-client.js"></script>
  <script>
    var mgr = new Oidc.UserManager();
    mgr.signinRedirectCallback().then(...);

  </script>
</body>

</html>

今天也是在看项目的时候发现这块不是特别的熟悉,故查阅一些资料并结合当下的项目进行了相关的总结,希望这里的概念以及代码示例对大家有所帮助~ https://github.com/IdentityModel/oidc-client-js/wiki这个链接是对oidc-client的详细介绍哦~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容