angular 接入 IdentityServer4

angular 接入 IdentityServer4

Intro

最近把活动室预约的项目做了一个升级,预约活动室需要登录才能预约,并用 IdentityServer4 做了一个统一的登录注册中心,这样以后就可以把其他的需要用户操作的应用统一到 IdentityServer 这里,这样就不需要在每个应用里都做一套用户的机制,接入 IdentityServer 就可以了。

目前活动室预约的服务器端和基于 angular 的客户端已经完成了 IdentityServer 的接入,并增加了用户的相关的一些功能,比如用户可以查看自己的预约记录并且可以取消自己未开始的预约,

还有一个小程序版的客户端暂时还未完成接入,所以小程序版目前暂时是不能够预约的

为什么要写这篇文章

目前在网上看到很多都是基于 implicit 模式接入 IdentityServer,这样实现起来很简单,但是现在 OAuth 已经不推荐这样做了,OAuth 推荐使用 code 模式来代替 implicit

implicit 模式会有一些安全风险,implicit 模式会将 accessToken 直接返回到客户端,而 code 模式只是会返回一个 code,accessToken 和 code 的分离的两步,implicit 模式很有可能会将 token 泄露出去

详细可以参考 StackOverflow 上的这个问答

https://stackoverflow.com/questions/13387698/why-is-there-an-authorization-code-flow-in-oauth2-when-implicit-flow-works

除此之外,还有一个小原因,大多是直接基于 oidc-client  的 一个 npm 包来实现的,我是用了一个针对 angular 封装的一个库 angular-oauth2-oidc,如果你在用 angular ,建议你可以尝试一下,针对 angular 做了一些封装和优化,对 angular 更友好一些

准备接入吧

API 配置

预约系统的 API 和网站管理系统是在一起的,针对需要登录才能访问的 API 单独设置了的 policy 访问

services.AddAuthentication().AddIdentityServerAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme, options =>{options.Authority = Configuration["Authorization:Authority"];options.RequireHttpsMetadata = false;options.NameClaimType = "name";options.RoleClaimType = "role";});services.AddAuthorization(options =>
{options.AddPolicy("ReservationApi", builder => builder.AddAuthenticationSchemes(IdentityServerAuthenticationDefaults.AuthenticationScheme).RequireAuthenticatedUser().RequireScope("ReservationApi"));
});

需要授权才能访问的接口设置 Authorize 并指定 Policy 为 ReservationApi

[Authorize(Policy = "ReservationApi")]
[HttpPost]
public async Task<IActionResult> MakeReservation([FromBody] ReservationViewModel model)

IdentityServer Client 配置

首先我们需要在 IdentityServer 这边添加一个客户端,因为我们要使用 code 模式,所以授权类型需要配置 authorization-code 模式,不使用 implicit 模式

允许的作用域(scope) 是客户端允许访问的 api 资源和用户的信息资源,openid 必选,profile 是默认的用户基本信息的集合,根据自己客户端的需要进行配置,ReservationApi 是访问 API 需要的 scope,其他的 scope 根据客户端需要进行配置

angular 客户端配置

安装 angular-oauth2-oidc npm 包,我现在使用的是 9.2.0 版本

添加 oidc 配置:

export const authCodeFlowConfig: AuthConfig = {issuer: 'https://id.weihanli.xyz',// URL of the SPA to redirect the user to after loginredirectUri: window.location.origin + '/account/callback',clientId: 'reservation-angular-client',dummyClientSecret: 'f6f1f917-0899-ef36-63c8-84728f411e7c',responseType: 'code',scope: 'openid profile ReservationApi offline_access',useSilentRefresh: false,showDebugInformation: true,sessionChecksEnabled: true,timeoutFactor: 0.01,// disablePKCI: true,clearHashAfterLogin: false
};

