Angular 8 配置 oidc

配置相对较为繁琐,最后会放上 Github 源码地址

新建一个 ng 项目

ng new angular-oidc

进入目录 cd angular-oidc

安装 oidc-client

npm i oidc-client --save

配置 oidc-client 参数

打开 environment.ts 将下面的代码覆盖原来的内容

import { WebStorageStateStore } from "oidc-client";export const environment = {production: false,authConfig: {authority: "http://localhost:57001",client_id: "query",redirect_uri: "http://localhost:4200/login-callback",response_type: "id_token token",scope: "openid profile",post_logout_redirect_uri: "http://localhost:4200",accessTokenExpiringNotificationTime: 4,filterProtocolClaims: true,silentRequestTimeout: 10000,loadUserInfo: true,userStore: new WebStorageStateStore({ store: window.localStorage }),},
};

需要修改的几个参数:

  • authority: 认证服务器,需要修改为自己的认证服务器
  • client_id: 客户端 id ,按照约定修改即可
  • redirect_uri: 认证服务器回调的客户端页面
  • post_logout_redirect_uri: 登出回调链接

模块划分

这里我们把模块划分为2块: 1) 游客模块 2) 用户模块

默认的壳组件所在的 module 作为游客模块, 另外还需要构建一个用户模块

游客模块

为了方便理解, 游客模块创建一个欢迎页, 点击继续按钮访问用户模块.

1. 创建一个欢迎页

没什么特别的作用, 就是为了方便理解单独设立的一个交互页面.

ng g c public/index

修改 index.component.html

WELLCOME TO ANGULAR OIDC

修改 index.component.ts

import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";@Component({selector: "app-index",templateUrl: "./index.component.html",styleUrls: ["./index.component.less"],
})
export class IndexComponent implements OnInit {constructor(private _router: Router) {}ngOnInit() {}public visitAuth(): void {this._router.navigate(["auth"]);}
}

2. 创建一个回调页

回调页是用户 oidc 认证结束后的回调, 起到一个过度的作用(目前先空着)

ng g c public/login-callback

3. 配置路由

打开 app-routing.module.ts, 对照修改

import { NgModule } from "@angular/core";
import { Routes, RouterModule } from "@angular/router";
import { IndexComponent } from "./public/index/index.component";
import { LoginCallbackComponent } from "./public/login-callback/login-callback.component";const routes: Routes = [{path: "",pathMatch: "full",component: IndexComponent,},{path: "login-callback",component: LoginCallbackComponent,},
];@NgModule({imports: [RouterModule.forRoot(routes)],exports: [RouterModule],
})
export class AppRoutingModule {}

启动程序 ng s -o, 这时候已经能看到一点点信息了, 不过还没有 home 路由, 下面来配置一下

用户模块

1. 添加一个 auth 模块

ng g m auth/auth --flat

--flat:在一个单独的文件夹创建

2. 将 auth 模块添加到壳组件

打开 app-module.ts, 主要修改一下内容

import { AuthModule } from "./auth/auth.module";
...imports: [..., AuthModule],

3. 添加 auth "壳组件"

ng g c auth/auth

4. 添加 auth 模块的路由

ng g m auth/auth-routing --flat

修改 auth-routing.module.ts 内容如下:

import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { AuthComponent } from "./auth/auth.component";const routes: Routes = [{path: "home",component: AuthComponent,},
];@NgModule({exports: [RouterModule],
})
export class AuthRoutingModule {}

5. 修改 app-routing.module.ts 添加 home 路由

const routes: Routes = [{path: "",pathMatch: "full",component: IndexComponent,},{path: "login-callback",component: LoginCallbackComponent,},{path: "home",component: AuthComponent,},
];

ctrl + c -> y 停止之前启动项目的终端, ng s 重新启动项目

此时的项目已经可以从游客路由跳转至用户路由,但我们是不允许游客默认访问用户路由的, 这时候就应该 守卫(Guard) 登场了。

配置守卫(Guard)

1. 添加 auth.service (认证相关的函数)

