You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过AadHttpClient在Azure Function中识别SPFx WebPart调用用户

问题:Node.js版Azure Function如何通过AadHttpClient识别SPFx WebPart的当前登录用户

注意:下文提及的Azure Function和SPFx WebPart均基于NodeJS/JavaScript编写,未使用C#。

我有一个由AAD(应用注册)保护的Azure Function,通过SharePoint页面上的SPFx WebPart使用AadHttpClient进行调用,SPFx代码如下:

return new Promise<void>((resolve: () => void, reject: (error: any) => void): void => {
    this.context.aadHttpClientFactory.getClient("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") // <--- 这是AAD客户端应用ID
    .then((client: AadHttpClient): void => {
        
        client.post("https://myAzureFunctionName.azurewebsites.net/api/HttpTrigger1", AadHttpClient.configurations.v1, {
            body: JSON.stringify({
                data: someData
            })
        })
        .then((res: HttpClientResponse): Promise<any> => {
            return res.json();
        })
        .then((response: any): void => {
            console.log("SUCCESSFUL API RESPONSE:", response); // <--- 此时已成功获取Azure Function的响应
            resolve();
        }, (err: any): void => {
            console.error(err);
        });

    }, err => reject(err));
});

目前调用正常,但在Azure Function端无法正确识别调用API的当前SharePoint用户。我想到的临时办法是将从_spPageContextInfo对象中获取的用户信息(如邮箱地址)附加到API请求中发送给Azure Function。

请问:从Azure Function端出发,通过AadHttpClient识别SPFx WebPart的当前登录终端用户的正规、可信方式是什么?以便我能在Azure Function中使用该用户的邮箱地址。


解决方案

要在Node.js版Azure Function中可信地识别SPFx端的当前登录用户,核心是利用AAD OAuth2的用户委派令牌——AadHttpClient默认会将当前SharePoint用户的委派令牌附加到请求的Authorization头中,你需要在Azure Function中解析这个令牌来获取用户信息,步骤如下:

1. 确认Azure Function的AAD认证配置

确保你的Azure Function对应的应用注册已正确配置:

  • 已为SPFx的客户端应用(即getClient中传入的应用ID)授予委派权限(通常是user_impersonation权限)
  • 该委派权限已获得管理员同意

2. 在Azure Function中解析并验证JWT令牌

AadHttpClient发送的请求会携带Bearer <JWT_TOKEN>格式的Authorization头,你可以提取并解析这个令牌获取用户信息,生产环境必须验证令牌的合法性:

基础解析(仅用于测试)

const jwt = require('jsonwebtoken');

module.exports = async function (context, req) {
    // 提取授权头中的令牌
    const authHeader = req.headers.authorization;
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
        context.res = {
            status: 401,
            body: '未提供有效的用户令牌'
        };
        return;
    }

    const token = authHeader.split(' ')[1];
    
    try {
        // 解析令牌(测试用,生产环境必须验证签名)
        const decodedToken = jwt.decode(token);
        
        // 从令牌声明中获取用户邮箱(优先取email,无则取upn)
        const userEmail = decodedToken.email || decodedToken.upn;
        const userId = decodedToken.oid; // 用户的AAD对象ID
        
        context.log(`当前调用用户:${userEmail}`);
        
        // 后续业务逻辑使用用户信息
        context.res = {
            status: 200,
            body: { message: `欢迎,${userEmail}`, userId: userId }
        };
    } catch (err) {
        context.res = {
            status: 401,
            body: '令牌解析失败'
        };
    }
};

生产环境的完整验证

必须验证令牌的签名、有效期、受众、发行者等,确保令牌可信:

const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');

// 初始化JWKS客户端,从Azure AD获取公钥用于验证签名
const client = jwksClient({
    jwksUri: 'https://login.microsoftonline.com/{你的租户ID}/discovery/v2.0/keys'
});

function getSigningKey(header, callback) {
    client.getSigningKey(header.kid, function(err, key) {
        const signingKey = key.getPublicKey();
        callback(null, signingKey);
    });
}

module.exports = async function (context, req) {
    const authHeader = req.headers.authorization;
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
        context.res = { status: 401, body: '无效的授权头' };
        return;
    }
    const token = authHeader.split(' ')[1];
    
    jwt.verify(token, getSigningKey, {
        audience: '你的Azure Function应用注册的客户端ID', // 令牌受众必须匹配Function的应用ID
        issuer: 'https://login.microsoftonline.com/{你的租户ID}/v2.0', // 令牌发行者
        algorithms: ['RS256'] // AAD v2.0令牌使用RS256算法签名
    }, function(err, decoded) {
        if (err) {
            context.res = { status: 401, body: '令牌验证失败:' + err.message };
            return;
        }
        // 验证通过,获取可信的用户信息
        const userEmail = decoded.email || decoded.upn;
        context.res = { status: 200, body: `用户邮箱:${userEmail}` };
    });
};

为什么不推荐手动传用户信息?

手动从_spPageContextInfo提取用户信息并发送存在安全风险——恶意用户可以篡改请求中的用户信息,而通过解析AAD颁发的委派令牌,令牌本身经过加密签名,无法篡改,能保证用户身份的可信性。

内容的提问来源于stack exchange,提问作者夏期劇場

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 07:10:11