如何验证Angular应用(Azure AD/MSAL)登录后调用NodeJS API的令牌?
Angular + Azure AD 令牌传参及 Node.js 后端验证方案
一、Angular 前端自动携带 Access Token 到请求头
用 HTTP 拦截器统一处理,避免每个请求手动添加令牌:
- 新建拦截器文件
auth.interceptor.ts:
import { Injectable } from '@angular/core'; import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http'; import { Observable, from } from 'rxjs'; import { MsalService } from '@azure/msal-angular'; import { mergeMap } from 'rxjs/operators'; @Injectable() export class AuthInterceptor implements HttpInterceptor { constructor(private msalService: MsalService) {} intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> { // 仅给后端API请求添加令牌,排除登录/第三方请求 if (request.url.startsWith('/api')) { return from(this.msalService.acquireTokenSilent({ scopes: ['api://{你的后端API Client ID}/access_as_user'], // 替换为Azure AD中注册的API权限 account: this.msalService.instance.getActiveAccount()! })).pipe( mergeMap(tokenResponse => { const authReq = request.clone({ setHeaders: { Authorization: `Bearer ${tokenResponse.accessToken}` } }); return next.handle(authReq); }) ); } return next.handle(request); } }
- 在
app.module.ts中注册拦截器:
import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { AuthInterceptor } from './auth.interceptor'; @NgModule({ providers: [ { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } ] }) export class AppModule { }
注意:scopes 必须与你在 Azure AD 中为后端API注册的权限一致,否则无法获取有效访问令牌。
二、Node.js 后端验证 Azure AD Access Token
用 jsonwebtoken 结合 jwks-rsa 实现验证中间件,自动校验令牌合法性:
- 安装依赖:
npm install jsonwebtoken jwks-rsa
- 编写验证中间件
auth.middleware.js:
const jwt = require('jsonwebtoken'); const jwksClient = require('jwks-rsa'); const client = jwksClient({ jwksUri: 'https://login.microsoftonline.com/{你的租户ID}/discovery/v2.0/keys' // 替换为你的Azure租户ID }); function getKey(header, callback) { client.getSigningKey(header.kid, function(err, key) { const signingKey = key.getPublicKey(); callback(null, signingKey); }); } function validateToken(req, res, next) { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json({ message: '未提供有效令牌' }); } const token = authHeader.split(' ')[1]; jwt.verify(token, getKey, { audience: 'api://{你的后端API Client ID}', // 替换为后端API的Client ID issuer: 'https://login.microsoftonline.com/{你的租户ID}/v2.0', // 替换为你的Azure租户ID algorithms: ['RS256'] }, function(err, decoded) { if (err) { return res.status(403).json({ message: '令牌验证失败', error: err.message }); } // 将解码后的用户信息挂载到req对象,后续接口可直接调用 req.user = decoded; next(); }); } module.exports = validateToken;
- 在 Express 路由中使用中间件:
const express = require('express'); const validateToken = require('./auth.middleware'); const router = express.Router(); // 受保护的API接口,必须经过令牌验证 router.get('/api/protected', validateToken, (req, res) => { res.json({ message: '访问受保护接口成功', user: req.user }); }); module.exports = router;
关键注意事项
- 不要用 ID Token 调用 API:ID Token 仅用于前端验证用户身份,Access Token 才是后端 API 的合法凭证。
- 权限配置匹配:前端请求的 scope 必须与后端 API 在 Azure AD 中注册的权限完全一致,否则会因 audience 不匹配导致验证失败。
- 占位符替换:所有
{你的租户ID}、{你的后端API Client ID}需替换为 Azure AD 中的实际值。 - 令牌自动刷新:前端拦截器中的
acquireTokenSilent会自动刷新过期令牌,无需手动处理。
内容的提问来源于stack exchange,提问作者user68288
相关产品推荐
相关产品推荐