在 app.module 引入 oauth 配置

  imports: [BrowserModule,AppRoutingModule,AppMaterialModule,HttpClientModule,FormsModule,ReactiveFormsModule,BrowserAnimationsModule,OAuthModule.forRoot({resourceServer: {allowedUrls: ['https://reservation.weihanli.xyz/api'],sendAccessToken: true}})]

OAuthModule 里 resourceServer 中的 allowedUrls 是配置的资源的地址,访问的资源符合这个地址时就会自动发送 accessToken,这样就不需要自己实现一个 interceptor 来实现自动在请求头中设置 accessToken 了

在 AppComponment 的构造器中初始化 oauth 配置,并加载 ids 的发现文档

export class AppComponent {constructor(private oauth: OAuthService) {this.oauth.configure(authConfig.authCodeFlowConfig);this.oauth.loadDiscoveryDocument();}// ...
}

添加一个 AuthGuard,路由守卫,需要登录才能访问的页面自动跳转到 /account/login 自动登录

AuthGuard:

import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { OAuthService } from 'angular-oauth2-oidc';@Injectable({providedIn: 'root'
})
export class AuthGuard implements CanActivate {constructor(private router: Router, private oauthService: OAuthService) {}canActivate() {if (this.oauthService.hasValidAccessToken()) {return true;} else {this.router.navigate(['/account/login']);return false;}}
}

路由配置:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ReservationListComponent } from './reservation/reservation-list/reservation-list.component';
import { NoticeListComponent } from './notice/notice-list/notice-list.component';
import { NoticeDetailComponent } from './notice/notice-detail/notice-detail.component';
import { AboutComponent } from './about/about.component';
import { NewReservationComponent } from './reservation/new-reservation/new-reservation.component';
import { LoginComponent } from './account/login/login.component';
import { AuthGuard } from './shared/auth.guard';
import { AuthCallbackComponent } from './account/auth-callback/auth-callback.component';
import { MyReservationComponent } from './account/my-reservation/my-reservation.component';const routes: Routes = [{ path: '', component: ReservationListComponent },{ path: 'reservations/new', component:NewReservationComponent, canActivate: [AuthGuard] },{ path: 'reservations', component: ReservationListComponent },{ path: 'notice', component: NoticeListComponent },{ path: 'notice/:noticePath', component: NoticeDetailComponent },{ path: 'about', component: AboutComponent },{ path: 'account/login', component: LoginComponent },{ path: 'account/callback', component: AuthCallbackComponent },{ path: 'account/reservations', component: MyReservationComponent, canActivate: [AuthGuard] },{ path: '**', redirectTo: '/'}
];@NgModule({imports: [RouterModule.forRoot(routes)],exports: [RouterModule]
})
export class AppRoutingModule { }

AccountLogin 会将用户引导到 ids 进行登录,登录之后会跳转到配置的重定向 url,我配置的是 account/callback

import { Component, OnInit } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';@Component({selector: 'app-login',templateUrl: './login.component.html',styleUrls: ['./login.component.less']
})
export class LoginComponent implements OnInit {constructor(private oauthService: OAuthService) {}ngOnInit(): void {// 登录this.oauthService.initLoginFlow();}}

Auth-Callback

import { Component, OnInit } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';
import { Router } from '@angular/router';@Component({selector: 'app-auth-callback',templateUrl: './auth-callback.component.html',styleUrls: ['./auth-callback.component.less']
})
export class AuthCallbackComponent implements OnInit {constructor(private oauthService: OAuthService, private router:Router) {}ngOnInit(): void {this.oauthService.loadDiscoveryDocumentAndTryLogin().then(_=> {this.oauthService.loadUserProfile().then(x=>{this.router.navigate(['/reservations/new']);});});}}

More

当前实现还不太完善,重定向现在始终是跳转到的新预约的页面,应当在跳转登录之前记录一下当前的地址保存在 storage 中,在 auth-callback 里登录成功之后跳转到 storage 中之前的地址

更多信息可以参考 angular 源码 <https://github.com/OpenReservation/angular-client>  API 源码:<https://github.com/OpenReservation/ReservationServer>

Reference