ng g s auth/auth --flat

替换 auth.service.ts 内容:

import { Injectable, EventEmitter } from '@angular/core';
import { environment } from 'src/environments/environment';
import { UserManager, User } from 'oidc-client';
import { Observable, from } from 'rxjs';@Injectable({providedIn: 'root'
})
export class AuthService {// 大多数 oidc-client 操作都在其中private manager: UserManager = new UserManager(environment.authConfig);// private manager: UserManager = undefined;// 登录状态改变事件public loginStatusChanged: EventEmitter = new EventEmitter();// localStorage 中存放用户信息的 Keyprivate userKey = `oidc.user:${environment.authConfig.authority}:${environment.authConfig.client_id}`;// private userKey = `oidc.user:${this._conf.env.authConfig.authority}:${this._conf.env.authConfig.client_id}`;constructor() {// 如果访问用的 token 过期,调用 login()this.manager.events.addAccessTokenExpired(() => {this.login();});}login() {this.manager.signinRedirect();}logout() {this.manager.signoutRedirect();}loginCallBack() {return Observable.create(observer => {from(this.manager.signinRedirectCallback()).subscribe((user: User) => {this.loginStatusChanged.emit(user);observer.next(user);observer.complete();});});}tryGetUser() {return from(this.manager.getUser());}get type(): string {return 'Bearer';}get user(): User | null {const temp = localStorage.getItem(this.userKey);if (temp) {const user: User = JSON.parse(temp);return user;}return null;}get token(): string | null {const temp = localStorage.getItem(this.userKey);if (temp) {const user: User = JSON.parse(temp);return user.access_token;}return null;}get authorizationHeader(): string | null {if (this.token) {return `${this.type} ${this.token}`;}return null;}
}

2. 添加 auth.guard

ng g g auth/auth --flat

选择 CanActivate

替换 auth.guard.ts 内容:

import { Injectable } from "@angular/core";
import {CanActivate,CanActivateChild,ActivatedRouteSnapshot,RouterStateSnapshot,UrlTree,
} from "@angular/router";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";
import { AuthService } from "./auth.service";
import { User } from "oidc-client";@Injectable({providedIn: "root",
})
export class AuthGuard implements CanActivate {constructor(private _auth: AuthService) {}canActivate(next: ActivatedRouteSnapshot,state: RouterStateSnapshot): Observable {return this.mapper(this._auth.tryGetUser());}private mapper = map((user: User) => {if (user) return true;this._auth.login();return false;});
}

3. 修改 app-routing.module.ts

import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { AuthComponent } from "./auth/auth.component";
import { C1Component } from "./test/c1/c1.component";
import { C2Component } from "./test/c2/c2.component";const routes: Routes = [{path: "home",component: AuthComponent,children: [{ path: "c1", component: C1Component },{ path: "c2", component: C2Component },],},
];@NgModule({imports: [RouterModule.forChild(routes)],exports: [RouterModule],
})
export class AuthRoutingModule {}

4. 修改 login-callback.component.ts

回到成功后,导航到 home 页,你也可以写更多的其他逻辑。

import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { User } from "oidc-client";
import { AuthService } from "src/app/auth/auth.service";@Component({selector: "app-login-callback",templateUrl: "./login-callback.component.html",styleUrls: ["./login-callback.component.less"],
})
export class LoginCallbackComponent implements OnInit {constructor(private _router: Router, private _auth: AuthService) {}ngOnInit() {this._auth.loginCallBack().subscribe((user: User) => {this._router.navigate(["home"]);});}
}

顺便美化一下下样式

login-callback.component.html:

登录成功,跳转中...

login-callback.component.less(我这里使用的是 less,你的可能是 css/scss/sass):

.callback-bar {margin: 0px 0px 0px 0px;padding: 8px 0px 0px 0px;font-size: 24px;font-weight: 600px;color: white;background-color: #3881bf;box-shadow: 0px 3px 5px #666;height: 50px;
}

再此重启一下程序(往往一些奇奇怪怪的问题重新启动后会被解决)。

