内容简介:进入目录打开
配置相对较为繁琐,最后会放上 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 redirect_uri post_logout_redirect_uri
模块划分
这里我们把模块划分为 2
块: 1) 游客模块
2) 用户模块
默认的壳组件所在的 module 作为游客模块, 另外还需要构建一个用户模块
游客模块
为了方便理解, 游客模块创建一个欢迎页, 点击继续按钮访问用户模块.
1. 创建一个欢迎页
没什么特别的作用, 就是为了方便理解单独设立的一个交互页面.
ng g c public/index
修改 index.component.html
<h3>WELLCOME TO ANGULAR OIDC</h3> <input type="button" value="visit" (click)="visitAuth()">
修改 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<User> = new EventEmitter(); // localStorage 中存放用户信息的 Key private 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<boolean> { 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
:
<div class="callback-bar"> <span style="margin-left: 10px;">登录成功,跳转中...</span> </div>
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
<div> <input type="button" value="c1" (click)="goC1()"> <input type="button" value="c2" (click)="goC2()"> </div> <div> <router-outlet></router-outlet> </div>
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
<div> <input type="button" value="c1" (click)="goC1()"> <input type="button" value="c2" (click)="goC2()"> <input type="button" value="exit" (click)="exit()"> </div> <div> <router-outlet></router-outlet> </div>
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
居然跳过了认证,直接进来了!
造成这个问题的原因是但是我们的守卫添加的方式是 canActivate
, canActivate
只会保护本路由,而不会保护其子路由。因此,我们还需要保护子路由!
保护子路由
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<boolean> { return this.mapper(this._auth.tryGetUser()); } canActivateChild( next: ActivatedRouteSnapshot, state: RouterStateSnapshot ): | Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | 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"; // <- here const routes: Routes = [ { path: "home", component: AuthComponent, canActivateChild: [AuthGuard], // <- here children: [ { path: "c1", component: C1Component }, { path: "c2", component: C2Component }, ], }, ];
重启项目,再此访问 '/home/c1',成功跳转,访问 '/home',同样成功跳转。
Github
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 6、如何获取配置中心的配置
- React降级配置及Ant Design配置
- vscode 配置eslint 开发vue的相关配置
- git commit 规范校验配置和版本发布配置
- hadoop地址配置、内存配置、守护进程设置、环境设置
- 在hibernate中配置事务级别与命名查询配置【原创】
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Ordering Disorder
Khoi Vinh / New Riders Press / 2010-12-03 / USD 29.99
The grid has long been an invaluable tool for creating order out of chaos for designers of all kinds—from city planners to architects to typesetters and graphic artists. In recent years, web designers......一起来看看 《Ordering Disorder》 这本书的介绍吧!