我正在使用ASP.NET Core 2.1 WebApi项目,因为我们使用了基于令牌的身份验证
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);
}
}
如果我为authorize方法传递标头,则工作正常,但是缺少标头会出现错误No authenticationScheme was specified, and there was no DefaultChallengeScheme found.
这里我有两个问题:
1)可以在未指定标头的情况下向用户端发送此异常的500吗?
2)如何处理这种情况并传递有意义的消息“缺少标题”或其他内容?
答案 0 :(得分:3)
可以在未指定标头的情况下将500例外发送给用户端吗?
恐怕不是个好主意。
状态500
指示服务器错误。当客户端发送没有令牌的请求时,告诉客户端“发生内部错误”是没有意义的。更好的方法是发送401
挑战用户或发送403
禁止用户。
如何处理这种情况并传递有意义的消息“缺少标题”或其他内容?
首先,我不得不说我认为使用AuthorizationFilter
来验证用户身份不是一个好的选择。
如错误所述,由于未指定AuthenticationScheme
并且未找到DefaultChallengeScheme
而抛出错误。
要修复该错误,只需指定一个身份验证方案。例如,如果您使用的是JwtToken
,则应添加一个AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
或使用一个[Authorize(AuthenticationSchemes ="JwtBearerDefaults.AuthenticationScheme")]
属性
否则,如果要自定义其对用户进行身份验证的方式(例如,自定义基于令牌的身份验证),则应创建一个新的令牌身份验证处理程序。已经有一个内置的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";
}
如果用户发送的请求没有令牌或令牌不正确,则服务器的响应将是:
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令牌,则可以使用以下代码注册JwtBearer身份验证:
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
标头。
另一种方法是通过以下方式转发挑战:
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