我正在使用 ASP.NET Core 2.1 WebApi 项目,因为我们使用了基于 token 的身份验证
public class UserIdentityFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
StringValues authorizationHeaders;
if (!context.HttpContext.Request.Headers.TryGetValue("Authorization", out authorizationHeaders))
return;
...
...
}
}
并具有用于错误处理的中间件:
public async Task Invoke(HttpContext context, ILogger logger, IAppConfiguration appConfiguration)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex, logger, appConfiguration);
}
}
如果我为授权方法传递 header ,它工作正常,但是缺少 header 会导致错误
No authenticationScheme was specified, and there was no DefaultChallengeScheme found.
这里我有两个问题:
1) 没有指定 header 时,可以向用户端发送 500 与此异常吗?
2)如何处理这种情况并传递有意义的消息“标题丢失”之类的?
最佳答案
恐怕这不是个好主意。500
状态代码表示存在服务器错误。当客户端发送没有 token 的请求时,告诉客户端“发生内部错误”是没有意义的。更好的方法是发送 401
来挑战用户或发送 403
来 forbid 。
首先,我不得不说我不认为使用 AuthorizationFilter
来验证用户是一个不错的选择。
正如错误所描述的, 错误被抛出,因为没有指定 AuthenticationScheme
,并且没有找到 DefaultChallengeScheme
。
要修复该错误, 只需指定一个身份验证方案 。例如,如果您使用 JwtToken
,则应添加 AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
或使用 [Authorize(AuthenticationSchemes ="JwtBearerDefaults.AuthenticationScheme")]
属性
否则,如果您想自定义它对用户进行身份验证的方式(例如,自定义基于 token 的身份验证),您应该创建一个新的 token 身份验证处理程序。已经有一个内置的抽象 AuthenticationHandler
类:
public abstract class AuthenticationHandler<TOptions> : IAuthenticationHandler
where TOptions : AuthenticationSchemeOptions, new()
{
// ...
}
由于默认
HandleChallengeAsync()
将发送 401
响应,您可以简单地扩展 AuthenticationHandler
并覆盖 HandleChallengeAsync()
方法来自定义您自己的消息来挑战用户 :public class OurOwnAuthenticationHandler : AuthenticationHandler<ApiKeyAuthOpts>
{
public OurOwnAuthenticationHandler(IOptionsMonitor<ApiKeyAuthOpts> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock)
{
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
StringValues authorizationHeaders;
if (!context.HttpContext.Request.Headers.TryGetValue("Authorization", out authorizationHeaders))
return AuthenticateResult.NoResult();
// ... return AuthenticateResult.Fail(exceptionMessage);
// ... return AuthenticateResult.Success(ticket)
}
protected override Task HandleChallengeAsync(AuthenticationProperties properties)
{
Response.StatusCode = 401;
var message = "tell me your token";
Response.Body.Write(Encoding.UTF8.GetBytes(message));
return Task.CompletedTask;
}
protected override Task HandleForbiddenAsync(AuthenticationProperties properties)
{
Response.StatusCode = 403;
var message = "you have no rights";
Response.Body.Write(Encoding.UTF8.GetBytes(message));
return Task.CompletedTask;
}
}
最后,您还需要注册身份验证处理程序:
services.AddAuthentication("OurOwnAuthN")
.AddScheme<OurOwnAuthNOpts,OurOwnAuthNHandler>("OurOwnAuthN","Our Own AuthN Scheme",opts=>{
// ...
});
如果您不想将“OurOwnAuthN”设置为默认身份验证方案,您可以使用
[Authorize(AuthenticationSchemes ="OurOwnAuthN")]
来保护您的资源:// your `ConfigureServices()`
services.AddAuthentication()
.AddScheme<OurOwnAuthNOpts,OurOwnAuthNHandler>("OurOwnAuthN","Our Own AuthN Scheme",opts=>{
// ...
});
// your action method :
// GET api/values/5
[Authorize(AuthenticationSchemes ="OurOwnAuthN")]
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return "value";
}
如果用户发送没有 token 或 token 不正确的请求,来自服务器的响应将是:
HTTP/1.1 401 Unauthorized
Transfer-Encoding: chunked
Server: Kestrel
X-SourceFiles: =?UTF-8?B?RDpccmVwb3J0XDIwMThcMTBcMThcU08uYXV0aGVudGljYXRpb25TY2hlbWUsIE5vIERlZmF1bHRDaGFsbGVuZ2VTY2hlbWVcQXBwXEFwcFxhcGlcdmFsdWVzXDE=?=
X-Powered-By: ASP.NET
tell me your token
[编辑]
如果您使用 Jwt Token ,您可以使用以下代码注册 JwtBearer Authentication :
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = 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"]))
};
});
[Edit2]
JwtBearer AuthenticationHandler 提供了一个
Challenge
来自定义 WWW-Authenticate
: .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.Challenge ="tell me your token";;
})
响应将是:
HTTP/1.1 401 Unauthorized
Server: Kestrel
WWW-Authenticate: tell me your token, error="invalid_token"
X-SourceFiles: =?UTF-8?B?RDpccmVwb3J0XDIwMThcMTBcMThcU08uYXV0aGVudGljYXRpb25TY2hlbWUsIE5vIERlZmF1bHRDaGFsbGVuZ2VTY2hlbWVcQXBwXEFwcFxhcGlcdmFsdWVzXDE=?=
X-Powered-By: ASP.NET
Content-Length: 0
请注意
WwW-Authenticate
header 。另一种方法是通过以下方式将 挑战转发给 :
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.ForwardChallenge = "OurOwnAuthN";
})
.AddScheme<OurOwnAuthNOpts,OurOwnAuthNHandler>("OurOwnAuthN","Our Own Authentication Scheme",opts=>{
// ...
});
响应将是:
HTTP/1.1 401 Unauthorized
Transfer-Encoding: chunked
Server: Kestrel
X-SourceFiles: =?UTF-8?B?RDpccmVwb3J0XDIwMThcMTBcMThcU08uYXV0aGVudGljYXRpb25TY2hlbWUsIE5vIERlZmF1bHRDaGFsbGVuZ2VTY2hlbWVcQXBwXEFwcFxhcGlcdmFsdWVzXDE=?=
X-Powered-By: ASP.NET
tell me your token
关于c# - 未指定 authenticationScheme,也未找到 DefaultChallengeScheme - ASP.NET core 2.1,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52854049/