我已经派生出OAuthAuthorizationServerProvider
来验证客户端和资源所有者。
当我验证资源所有者时,发现他们的凭证无效时,我调用context.Rejected()
,但HTTP响应显示状态码为HTTP/400 Bad Request而不是我期望的HTTP/401 Unauthorized。
如何自定义OAuthAuthorizationServerProvider
的响应HTTP状态码?
这是我们如何覆盖 OwinMiddleware...首先我们在 Owin 之上创建了自己的中间件...我想我们遇到了类似于你所遇到的问题。
首先需要创建一个常量:
public class Constants
{
public const string OwinChallengeFlag = "X-Challenge";
}
然后我们覆盖了OwinMiddleware
public class AuthenticationMiddleware : OwinMiddleware
{
public AuthenticationMiddleware(OwinMiddleware next) : base(next) { }
public override async Task Invoke(IOwinContext context)
{
await Next.Invoke(context);
if (context.Response.StatusCode == 400 && context.Response.Headers.ContainsKey(Constants.OwinChallengeFlag))
{
var headerValues = context.Response.Headers.GetValues(Constants.OwinChallengeFlag);
context.Response.StatusCode = Convert.ToInt16(headerValues.FirstOrDefault());
context.Response.Headers.Remove(Constants.OwinChallengeFlag);
}
}
}
在startup.Auth文件中,我们允许覆盖调用Owin命令。public void ConfigureAuth(IAppBuilder app)
....
app.Use<AuthenticationMiddleware>(); //Allows override of Invoke OWIN commands
....
}
在ApplicationOAuthProvider中,我们修改了GrantResourceOwnerCredentials方法。
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
using (UserManager<IdentityUser> userManager = _userManagerFactory())
{
IdentityUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
context.Response.Headers.Add(Constants.OwinChallengeFlag, new[] { ((int)HttpStatusCode.Unauthorized).ToString() }); //Little trick to get this to throw 401, refer to AuthenticationMiddleware for more
//return;
}
....
我采用了稍微不同的方法,使用System.Security.Authentication.AuthenticationException
和异常中间件。
身份验证服务器提供程序:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
using (UserManager<IdentityUser> userManager = _userManagerFactory())
{
IdentityUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
throw new AuthenticationException("The user name or password is incorrect.");
}
....
中间件:
public class ExceptionMiddleware : OwinMiddleware
{
public ExceptionMiddleware(OwinMiddleware next) : base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
try
{
await Next.Invoke(context);
}
catch (Exception ex)
{
HandleException(ex, context);
}
}
private void HandleException(Exception ex, IOwinContext owinContext)
{
var errorDetails = new ErrorDetails()
{
Detail = ex.Message,
Status = (int)HttpStatusCode.InternalServerError
};
switch (ex)
{
case AuthenticationException _:
errorDetails.Status = (int)HttpStatusCode.Unauthorized;
errorDetails.Title = "invalid_grant";
break;
case [..]
case Exception _:
errorDetails.Title = "An unexpected error occured";
break;
}
var serializedError = errorDetails.ToString();
Log.Error($"Returning error response: {serializedError}");
owinContext.Response.StatusCode = errorDetails.Status;
owinContext.Response.ContentType = "application/json";
owinContext.Response.Write(serializedError);
}
private class ErrorDetails
{
public int Status { get; set; }
public string Title { get; set; }
public string Detail { get; set; }
public override string ToString()
{
return JsonSerializer.Serialize(this);
}
}
应用配置:
public void Configure(IAppBuilder app)
....
app.Use<ExceptionMiddleware>();
// Configure auth here
....
}
结果: