没有指定 authenticationScheme,也没有找到 DefaultChallengeScheme - ASP.NET core 2.1
Posted
技术标签:
【中文标题】没有指定 authenticationScheme,也没有找到 DefaultChallengeScheme - ASP.NET core 2.1【英文标题】:No authenticationScheme was specified, and there was no DefaultChallengeScheme found - ASP.NET core 2.1 【发布时间】:2019-03-22 02:00:58 【问题描述】:我正在使用 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);
如果我为授权方法传递标头,它工作正常,但是缺少相同的标头会产生错误No authenticationScheme was specified, and there was no DefaultChallengeScheme found.
这里我有两个问题:
1) 如果没有指定标头,可以向用户端发送 500 异常吗?
2) 如何处理这种情况并传递有意义的消息“header is missing”之类的?
【问题讨论】:
【参考方案1】:如果没有指定header,是否可以向用户端发送500异常?
恐怕这不是个好主意。
500
状态代码表示存在服务器错误。当客户端发送没有令牌的请求时,告诉客户端“发生内部错误”是没有意义的。更好的方法是发送401
挑战用户或发送403
禁止。
如何处理这种情况并传递有意义的消息“header is missing”之类的?
首先,我不得不说我不认为使用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()
方法来自定义您自己的消息来挑战用户::: p>
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 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"]))
;
);
[编辑2]
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
【讨论】:
注册services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme);
并添加[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
但是,得到"No authentication handlers are registered. Did you forget to call AddAuthentication().Add[SomeAuthHandler](\"Bearer\",...)?
的错误
@Div 您不需要存储令牌。 Jwt Token 存储在客户端并在服务器上验证。只需创建一个新令牌并将它们发送给客户端就可以了。
@Div 我不知道是否有标准的方法来做到这一点。但我曾经同时发出一个accessToken(2小时内过期)和一个refreshToken(2.1小时内过期)。客户端使用 refreshToken 请求新令牌,将旧的 accessToken 和 refreshToken 替换为新的。
@Div 即使刷新令牌在某个时间过期,客户端也可以再次登录并获得新的 accessToken 和刷新令牌。服务器不关心那个。他们只是验证令牌。
@Div 对不起,我误解了你关于 refresh token 的问题。我以为您正在尝试保存 accessToken 。人们通常将刷新令牌保存在某处。如果您不想保存刷新令牌,只需发出一个名为 refresh token 的新 JwtToken ,它被视为刷新令牌,只能用作刷新令牌。以上是关于没有指定 authenticationScheme,也没有找到 DefaultChallengeScheme - ASP.NET core 2.1的主要内容,如果未能解决你的问题,请参考以下文章
未指定 authenticationScheme,并且没有找到具有默认身份验证和自定义授权的 DefaultChallengeScheme
为啥我应该始终为我的 Authorize 属性定义 JwtBearerDefaults.AuthenticationScheme?