【问题标题】:ASP.Net OAuth Authorization Server: Add an array as additional response parameterASP.Net OAuth 授权服务器:添加一个数组作为附加响应参数
【发布时间】:2017-04-12 00:54:20
【问题描述】:

我已经实现了一个自定义OAuthAuthorizationServerProvider,并且我想在我的客户请求访问令牌时在响应中添加一些额外的元素。

为此,我重写了OAuthAuthorizationServerProvider.TokenEndpoint 方法,并成功地添加了一些单个元素(通过将它们添加到context.AdditionalResponseParameters 字典中)。现在我有这样的回应:

{
  "access_token": "wxoCtLSdPXNW9KK09PVhSqYho...",
  "token_type": "bearer",
  "expires_in": 1199,
  "refresh_token": "uk0kFyj4Q2OufWKt4IzWQHlj...",
  "toto": "bloblo",
  "tata": "blabla"
}

这很好,但我的目标是添加一个数组以获得这种响应:

{
  "access_token": "wxoCtLSdPXNW9KK09PVhSqYho...",
  "token_type": "bearer",
  "expires_in": 1199,
  "refresh_token": "uk0kFyj4Q2OufWKt4IzWQHlj...",
  "scopes": ["read", "write"]
}

我试图添加一个 json 解析的列表或数组而不是一个简单的字符串,但它给了我

"scopes": "[\"read\",\"write\"]"

这是解析成Json的字符串,不是Json数组:/

如何在 TokenEndpoint 响应中添加 Json 数组?

【问题讨论】:

  • 这似乎是不可能的,因为 AdditionalResponseParameters 的处理方式(JsonTextWriter.WriteValue 将用于每个参数,并且此方法只需要原始值,如数字、guid、字符串等,不是数组或对象)。
  • 哦,好吧,那么我会找到一种方法,只用字符串来说明它。谢谢 Evk

标签: c# asp.net oauth-2.0


【解决方案1】:

问题

当我们使用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

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 中会被忽略。
*) 有些类是内部的,不能被覆盖或继承。

微软开发者似乎没有预见到这个问题,只是将自定义属性限制为IDictionary<string, string>

解决方案 1

而不是app.UseOAuthBearerAuthentication(...) 应用您自己的代码

app.Use<MyOAuthBearerAuthenticationMiddleware>(options);
app.UseStageMarker(PipelineStage.Authenticate);

您可以从OAuthBearerAuthenticationMiddleware 派生一个类并用于您自己的目的。

解决方案 2

覆盖令牌端点响应。这是一件棘手的事情。

1) 创建一个自定义中间件,该中间件将包装其他调用并覆盖 Body 响应流。

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之前的新中间件。

app.Use<AuthenticationPermissionsMiddleware>();
app.UseOAuthBearerTokens(options);

【讨论】:

  • 这是唯一的解决方案,如果你想返回自定义响应,很好解释。
猜你喜欢
  • 1970-01-01
  • 2018-07-13
  • 2013-10-05
  • 2015-05-17
  • 2013-09-22
  • 2020-07-17
  • 1970-01-01
  • 2020-03-24
  • 2015-01-15
相关资源
最近更新 更多