Angular 11+.NET 5项目中将Azure AD token替换为自定义JWT token方案咨询
实现方案
整个需求分为后端.NET 5 API 适配、前端Angular 11适配两部分完成,无需改动原有基础认证的逻辑:
一、后端.NET 5 API 实现
1. 多认证方案配置
同时支持Azure AD JWT和自定义JWT校验,通过策略模式自动识别当前请求携带的token类型,在Startup.cs的ConfigureServices方法中添加如下配置:
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using System.Text; public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(options => { options.DefaultScheme = "CustomOrAzureAD"; }) .AddPolicyScheme("CustomOrAzureAD", "Custom or Azure AD JWT", options => { options.ForwardDefaultSelector = context => { var authHeader = context.Request.Headers.Authorization.FirstOrDefault(); if (authHeader?.StartsWith("Bearer ") == true) { var token = authHeader["Bearer ".Length..].Trim(); // 解析token header判断是否为Azure AD签发,也可通过issuer前缀判断 var tokenHeader = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>( Encoding.UTF8.GetString(Convert.FromBase64String(token.Split('.')[0])) ); if (tokenHeader.TryGetValue("iss", out var issuer) && issuer.ToString().Contains("login.microsoftonline.com")) { return "AzureADJwt"; } } return "CustomJwt"; }; }) .AddJwtBearer("AzureADJwt", options => { // 替换为你的Azure AD租户配置 options.Authority = "https://login.microsoftonline.com/你的租户ID"; options.Audience = "你的Azure AD应用客户端ID"; }) .AddJwtBearer("CustomJwt", options => { // 沿用原有基础认证的自定义JWT校验配置,保持完全一致 options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = "自定义签发方", ValidateAudience = true, ValidAudience = "自定义受众", ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("你的自定义JWT签名密钥")) }; }); // 其他服务配置... services.AddControllers(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // 其他中间件... app.UseAuthentication(); app.UseAuthorization(); // 其他配置... }
2. 自定义JWT生成与返回逻辑
添加全局行动作过滤器,校验如果当前请求使用Azure AD token认证通过,且用户已在本地系统存在匹配记录,生成与基础认证结构完全一致的自定义JWT,放入响应头返回给前端:
public class AzureAdTokenExchangeFilter : IActionFilter { public void OnActionExecuting(ActionExecutingContext context) {} public void OnActionExecuted(ActionExecutedContext context) { var user = context.HttpContext.User; // 判断当前认证方案是Azure AD,且用户已匹配本地账号 if (user.Identity.AuthenticationType == "AzureADJwt" && user.Identity.IsAuthenticated) { // 从Azure AD的Claims中获取用户唯一标识,匹配本地用户信息 var aadUserId = user.Claims.FirstOrDefault(c => c.Type == "oid")?.Value; var localUser = _userRepository.FindByAadUserId(aadUserId); if (localUser != null) { // 调用原有生成自定义JWT的逻辑,和基础认证生成逻辑完全一致 var customJwt = _jwtService.GenerateToken(localUser); // 自定义JWT放入响应头 context.HttpContext.Response.Headers.Add("x-custom-jwt", customJwt); } } } }
将过滤器注册到全局过滤器集合即可生效。
二、前端Angular 11 实现
核心逻辑通过Angular HTTP拦截器实现,无需改动业务接口调用逻辑
1. 前置配置
完成原有MSAL Angular的Azure AD登录逻辑,登录成功后将获取到的Azure AD access_token存入sessionStorage,key设置为azure_ad_token。
2. 认证拦截器实现
新增HTTP拦截器,统一处理请求头token替换、响应头自定义JWT存储逻辑:
import { Injectable } from '@angular/core'; import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpResponse, HttpErrorResponse } from '@angular/common/http'; import { Observable, tap, catchError, throwError } from 'rxjs'; import { MsalService } from '@azure/msal-angular'; import { Router } from '@angular/router'; @Injectable() export class AuthInterceptor implements HttpInterceptor { constructor(private msalService: MsalService, private router: Router) {} intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> { let token = ''; // 优先使用本地存储的自定义JWT const customJwt = localStorage.getItem('custom_jwt'); if (customJwt) { token = customJwt; } else { // 无自定义JWT时使用Azure AD token token = sessionStorage.getItem('azure_ad_token') || ''; } // 挂载Authorization请求头 if (token) { req = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }); } return next.handle(req).pipe( tap((event: HttpEvent<unknown>) => { if (event instanceof HttpResponse) { // 从响应头获取后端返回的自定义JWT const returnedCustomJwt = event.headers.get('x-custom-jwt'); if (returnedCustomJwt) { // 存储自定义JWT,删除无用的Azure AD token localStorage.setItem('custom_jwt', returnedCustomJwt); sessionStorage.removeItem('azure_ad_token'); } } }), catchError((error: HttpErrorResponse) => { if (error.status === 401) { const customJwt = localStorage.getItem('custom_jwt'); if (!customJwt) { // Azure AD token无效,触发重新登录 this.msalService.loginRedirect(); } else { // 自定义JWT过期,走原有基础认证的过期逻辑 localStorage.removeItem('custom_jwt'); this.router.navigate(['/login']); } } return throwError(() => error); }) ); } }
将拦截器注册到AppModule的HTTP_INTERCEPTORS提供者数组中即可生效。
三、流程校验点
- 首次Azure AD登录后,本地仅存储Azure AD token,无自定义JWT
- 首次API请求携带Azure AD token,后端校验通过后返回自定义JWT
- 前端获取自定义JWT后存储,删除Azure AD token,后续所有请求自动携带自定义JWT
内容的提问来源于stack exchange,提问作者amol jadhav
相关产品推荐
相关产品推荐

