ASP.NET Core JWT/Windows 身份验证 HTTP 400 错误

Posted

技术标签:

【中文标题】ASP.NET Core JWT/Windows 身份验证 HTTP 400 错误【英文标题】:ASP.NET Core JWT/Windows Authentication HTTP 400 Error 【发布时间】:2019-04-19 01:18:43 【问题描述】:

我有一个 ASP.NET Core 2.1 Web API,目前需要用户输入用户名/密码才能接收 JWT 以进行授权。我还想添加使用 Windows 身份验证来接收 JWT 的选项。最终,我计划有两个授权控制器,一个用于用户名/密码,另一个用于 Windows Auth。

为了测试这一点,我首先在 IIS express 中启用了 Windows 身份验证,方法是右键单击我的项目并转到“属性”。

然后,我制作了一个简单的测试控制器,看看是否可以使用我的 Windows 凭据进行身份验证。

[Authorize(AuthenticationSchemes = "Windows")]
[Route("api/ping")]
public class PingController : Controller

    // GET api/values
    [HttpGet]
    public IEnumerable<string> Get()
    
        return new string[]  "Pong" ;
    

这似乎有效,因为当我在浏览器中导航到此端点时,屏幕按预期显示 Pong

但是,当我尝试访问使用承载身份验证方案的任何其他控制器时,我遇到了问题。控制器声明他们的身份验证方案,如下所示:

[Authorize(Policy = "MyPolicy", AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

每当我向其中一个控制器发出请求时,我都会收到以下错误:

HTTP Error 400. The request is badly formed.

在关闭 Windows 身份验证的情况下,同样的请求也可以正常工作。

如何对某些控制器强制执行 Windows 身份验证,对其他控制器强制执行承载身份验证?

【问题讨论】:

希望这个问题能帮到你***.com/questions/51053459/… 把项目拆分成2个项目 【参考方案1】:

如何对某些控制器强制执行 Windows 身份验证,对其他控制器强制执行承载身份验证?

首先,我不得不说在处理 JWT 和 Windows 身份验证的混合方案时有点奇怪。我的意思是当未经JwtBearer 身份验证的用户尝试访问受JwtBearer Scheme 保护的那些url 资源时,将受到Windows 身份验证的挑战。

其次,对于您的问题,我们可以JwtBearer 身份验证配置为使用不用作HTTP 标头的自定义令牌(即Authorization: Bearer xxxx_yyyy_zzz。例如,通过查询字符串或自定义标头发送JWT 令牌。

详细说明:

配置JwtBearer 身份验证以从查询字符串中读取令牌:

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options=> 
            options.TokenValidationParameters = new TokenValidationParameters
            
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                ValidIssuer = Configuration["Jwt:Issuer"],
                ValidAudience = Configuration["Jwt:Audience"],
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
            ;
            options.Events = new JwtBearerEvents() 
                OnMessageReceived = async (context) =>
                    var bearer=context.HttpContext.Request.Query["bearer"].FirstOrDefault();
                    if(!String.IsNullOrEmpty(bearer))
                        context.Token = bearer;
                    
                ,
            ;
        );

出于测试目的,我为您的MyPolicy 添加了一个虚拟策略处理程序:

services.AddAuthorization(o => 
    o.AddPolicy("MyPolicy",p => 
        p.Requirements.Add(new MyPolicyRequirement());
    );
);
services.AddSingleton<IAuthorizationHandler,MyPolicyRequirementHandler>();
services.AddHttpContextAccessor();

这里的MyPolicyRequirementHandler 是:

public class MyPolicyRequirementHandler : AuthorizationHandler<MyPolicyRequirement>


    public MyPolicyRequirementHandler()
    
    

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MyPolicyRequirement requirement)
    
        var user= context.User;
        if (user.Identity.IsAuthenticated)
        
            context.Succeed(requirement);
        

        return Task.CompletedTask;
    

以及受WindowsJwtBearer的Authentication保护的两个控制器:

[Authorize(AuthenticationSchemes = "Windows")]
[Route("api/[controller]")]
[ApiController]
public class PingController : ControllerBase

    // GET api/values
    [HttpGet]
    public IEnumerable<string> Get()
    
        return new string[]  "Pong" ;
    


[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Policy ="MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class FooController : ControllerBase

    // GET api/values
    [HttpGet]
    public IEnumerable<string> Get()
    
        return new string[]  "Bar" ;
    

测试用例:

使用 Windows 身份验证测试

访问/api/ping时的截图

使用 Jwt Bearer 身份验证测试

首先,在服务器端生成一个JwtToken

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6Iml0bWludXMiLCJuYmYiOjE1NDIzNDMxNzMsImV4cCI6MTU0MjQxNTE3MywiaWF0IjoxNTQyMzQzMTczLCJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo0NDM4NSIsImF1ZCI6IndlYmNsaWVudCJ9.iMnq8UBRQforNeRBehrULAScD8D2-ta4nmdQt1rTZ3s

然后向/api/foo的端点发送一个HTTP GET请求,查询字符串为bearer=xxx_yyy_zzz

GET https://localhost:44385/api/foo?bearer=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6Iml0bWludXMiLCJuYmYiOjE1NDIzNDMxNzMsImV4cCI6MTU0MjQxNTE3MywiaWF0IjoxNTQyMzQzMTczLCJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo0NDM4NSIsImF1ZCI6IndlYmNsaWVudCJ9.iMnq8UBRQforNeRBehrULAScD8D2-ta4nmdQt1rTZ3s HTTP/1.1

它将按预期返回[foo]

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
Server: Kestrel
X-SourceFiles: =?UTF-8?B?RDpccmVwb3J0XDIwMThcMTZcV2luZG93c0F1dGh0ZW50aWNhdGlvbkFuZEp3dEF1dGhlbnRpY2F0aW9uXEFwcFxhcGlcZm9v?=
X-Powered-By: ASP.NET

["Bar"]

【讨论】:

//当一个没有被JwtBearer认证的用户,试图访问那些受JwtBearer Scheme保护的url资源时,会受到Windows认证的挑战//——是否可以在保留两者的同时避免这种行为身份验证方案? @Jones 你有答案吗?我也有同样的挣扎。无法弄清楚如何进行混合身份验证,但 JWT 端点不应受到 Windows 身份验证的挑战。我不想制作不同的应用程序来生成令牌。 @AngryCoder - 是的,Windows 身份验证需要标头 Authorization 中的令牌。因此,要同时使用两者,请在一些自定义标头中发送 jwt 令牌。我使用JWTToken

以上是关于ASP.NET Core JWT/Windows 身份验证 HTTP 400 错误的主要内容,如果未能解决你的问题,请参考以下文章

Asp.Net core (Full .Net framework) vs Asp.Net core (.Net Core) 性能

Asp.NET Core进阶 第四篇 Asp.Net Core Blazor框架

.NET Core 1.0ASP.NET Core 1.0和EF Core 1.0简介

asp.net core 注入后仍然报错?

深入研究 Mini ASP.NET Core(迷你 ASP.NET Core),看看 ASP.NET Core 内部到底是如何运行的

.Net Core 学习 - ASP.NET Core 概念学习