本文介绍了ASP.Net OAuth授权服务器:添加一个数组作为其他响应参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经实现了自定义的 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.

Microsoft开发人员似乎没有预见到此问题,只是将自定义属性限制为 IDictionary< string,string> .

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授权服务器:添加一个数组作为其他响应参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

11-03 13:59