  • https://sunnycoding.cn/2020/03/14/angular-spa-auth-with-ocelot-and-ids4-part3/#i-2

  • https://github.com/OpenReservation/angular-client

  • https://github.com/manfredsteyer/angular-oauth2-oidc/

  • https://github.com/OpenReservation/ReservationServer

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/309126.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

主机Redis服务迁移到现有Docker Overlay网络

“《麻雀虽小&#xff0c;五脏俱全》之主机现有Redis服务迁移到Docker Swarm Overlay网络&#xff0c;并搭建高可用容器集群。hello, 好久不见&#xff0c;之前文章记录了一个实战的2C分布式项目的改造过程&#xff0c;结果如下&#xff1a;其中Redis并未完成容器化改造&#x…

Java控制结构

控制结构 程序流程控制介绍 顺序控制 分支控制if-else 单分支 案例演示 01: import java.util.Scanner; public class IfWorkDemo {public static void main(String[] args){Scanner myScanner new Scanner(System.in);System.out.println("input your age");int…

.Net Core Configuration源码探究

前言上篇文章我们演示了为Configuration添加Etcd数据源&#xff0c;并且了解到为Configuration扩展自定义数据源还是非常简单的&#xff0c;核心就是把数据源的数据按照一定的规则读取到指定的字典里&#xff0c;这些都得益于微软设计的合理性和便捷性。本篇文章我们将一起探究…

面试官:你说你喜欢研究新技术,那么请说说你对 Blazor 的了解

阅读本文大概需要 1.5 分钟。最近在几个微信 .NET 交流群里大家讨论比较频繁的话题就是这几天自己的面试经历。面试官&#xff1a;“你刚说你喜欢研究新技术&#xff0c;那么你对 Blazor 了解多少&#xff1f;”作为一位专注于 .NET 开发的软件工程师&#xff0c;你好意思说你对…

Java变量

变量 ​ 变量是程序的基本组成单位 变量的介绍 概念 变量相当于内存中一个数据存储空间的表示&#xff0c;你可以把变量看做是一个房间的门牌号&#xff0c;通过门牌号我们可以找到房间&#xff0c;而通过变量名可以访问到变量(值)。 01&#xff1a; class Test {public s…

[Student.Achieve] 学生教务管理系统开源

&#xff08;源自&#xff1a;https://neters.club&#xff09;一觉醒来Github改版了&#xff0c;我个人还是挺喜欢的????。还有两个月就是老张做开源两周年了&#xff0c;时间真快&#xff0c;也慢慢的贡献了很多的开源作品&#xff0c;上边的是主要的七个作品&#xff0c…

.NET Core HttpClient源码探究

前言在之前的文章我们介绍过HttpClient相关的服务发现&#xff0c;确实HttpClient是目前.NET Core进行Http网络编程的的主要手段。在之前的介绍中也看到了&#xff0c;我们使用了一个很重要的抽象HttpMessageHandler&#xff0c;接下来我们就探究一下HttpClient源码&#xff0c…

Java 多线程:线程优先级

1 优先级取值范围 Java 线程优先级使用 1 ~ 10 的整数表示&#xff1a; 最低优先级 1&#xff1a;Thread.MIN_PRIORITY 最高优先级 10&#xff1a;Thread.MAX_PRIORITY 普通优先级 5&#xff1a;Thread.NORM_PRIORITY 2 获取线程优先级 public static void main(String[]…

《Unit Testing》1.1 -1.2 单元测试的目的

本系列是《Unit Testing》 一书的读书笔记 精华提取。书中的例子 C# 语言编写&#xff0c;但概念是通用的&#xff0c;只要懂得面向对象编程就可以。 单元测试当前的状态目前&#xff0c;在&#xff08;美国的&#xff09;大部分公司里&#xff0c;单元测试都是强制性的。生产…

Java Exception

Exception 异常捕获 将代码块选中->ctrlaltt->选中try-catch 01: public class Exception01 {public static void main(String[] args) {int n1 10;int n2 0;try {int res n1/n2;} catch (Exception e) { // e.printStackTrace();System.out.println(e.…

《Unit Testing》1.3 使用覆盖率指标来度量测试套件的好坏

使用覆盖率来度量测试套件&#xff08;Test Suite&#xff09;的质量有两种比较流行的测试覆盖率的度量方法&#xff1a;代码覆盖率分支覆盖率覆盖率度量会显示一个测试套件&#xff08;Test Suite&#xff09;会执行多少代码&#xff0c;范围从 0 至 100%。除了上述两种方法之…

Linux创始人:v5.8是有史以来最大的发行版之一

导语Linux v5.8已经修改了所有文件的20&#xff05;&#xff0c;是迄今为止变化最大的一次发行版。正文Linux创始人Linus Torvalds表示&#xff1a;Linux内核5.8版是“我们有史以来最大的发行版之一”。如果一切顺利&#xff0c;Linux v5.8稳定版应该在2020年8月的某个时候出现…

[高等数学]这你不背?

求导及求微分的基本公式: 泰勒中值定理: 麦克劳林公式: 不定积分公式: 凑微分: 第二类换元积分法常用的三种情况: 求高阶导数的几个公式: 二阶常系数非齐次线性微分方程的特解: 排列组合公式: C的计算&#xff1a; 下标的数字乘以上标的数字的个数,且每个数字都要-1.再除以上标…

怎么开会才不浪费时间?

这里是Z哥的个人公众号每周五11&#xff1a;45 按时送达当然了&#xff0c;也会时不时加个餐&#xff5e;我的第「148」篇原创敬上大家好&#xff0c;我是Z哥&#xff0c;先祝大家端午节日快乐。节日期间就发篇比较短的文章吧。人在职场混&#xff0c;开会应该是本职工作之外花…

.NET 5.0预览版6发布:支持Windows ARM64设备

2020年6月25日&#xff0c;微软dotnet团队在博客宣布了第六个 .NET 5.0 的预览版&#xff1a;https://devblogs.microsoft.com/dotnet/announcing-net-5-0-preview-6/&#xff0c;在改进性能的同时增加了一些新的功能。ASP.NET Core和 EF Core也将于今日发布了。注意&#xff1…

利用真值表法求取主析取范式以及主合取范式的实现(C++)

代码如下: #include <iostream> #include <stack> #include <string> #include <vector> using namespace std; const int N 300; stack<char> s; stack<char> v; int seq; bool vis[N]; bool flag[N]; void dfs(int n); vector<int&…

基于 Blazor 开发五子棋小游戏

今天是农历五月初五&#xff0c;端午节。在此&#xff0c;祝大家端午安康&#xff01;端午节是中华民族古老的传统节日之一。端午也称端五&#xff0c;端阳。此外&#xff0c;端午节还有许多别称&#xff0c;如&#xff1a;午日节、重五节、五月节、浴兰节、女儿节、天中节、地…

汇编cmp比较指令详解

刚刚看到了cmp指令&#xff0c;一开始有点晕。后来上网找了些资料&#xff0c;终于看明白了&#xff0c;为了方便初学者&#xff0c;我就简单写下我的思路吧。高手绕过&#xff0c;谢谢&#xff01; cmp(compare)指令进行比较两个操作数的大小例:cmp oprd1,oprd2为第一个操作减…

如何在ASP.NET Core中使用SignalR构建与Angular通信的实时通信应用程序

图片假设我们要创建一个监视Web应用程序&#xff0c;该应用程序为用户提供了一个能够显示一系列信息的仪表板&#xff0c;这些信息会随着时间的推移而更新。第一种方法是在定义的时间间隔&#xff08;轮询&#xff09;定期调用API 以更新仪表板上的数据。无论如何&#xff0c;还…