23ai 新特性之Azure AD OAuth2 集成实战

在企业云化与数字化转型中,身份认证与授权是所有业务系统的 “第一道安全防线”。OAuth2.0 作为现代授权框架的事实标准,结合 OpenID Connect(OIDC)可实现安全的用户认证与单点登录(SSO)。Azure Active Directory(Azure AD,现称 Microsoft Entra ID) 作为微软企业级身份管理平台,原生深度支持 OAuth2.0 与 OIDC 协议,能无缝对接 Web 应用、单页应用(SPA)、移动应用及后端 API,提供多租户管理、条件访问、风险识别等企业级能力,是构建统一身份体系的首选方案。

一、第一步:Azure AD 应用注册(核心前置操作)

所有集成的前提是:在 Azure AD 中注册应用,获取 Client ID、Tenant ID 等核心参数。

1. 登录 Azure 门户并进入 Azure AD

登录Azure 门户,切换到目标 Azure AD 租户;
搜索并进入Azure Active Directory(Microsoft Entra ID) → 应用注册 → 新注册Microsoft Learn。

2. 填写应用注册信息

名称:应用显示名(如 “企业 Web 门户”);
支持的帐户类型:
仅此组织目录中的帐户(单租户,企业内部应用);
任何组织目录中的帐户(多租户,对外提供服务);
任何组织目录和个人 Microsoft 帐户(混合场景);
重定向 URI:选择 “Web”,填写应用回调地址(如https://your-app.com/auth/callback,开发环境可用http://localhost:5000/callback,生产环境必须 HTTPS);
点击注册,完成创建。

3. 记录核心参数(务必保存)

注册成功后,在概述页记录:
应用程序(客户端)ID:Client ID,应用唯一标识;
目录(租户)ID:Tenant ID,Azure AD 租户唯一标识;
应用程序 ID URI:API 标识(用于 API 权限配置)。

4. 创建客户端密码(机密客户端必备)

Web 应用 / 后端服务作为机密客户端,需创建密码与 Azure AD 通信:
进入证书和密码 → 新客户端密码;
填写描述(如 “生产环境密码”),选择过期时间(建议 12 个月,遵循安全轮换策略);
点击添加,立即复制并保存密码值(仅显示一次,丢失需重新创建)。

5. 配置 API 权限(控制用户授权范围)

默认权限仅包含用户基本信息,按需添加 Microsoft Graph 或自定义 API 权限:
进入API 权限 → 添加权限 → 选择Microsoft Graph(常用)或 “我的 API”(自定义 API);
选择委托的权限(用户授权)或应用程序权限(M2M);
勾选所需权限(如User.Read:读取用户基本信息,openid/profile/email:OIDC 身份认证);
点击添加权限,如需管理员授权,点击授予管理员同意。

二、第二步:授权代码流实战(Web 应用 SSO 集成)

授权代码流是企业 Web 应用最安全的 SSO 方案,流程:用户访问应用→跳转 Azure AD 登录→认证成功返回授权码→应用用授权码换令牌→验证令牌并登录Microsoft Learn。

1. 构造授权请求(前端跳转)

用户访问受保护资源时,应用引导用户跳转至 Azure AD 授权端点,格式如下:

GET https://login.microsoftonline.com/{TenantID}/oauth2/v2.0/authorize
?client_id={ClientID}
&response_type=code  // 授权代码流固定为code
&redirect_uri={RedirectURI}
&response_mode=query
&scope=openid profile email User.Read  // OIDC+用户权限
&state={随机字符串}  // 防CSRF攻击,返回时原样带回

TenantID:租户 ID,或用common(多租户);
scope:openid(OIDC 必选)+ 业务权限,空格分隔。

2. Azure AD 登录并返回授权码

用户在 Azure AD 登录页输入账号密码(或 SSO 登录),授权应用获取权限后,Azure AD 跳转回redirect_uri,并携带授权码(code)和state:

GET https://your-app.com/auth/callback?code={AuthorizationCode}&state={State}

3. 用授权码换令牌(后端请求,安全传输)

应用后端接收授权码后,向 Azure AD 令牌端点发送 POST 请求,换取Access Token、ID Token、Refresh Token:

POST https://login.microsoftonline.com/{TenantID}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id={ClientID}
&client_secret={ClientSecret}  // 机密客户端必填
&grant_type=authorization_code
&code={AuthorizationCode}
&redirect_uri={RedirectURI}
响应示例(JSON):
json
{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "0.AAAA...",
  "token_type": "Bearer",
  "expires_in": 3600,  // Access Token有效期1小时
  "scope": "openid profile email User.Read"
}

4. 验证令牌并实现 SSO 登录

ID Token 验证(身份认证):OIDC 的 ID Token 是 JWT 格式,需验证签名、发行者(iss)、受众(aud)、过期时间(exp),确认用户身份;
Access Token 验证(授权):访问后端 API 时,在请求头携带Authorization: Bearer {access_token},API 验证令牌有效性后授权访问;
创建会话:验证通过后,应用创建用户会话,完成 SSO 登录Microsoft Learn。

5. 刷新令牌(无感续期)

Access Token 有效期短(1 小时),用Refresh Token无感获取新令牌,无需用户重新登录:

POST https://login.microsoftonline.com/{TenantID}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

client_id={ClientID}
&client_secret={ClientSecret}
&grant_type=refresh_token
&refresh_token={RefreshToken}

三、第三步:保护后端 API(令牌验证实战)

API 作为资源服务器,需验证客户端携带的 Access Token,防止未授权访问。以ASP.NET Core为例,快速集成 Azure AD 令牌验证:

1. 安装 NuGet 包

bash
运行
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
Install-Package Microsoft.Identity.Web

2. 配置令牌验证(Program.cs)

csharp
运行
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(options =>
    {
        options.TenantId = "你的租户ID";
        options.ClientId = "你的API客户端ID";
    });

builder.Services.AddAuthorization();

3. 保护 API 接口

csharp
运行
[ApiController]
[Route("api/[controller]")]
[Authorize]  // 需有效Access Token才能访问
public class UserController : ControllerBase
{
    [HttpGet("profile")]
    public IActionResult GetProfile()
    {
        // 从令牌中获取用户信息
        var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        var userName = User.FindFirst(ClaimTypes.Name)?.Value;
        return Ok(new { UserId = userId, UserName = userName });
    }
}

4. 测试 API 访问

客户端携带 Access Token 请求 API:

GET https://your-api.com/api/user/profile
Authorization: Bearer {Access Token}

令牌有效:返回 200 + 用户数据;
令牌无效 / 过期:返回 401 Unauthorized。

Related Posts