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 错误的主要内容,如果未能解决你的问题,请参考以下文章