问题描述
我已经实现了一个自定义的 OAuthAuthorizationServerProvider
,并且当我的客户端请求访问令牌时,我想在响应中添加一些额外的元素.
I have implemented a custom OAuthAuthorizationServerProvider
and I want to add some additional elements in the response when my client is requesting an access token.
为此,我覆盖了 OAuthAuthorizationServerProvider.TokenEndpoint
方法,并成功添加了一些单个元素(通过将它们添加到 context.AdditionalResponseParameters
字典中).现在我有这样的回应:
To do so I overrided the OAuthAuthorizationServerProvider.TokenEndpoint
method and I successfully managed to add some single elements (by adding them into the context.AdditionalResponseParameters
dictionary). Now I have this kind of response:
{
"access_token": "wxoCtLSdPXNW9KK09PVhSqYho...",
"token_type": "bearer",
"expires_in": 1199,
"refresh_token": "uk0kFyj4Q2OufWKt4IzWQHlj...",
"toto": "bloblo",
"tata": "blabla"
}
这很棒,但我的目标是添加一个数组以获得这种响应:
This is great but my goal here is to add an array in order to get this kind of response:
{
"access_token": "wxoCtLSdPXNW9KK09PVhSqYho...",
"token_type": "bearer",
"expires_in": 1199,
"refresh_token": "uk0kFyj4Q2OufWKt4IzWQHlj...",
"scopes": ["read", "write"]
}
我试图添加一个 json 解析的列表或数组而不是一个简单的字符串,但它给了我
I tried to add a json-parsed list or array instead of a simple string but it gives me
"scopes": "["read","write"]"
那是解析成 Json 的字符串,而不是 Json 数组 :/
That's the string parsed into a Json, not a Json array :/
如何在 TokenEndpoint 响应中添加 Json 数组?
How can I add a Json array in the TokenEndpoint response?
推荐答案
问题
当我们使用app.OAuthBearerAuthenticationExtensions
时,调用下一个链:
public static class OAuthBearerAuthenticationExtensions
{
public static IAppBuilder UseOAuthBearerAuthentication(this IAppBuilder app, OAuthBearerAuthenticationOptions options)
{
if (app == null)
throw new ArgumentNullException(nameof (app));
app.Use((object) typeof (OAuthBearerAuthenticationMiddleware), (object) app, (object) options);
app.UseStageMarker(PipelineStage.Authenticate);
return app;
}
}
然后OAuthAuthorizationServerMiddleware
类型的对象使用内部类OAuthAuthorizationServerHandler
,其中使用了JsonTextWriter
:
Then an object of type OAuthAuthorizationServerMiddleware
uses internal class OAuthAuthorizationServerHandler
where JsonTextWriter
is used:
using (var jsonTextWriter = new JsonTextWriter((TextWriter) new StreamWriter((Stream) memory)))
{
jsonTextWriter.WriteStartObject();
jsonTextWriter.WritePropertyName("access_token");
jsonTextWriter.WriteValue(accessToken);
jsonTextWriter.WritePropertyName("token_type");
jsonTextWriter.WriteValue("bearer");
// and so on
this.Response.ContentLength = new long?((long) body.Length);
await this.Response.WriteAsync(body, this.Request.CallCancelled);
}
这里有两个限制:
*) JsonTextWriter
是纯类,不能配置,只是把字符串写成StringBuilder,所以不能应用Json.Settings = new MySettings()
.另外 JsontTextWriter
不支持复杂对象.数组只能写成 jsonTextWriter.WriteStartArray()
和 jsonTextWriter.WriteEndArray()
,但这在 OAuthAuthorizationServerHandler
中被忽略.
*) 有些类是内部的,不能被覆盖或继承.
There are two limitations here:
*) JsonTextWriter
is a pure class that cannot be configured, it just writes string as StringBuilder, so Json.Settings = new MySettings()
cannot be applied. Also JsontTextWriter
does not support complex objects. Arrays can be only written as jsonTextWriter.WriteStartArray()
and jsonTextWriter.WriteEndArray()
, but this is ignored in the OAuthAuthorizationServerHandler
.
*) Some classes are internal and cannot be overwritten or inherited.
微软开发者似乎没有预见到这个问题,只是将自定义属性限制为IDictionary
.
It seems that Microsoft developers did not foresee this problem and just limited custom properties to IDictionary<string, string>
.
代替 app.UseOAuthBearerAuthentication(...)
应用您自己的代码
Instead of app.UseOAuthBearerAuthentication(...)
apply your own code
app.Use<MyOAuthBearerAuthenticationMiddleware>(options);
app.UseStageMarker(PipelineStage.Authenticate);
您可以从 OAuthBearerAuthenticationMiddleware
派生一个类并用于您自己的目的.
You can derive a class from OAuthBearerAuthenticationMiddleware
and use for your own purposes.
覆盖令牌端点响应.这是一件棘手的事情.
Override the token Endpoint response. This is a tricky thing.
1) 创建一个自定义中间件,用于包装其他调用并覆盖 Body 响应流.
1) Create a custom middleware that will wrap other calls and override Body response stream.
class AuthenticationPermissionsMiddleware : OwinMiddleware
{
public AuthenticationPermissionsMiddleware(OwinMiddleware next)
: base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
if (!context.Request.Path.Equals("/Token")
{
await Next.Invoke(context);
return;
}
using (var tokenBodyStream = new MemoryStream())
{
// save initial OWIN stream
var initialOwinBodyStream = context.Response.Body;
// create new memory stream
context.Response.Body = tokenBodyStream;
// other middlewares will will update our tokenBodyStream
await Next.Invoke(context);
var tokenResponseBody = GetBodyFromStream(context.Response);
var obj = JsonConvert.DeserializeObject(tokenResponseBody);
var jObject = JObject.FromObject(obj);
// add your custom array or any other object
var scopes = new Scope[];
jObject.Add("scopes", JToken.FromObject(scopes));
var bytes = Encoding.UTF8.GetBytes(jObject.ToString());
context.Response.Body.Seek(0, SeekOrigin.Begin);
await tokenBodyStream.WriteAsync(bytes, 0, bytes.Length);
context.Response.ContentLength = data.LongLength;
tokenBodyStream.Seek(0, SeekOrigin.Begin);
// get back result to the OWIN stream
await context.Response.Body.CopyToAsync(initialOwinBodyStream);
}
}
}
private string GetBodyFromStream(IOwinResponse response)
{
using (var memoryStream = new MemoryStream())
{
response.Body.Seek(0, SeekOrigin.Begin);
response.Body.CopyTo(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(memoryStream))
{
return reader.ReadToEnd();
}
}
}
}
2) 在认证启动方法中使用 UseOAuthBearerTokens
之前的新中间件.
2) Use the new middleware before UseOAuthBearerTokens
in the authentication startup method.
app.Use<AuthenticationPermissionsMiddleware>();
app.UseOAuthBearerTokens(options);
这篇关于ASP.Net OAuth 授权服务器:添加一个数组作为额外的响应参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!