问题描述
我有一个ASP.NET Core 2.1 Web API,当前需要用户输入用户名/密码来接收JWT授权.我想添加使用Windows身份验证来接收JWT的选项.最终,我计划有两个授权控制器,一个用于用户名/密码,另一个用于Windows Auth.
要对此进行测试,我首先在IIS express中启用了Windows身份验证,方法是右键单击我的项目,然后转到属性".
然后,我制作了一个简单的测试控制器,以查看是否可以使用Windows凭据进行身份验证.
[Authorize(AuthenticationSchemes ="Windows")][Route("api/ping")]公共类PingController:控制器{//获取api/values[HttpGet]公共IEnumerable< string>得到(){返回新的字符串[] {"Pong"};}}
这似乎可行,因为当我在浏览器中导航到该端点时,屏幕按预期显示了 Pong
.
但是,当我尝试访问使用承载身份验证方案的其他任何控制器时,都会遇到问题.控制器以如下方式声明其身份验证方案:
[Authorize(策略="MyPolicy",AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
每当我向这些控制器之一发出请求时,都会出现以下错误:
HTTP错误400.请求的格式错误.
在关闭Windows身份验证的情况下,相同的请求也可以正常工作.
如何在某些控制器上强制执行Windows身份验证,而在其他控制器上强制执行Bearer身份验证?
首先,我不得不说在处理JWT和Windows身份验证的混合方案时有些奇怪.我的意思是,当未通过 JwtBearer
进行身份验证的用户尝试访问受 JwtBearer
方案保护的那些url资源时,将受到Windows身份验证的挑战.
第二,关于您的问题,我们可以配置 JwtBearer
身份验证以使用自定义令牌,该令牌不会用作 HTTP
标头(即授权:不记名xxxx_yyyy_zzz
).例如,通过查询字符串或自定义标头发送 JWT
令牌.
详细信息:
配置 JwtBearer
身份验证以从查询字符串读取令牌:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => {options.TokenValidationParameters =新的TokenValidationParameters{ValidateIssuer = true,ValidateAudience = true,ValidateLifetime = true,ValidateIssuerSigningKey = true,ValidIssuer = Configuration ["Jwt:Issuer"],ValidAudience =配置["Jwt:Audience"],IssuerSigningKey =新的SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration ["Jwt:Key"]))};options.Events = new JwtBearerEvents(){OnMessageReceived =异步(上下文)=> {var bearer = context.HttpContext.Request.Query ["bearer"].FirstOrDefault();if(!String.IsNullOrEmpty(bearer)){context.Token =承载;}},};});
出于测试目的,我为您的 MyPolicy
添加了一个虚拟策略处理程序:
services.AddAuthorization(o => {o.AddPolicy("MyPolicy",p => {p.Requirements.Add(new MyPolicyRequirement());});});services.AddSingleton< IAuthorizationHandler,MyPolicyRequirementHandler>();.services.AddHttpContextAccessor();
这里 MyPolicyRequirementHandler
是:
公共类MyPolicyRequirementHandler:AuthorizationHandler< MyPolicyRequirement>{公共MyPolicyRequirementHandler(){}受保护的重写Task HandleRequirementAsync(AuthorizationHandlerContext上下文,MyPolicyRequirement要求){var user = context.User;如果(user.Identity.IsAuthenticated){context.Succeed(要求);}返回Task.CompletedTask;}}
以及受 Windows
或 JwtBearer
的身份验证保护的两个控制器:
[Authorize(AuthenticationSchemes ="Windows")][Route("api/[controller]")][ApiController]公共类PingController:ControllerBase{//获取api/values[HttpGet]公共IEnumerable< string>得到(){返回新的字符串[] {"Pong"};}}[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme,Policy ="MyPolicy")][Route("api/[controller]")][ApiController]公共类FooController:ControllerBase{//获取api/values[HttpGet]公共IEnumerable< string>得到(){返回新的字符串[] {"Bar"};}}
测试用例:
使用Windows身份验证进行测试
访问/api/ping
使用Jwt Bearer身份验证进行测试
首先,在服务器端生成一个 JwtToken
:
然后使用 bearer = xxx_yyy_zzz
的查询字符串将HTTP GET请求发送到/api/foo
的端点:
GET的https://本地主机:44385/API/富承载= eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6Iml0bWludXMiLCJuYmYiOjE1NDIzNDMxNzMsImV4cCI6MTU0MjQxNTE3MywiaWF0IjoxNTQyMzQzMTczLCJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo0NDM4NSIsImF1ZCI6IndlYmNsaWVudCJ9.iMnq8UBRQforNeRBehrULAScD8D2-ta4nmdQt1rTZ3s HTTP/1.1
它将按预期返回 [foo]
:
HTTP/1.1 200 OK传输编码:分块内容类型:application/json;字符集= utf-8服务器:红estX源文件:=?UTF-8?X-Powered-by:ASP.NET[酒吧"]
I have an ASP.NET Core 2.1 Web API which currently requires users to enter a username/password to recieve a JWT for authorization. I want to add the option of using Windows Authentication to recieve a JWT as well. Ultimately, I plan to have two authorization controllers, one for username/password, the other for Windows Auth.
To test this, I first enabled Windows Authentication in IIS express, by right clicking on my project and going to Properties.
Then, I made a simple test controller to see if I could authenticate with my Windows Credentials.
[Authorize(AuthenticationSchemes = "Windows")]
[Route("api/ping")]
public class PingController : Controller
{
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "Pong" };
}
}
This seemed to work, as when I navigated to this endpoint in my browser, the screen displayed Pong
as expected.
However, I run into problems when I try and access any of my other controllers that use Bearer Authentication Schemes. The controllers declare their Authentication Scheme like so:
[Authorize(Policy = "MyPolicy", AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
Whenever I make a request to one of these controllers I get the following error:
HTTP Error 400. The request is badly formed.
This same request works just fine with Windows Authentication turned off.
How can I enforce Windows Authentication on some controllers and Bearer Authentication on others?
Firstly, I have to say it is somehow weird when dealing with the mixed schemes of JWT and Windows Authentication. I mean when an user who is not authenticated by JwtBearer
, tries to access those url resources protected by JwtBearer
Scheme, will be challenged by Windows authentication.
Secondly, as for your question, we can configure the JwtBearer
authentication to use a custom token which is not used as the HTTP
header (i.e. Authorization: Bearer xxxx_yyyy_zzz
) . For example, send the JWT
token by querystring or a custom header.
How to In Details :
Configure the JwtBearer
authentication to read token from querystring:
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;
}
},
};
});
For a testing purpose, I add a dummy policy handler for your MyPolicy
:
services.AddAuthorization(o => {
o.AddPolicy("MyPolicy",p => {
p.Requirements.Add(new MyPolicyRequirement());
});
});
services.AddSingleton<IAuthorizationHandler,MyPolicyRequirementHandler>();
services.AddHttpContextAccessor();
Here the MyPolicyRequirementHandler
is :
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;
}
}
And the two controllers protected by the Authentication of Windows
or JwtBearer
:
[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" };
}
}
Test Case :
Test with Windows Authentication
A Screenshot when accessing the /api/ping
Test with Jwt Bearer Authentication
Firstly, generate a JwtToken
on server side :
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6Iml0bWludXMiLCJuYmYiOjE1NDIzNDMxNzMsImV4cCI6MTU0MjQxNTE3MywiaWF0IjoxNTQyMzQzMTczLCJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo0NDM4NSIsImF1ZCI6IndlYmNsaWVudCJ9.iMnq8UBRQforNeRBehrULAScD8D2-ta4nmdQt1rTZ3s
And then send a HTTP GET request to the endpoint of /api/foo
with a querystring of bearer=xxx_yyy_zzz
:
GET https://localhost:44385/api/foo?bearer=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6Iml0bWludXMiLCJuYmYiOjE1NDIzNDMxNzMsImV4cCI6MTU0MjQxNTE3MywiaWF0IjoxNTQyMzQzMTczLCJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo0NDM4NSIsImF1ZCI6IndlYmNsaWVudCJ9.iMnq8UBRQforNeRBehrULAScD8D2-ta4nmdQt1rTZ3s HTTP/1.1
it will return [foo]
as expected :
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"]
这篇关于ASP.NET Core JWT/Windows身份验证HTTP 400错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!