Angular 8 配置 oidc

Angular 8 配置 oidc配置相对较为繁琐 最后会放上 Github 源码地址新建一个 ng 项目 ngnewangular oidc 进入目录 cdangular oidc 安装 oidc clientnpmioi clientsave 配置 oidc client 参数打开 environment ts 将下面的代码覆盖原来的内容 import WebStorageSt

配置相对较为繁琐,最后会放上 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 中存放用户信息的 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 
  
    { 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"; // <- here const routes: Routes = [ { path: "home", component: AuthComponent, canActivateChild: [AuthGuard], // <- here children: [ { path: "c1", component: C1Component }, { path: "c2", component: C2Component }, ], }, ]; 

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

Github

angular-oidc

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/177381.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月26日 下午7:39
下一篇 2026年3月26日 下午7:39


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号