这时候就已经实现了一个认证的过程,不过 auth 模块(用户模块)只有一个组件,总感觉不够直观,因此,我们需要在 auth 模块添加更多的组件,形成子路由,在观察功能。

添加 auth 子组件、子路由

修改 auth.component 组件

1. auth.component.html




2. auth.component.ts

import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";@Component({selector: "app-auth",templateUrl: "./auth.component.html",styleUrls: ["./auth.component.less"],
})
export class AuthComponent implements OnInit {constructor(private _router: Router) {}ngOnInit() {}public goC1(): void {this._router.navigate(["home/c1"]);}public goC2(): void {this._router.navigate(["home/c2"]);}
}

新建子路由

2. 添加 c1、c2 子组件

ng g c auth/test/c1
ng g c auth/test/c2

保持默认内容即可。

3. 修改 auth-routing.module.ts

import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { AuthComponent } from "./auth/auth.component";
import { C1Component } from "./test/c1/c1.component";
import { C2Component } from "./test/c2/c2.component";const routes: Routes = [{path: "home",component: AuthComponent,children: [{ path: "c1", component: C1Component },{ path: "c2", component: C2Component },],},
];@NgModule({imports: [RouterModule.forChild(routes)],exports: [RouterModule],
})
export class AuthRoutingModule {}

重启项目,这时候得到一个错误信息:

Error: Template parse errors:
'router-outlet' is not a known element:

这表示 auth 模块没有引入 RouterModule,其实是我们的 auth.module.ts 没有引入 auth-routing.module.ts 导致的(routing 中有引入 RouterModule)

修改 auth.module.ts:

...
import { AuthRoutingModule } from './auth-routing.module';@NgModule({...imports: [..., AuthRoutingModule],
})

重启项目,可以看到现在基本功能都已经实现了,不过还差一个退出功能。

退出登录

1. 修改 auth.component.html




2. 修改 auth.component.ts

import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { AuthService } from "../auth.service";@Component({selector: "app-auth",templateUrl: "./auth.component.html",styleUrls: ["./auth.component.less"],
})
export class AuthComponent implements OnInit {constructor(private _router: Router, private _auth: AuthService) {}ngOnInit() {}public goC1(): void {this._router.navigate(["home/c1"]);}public goC2(): void {this._router.navigate(["home/c2"]);}public exit(): void {this._auth.logout();}
}

重启测试,退出成功!

访问 /home 自动跳转登录,没问题。

访问 /home/c1 居然跳过了认证,直接进来了!

造成这个问题的原因是但是我们的守卫添加的方式是 canActivatecanActivate只会保护本路由,而不会保护其子路由。因此,我们还需要保护子路由!

保护子路由

1. 修改 auth.guard.ts

import { Injectable } from "@angular/core";
import {CanActivate,CanActivateChild,ActivatedRouteSnapshot,RouterStateSnapshot,UrlTree,
} from "@angular/router";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";
import { AuthService } from "./auth.service";
import { User } from "oidc-client";@Injectable({providedIn: "root",
})
export class AuthGuard implements CanActivate, CanActivateChild {constructor(private _auth: AuthService) {}canActivate(next: ActivatedRouteSnapshot,state: RouterStateSnapshot): Observable {return this.mapper(this._auth.tryGetUser());}canActivateChild(next: ActivatedRouteSnapshot,state: RouterStateSnapshot):| Observable| Promise| boolean| UrlTree {return this.mapper(this._auth.tryGetUser());}private mapper = map((user: User) => {if (user) return true;this._auth.login();return false;});
}

2. 修改 auth-routing.module.ts
主要修改代码如下:

import { AuthGuard } from "./auth.guard"; // <- hereconst routes: Routes = [{path: "home",component: AuthComponent,canActivateChild: [AuthGuard], // <- herechildren: [{ path: "c1", component: C1Component },{ path: "c2", component: C2Component },],},
];

重启项目,再此访问 ‘/home/c1’,成功跳转,访问 ‘/home’,同样成功跳转。

Github

angular-oidc


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部