一、需求背景
1、针对公司内部系统构建提供业务数据的API接口,分发给业务人员做自助数据分析。
2、业务人员通过PowerQuery、PowerBI连接API接口完成数据消费。
3、通过Windows鉴权完成权限管理。额外创建一套Basic鉴权会重复编码且用户体验不好。
4、BI或Excel连接API接口无需使用JWT方法、内部系统也无需过多考虑安全问题,所有选择Windows鉴权。
二、本文内容
1、来源于微软官方文档:在 ASP.NET Core 中配置 Windows 身份验证
2、本文内容对比官方文档并不全面。也有一些坑的补充。
三、配置
1、Program.cs。两个注意点的一定要看仔细的
- 代码配置
// ---window鉴权配置
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate();
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = options.DefaultPolicy;
});
// ---
var app = builder.Build();
-
注意1:配置代码要在var app..前面,否则报错。在ASP.NET Core 中,
builder.Build()
方法用于创建并构建应用程序的主机。该方法之后的代码将在应用程序运行之前执行。而在鉴权配置的情况下,它需要在应用程序启动之前完成以确保正确的身份验证和授权机制。
-
注意2:如果Program.cs中已经有了 app.UseRouting() 和 app.UseEndpoints(...) 的调用,则必须在它们之间添加 app.UseAuthorization()。否则配置了Authorize的Controller,在测试环境可以正常调用,在线上环境就会报500(500 Internal Server Error)。且只能通过日志信息得知。
2、关于3点和4点的说明
- 同一个项目中,有时候会要实现:部分接口通过鉴权才能访问,部分接口可以直接匿名访问。并不是所有接口都要参与鉴权。
3、生产环境中(IIS中)配置身份验证。两种方式:可视化的修改、按照配置文件的修改
-
可视化修改:官方文档给出的参考是开启Windows身份认证、关闭匿名验证。可以看到我的配置中,将匿名访问和Windows身份验证都启用了,因为我要实现项目中部分接口不需要通过Windows鉴权即可访问(Controller的方法参不参与windows鉴权,需要通过特性标注实现,详见4点)。
如果项目里面有需要鉴权和不需要鉴权的接口,那匿名访问和windows验证都需要开启,如果只开windows验证会报500错误(HTTP Error 500.30 - ASP.NET Core app failed to start)。
-
配置文件中修改:官方也给出了在项目根目录中配置web.config文件的方式,这种感觉坑很多,建议不使用,直接在IIS中可视化配置。
-
测试环境中(IIS Express中)开启windows身份校验。也和IIS中开启身份校验一样,有两种方式,一种是改配置文件,一种是通过可视化的方式更改。其实就是一个东西,可视化修改,配置文件中的内容也会自动变动。
4、如2所说,3中配置,如何设置哪些接口通过鉴权、哪些接口不通过鉴权?(以下呢内容基于3中的配置验证得知)
命名空间:Microsoft.AspNetCore.Authorization
[Authorize]表示当前接口的方法需要Windows验证、[AllowAnonymous]表示当前接口方法可以匿名访问
Authorize和AllowAnonymous可以标注在Controller上,也可以标注在Controller的方法上
不标注Authorize和AllowAnonymous,需要windows验证
Controller标注Authorize,action标注AllowAnonymous,那么这个action可以匿名访问
....还有一些其它的组合,大家可以尝试下,这块比较绕的-
代码:BasicDataMaintenanceController中的方法需要鉴权、TestController中的方法不需要鉴权
四、访问API进行验证
1、访问配置了[Authorize]的接口的方法,会弹出来登录框。否则报错401。
五、在代码中获取当前访问的人的域账号
1、在接口中:
[HttpGet]
[ApiCode("当前访问用户GID账号")]
public ActionResult<String> GetCurrentUser()
{
string gid = User.Identity.Name.Split('\')[1];
return gid;
}
2、在Filter中(切面编程),可以获取到当前请求人的GID,然后进行权限上的逻辑校验
六、对Swagger页面配置Windows鉴权,并且访问Swagger登陆Windows后,登陆的账号可作为访问api的请求头内容。
1、通过中间件实现
2、中间件代码
namespace JCH_GC.Helpers
{
/// <summary>
/// 打开Swagger页面鉴权
/// </summary>
public class AuthorizeSwaggerAccessMiddleware
{
private readonly RequestDelegate next;
public AuthorizeSwaggerAccessMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task InvokeAsync(HttpContext context)
{
if (context.Request.Path.StartsWithSegments("/swagger")) // 只有打开界面 的链接是 swagger结尾的才使用鉴权
{
// 在此处进行身份验证逻辑
// ...
if (!string.IsNullOrEmpty(context.User.Identity.Name))
{
// 这里使用 Windows 身份验证鉴权的方式进行判断
if (context.User.Identity.Name.Split('\')[1] != "admin") // 判断是不是管理员
{
context.Response.StatusCode = 401;
await context.Response.WriteAsync("Unauthorized");
}
else
{
await next(context);
}
}
else
{
context.Response.StatusCode = 401;
await context.Response.WriteAsync("Unauthorized");
}
}
else
{
await next(context);
}
}
}
}
3、Program.cs中代码配置
app.UseSwagger();
app.UseSwaggerUI(c=>
{
app.UseMiddleware<AuthorizeSwaggerAccessMiddleware>();
});
4、在Swagger中访问接口,测试登陆Swagger页面的账号是作为了请求头的内容
-
访问有权限接口
-
访问没权限接口:它返回的报错信息中,显示的是我登陆Swagger页面的windows账号
所以这里测试用也非常方便,相比较Identity鉴权要在Program.cs中配置很多信息而言
七、在PostMan中访问需要Windows鉴权的API
1、打开Postman并创建一个新的请求。
2、点击请求头部分下方的“Authorization”选项卡。
3、选择“Type”选项卡,然后从下拉列表中选择“NTLM”。
在“Username”和“Password”字段中输入Windows域用户的凭据。
八、在Python中访问需要Windows鉴权的API
import requests
from requests_ntlm import HttpNtlmAuth
url = 'http://1111/api/jch_gc/GetU8CurrentStockFinalProduct'
username = 'xx'
password = 'xxx'
domain = "xxx"
# Create a session object with NTLM authentication
session = requests.Session()
session.auth = HttpNtlmAuth(f'{domain}\{username}', password)
# Send a GET request to the API endpoint
response = session.get(url)
# Check the response status code
if response.status_code == 200:
# Do something with the API response
print(response.json())
else:
print("Failed to access API. Status code:", response.status_code)