Asp.net core 如何自定义OpenIddict产生的授权错误?
我在.NETCore2API中使用OpenIddict进行身份验证。客户端我依靠任何API错误来遵循自定义方案。然而,当刷新令牌已经过时时,我似乎无法找到如何自定义发送回的错误 从未到达/令牌终结点,因此错误不在我的控制之下 请求的结果是状态代码400,带有以下JSON:Asp.net core 如何自定义OpenIddict产生的授权错误?,asp.net-core,openiddict,Asp.net Core,Openiddict,我在.NETCore2API中使用OpenIddict进行身份验证。客户端我依靠任何API错误来遵循自定义方案。然而,当刷新令牌已经过时时,我似乎无法找到如何自定义发送回的错误 从未到达/令牌终结点,因此错误不在我的控制之下 请求的结果是状态代码400,带有以下JSON: {"error":"invalid_grant","error_description":"The specified refresh token is no longer valid."} 我曾尝试使用自定义中间件捕获它所
{"error":"invalid_grant","error_description":"The specified refresh token is no longer valid."}
我曾尝试使用自定义中间件捕获它所捕获的所有状态代码,但结果是在自定义中间件的执行完成之前返回的
如何正确自定义错误或拦截以更改它?谢谢
从未到达/令牌终结点,因此错误不在我的控制之下
实际上,已到达/token,并且grant_type的参数等于refresh_token。但刷新令牌过期时的拒绝逻辑不由我们处理。它是源代码中的某种硬编码:
此处的context.Reject来自程序集AspNet.Security.OpenIdConnect.Server
有关更多详细信息,请参阅
我曾尝试使用自定义中间件捕获它所捕获的所有状态代码,但结果是在自定义中间件的执行完成之前返回的
我已经尝试过了,我很确定我们可以使用自定义中间件来捕获所有状态代码。关键点是在下一次调用后检测状态代码:
app.Use(async(context , next )=>{
// passby all other end points
if(! context.Request.Path.StartsWithSegments("/connect/token")){
await next();
return;
}
// since we might want to detect the Response.Body, I add some stream here .
// if you only want to detect the status code , there's no need to use these streams
Stream originalStream = context.Response.Body;
var hijackedStream = new MemoryStream();
context.Response.Body = hijackedStream;
hijackedStream.Seek(0,SeekOrigin.Begin);
await next();
// if status code not 400 , pass by
if(context.Response.StatusCode != 400){
await CopyStreamToResponseBody(context,hijackedStream,originalStream);
return;
}
// read and custom the stream
hijackedStream.Seek(0,SeekOrigin.Begin);
using (StreamReader sr = new StreamReader(hijackedStream))
{
var raw= sr.ReadToEnd();
if(raw.Contains("The specified refresh token is no longer valid.")){
// custom your own response
context.Response.StatusCode = 401;
// ...
//context.Response.Body = ... /
}else{
await CopyStreamToResponseBody(context,hijackedStream,originalStream);
}
}
});
// helper to make the copy easy
private async Task CopyStreamToResponseBody(HttpContext context,Stream newStream, Stream originalStream){
newStream.Seek(0,SeekOrigin.Begin);
await newStream.CopyToAsync(originalStream);
context.Response.ContentLength =originalStream.Length;
context.Response.Body = originalStream;
}
从未到达/令牌终结点,因此错误不在我的控制之下
实际上,已到达/token,并且grant_type的参数等于refresh_token。但刷新令牌过期时的拒绝逻辑不由我们处理。它是源代码中的某种硬编码:
此处的context.Reject来自程序集AspNet.Security.OpenIdConnect.Server
有关更多详细信息,请参阅
我曾尝试使用自定义中间件捕获它所捕获的所有状态代码,但结果是在自定义中间件的执行完成之前返回的
我已经尝试过了,我很确定我们可以使用自定义中间件来捕获所有状态代码。关键点是在下一次调用后检测状态代码:
app.Use(async(context , next )=>{
// passby all other end points
if(! context.Request.Path.StartsWithSegments("/connect/token")){
await next();
return;
}
// since we might want to detect the Response.Body, I add some stream here .
// if you only want to detect the status code , there's no need to use these streams
Stream originalStream = context.Response.Body;
var hijackedStream = new MemoryStream();
context.Response.Body = hijackedStream;
hijackedStream.Seek(0,SeekOrigin.Begin);
await next();
// if status code not 400 , pass by
if(context.Response.StatusCode != 400){
await CopyStreamToResponseBody(context,hijackedStream,originalStream);
return;
}
// read and custom the stream
hijackedStream.Seek(0,SeekOrigin.Begin);
using (StreamReader sr = new StreamReader(hijackedStream))
{
var raw= sr.ReadToEnd();
if(raw.Contains("The specified refresh token is no longer valid.")){
// custom your own response
context.Response.StatusCode = 401;
// ...
//context.Response.Body = ... /
}else{
await CopyStreamToResponseBody(context,hijackedStream,originalStream);
}
}
});
// helper to make the copy easy
private async Task CopyStreamToResponseBody(HttpContext context,Stream newStream, Stream originalStream){
newStream.Seek(0,SeekOrigin.Begin);
await newStream.CopyToAsync(originalStream);
context.Response.ContentLength =originalStream.Length;
context.Response.Body = originalStream;
}
在将令牌响应有效负载写入响应流之前,可以使用OpenIddict的事件模型来定制它们。下面是一个例子: MyApplyTokenResponseHandler.cs Startup.cs
在将令牌响应有效负载写入响应流之前,可以使用OpenIddict的事件模型来定制它们。下面是一个例子: MyApplyTokenResponseHandler.cs Startup.cs
我认为未达到令牌端点的原因是令牌端点驻留在控制器中的构造函数永远不会被调用。在该控制器中的/token路由上的实际实现自然也是如此。不过,不久我将测试您的自定义中间件,以确认工作区正在工作。我认为令牌端点未到达的原因是令牌端点驻留在其中的控制器的构造函数永远不会被调用。在该控制器中的/token路由上的实际实现自然也是如此。不过,我将很快测试您的自定义中间件,以确认解决方案是否有效。非常好的信息,这允许我自定义错误描述字符串。但是,如果我想将自己的原始json对象写入响应流,我该如何正确地完成呢?您可以简单地将json项添加到响应对象中,例如response[my_json_array]=JArray.FromObjectnew[]{42}或者通过将响应写入输出流notification.Context.HttpContext.response.Body并调用notification.Context.HandleResponse通知OpenIddict您不希望它应用其默认逻辑来完全控制响应呈现。哇,这是一个不错的解决方案!很好的信息,这允许我自定义错误描述字符串。但是,如果我想将自己的原始json对象写入响应流,我该如何正确地完成呢?您可以简单地将json项添加到响应对象中,例如response[my_json_array]=JArray.FromObjectnew[]{42}或者通过将响应写入输出流notification.Context.HttpContext.response.Body并调用notification.Context.HandleResponse通知OpenIddict您不希望它应用其默认逻辑来完全控制响应呈现。哇,这是一个不错的解决方案!
public class MyApplyTokenResponseHandler : IOpenIddictServerEventHandler<ApplyTokenResponseContext>
{
public ValueTask HandleAsync(ApplyTokenResponseContext context)
{
var response = context.Response;
if (string.Equals(response.Error, OpenIddictConstants.Errors.InvalidGrant, StringComparison.Ordinal) &&
!string.IsNullOrEmpty(response.ErrorDescription))
{
response.ErrorDescription = "Your customized error";
}
return default;
}
}
services.AddOpenIddict()
.AddCore(options =>
{
// ...
})
.AddServer(options =>
{
// ...
options.AddEventHandler<ApplyTokenResponseContext>(builder =>
builder.UseSingletonHandler<MyApplyTokenResponseHandler>());
})
.AddValidation();