代码之家  ›  专栏  ›  技术社区  ›  birdus

授权令牌未绑定到ASP.NET核心

  •  1
  • birdus  · 技术社区  · 6 年前

    我调用的REST api如下:

    HttpClient client;
    var uri = new Uri(Const.GetUserAccount);
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("accessToken", App.AccessToken);
    var response = await client.GetAsync(uri);
    

    这是我的动作签名(accessToken为空):

    public async Task<ActionResult> GetAccountAsync([FromHeader] string accessToken)
    

    令牌在Request.Headers.header授权其价值是:

    "accessToken" + a space + the guid
    

    这似乎很奇怪。不应该有名称-值对吗?比如:

    “accessToken”:“theGUID”

    这就是它没有约束力的原因吗?如果是,如何正确传递?如果不是,我做错了什么?

    1 回复  |  直到 6 年前
        1
  •  6
  •   itminus    6 年前

    原因是您的操作方法需要 accessToken

    public async Task<ActionResult> GetAccountAsync([FromHeader] string accessToken)
    

    而你却没有 AccessToken: xxx_yyy_zzz 请求中的标头。

    如果您发送以下请求:

    GET https://localhost:44323/api/values/account HTTP/1.1
    accessToken : xxx_yyy_zzz
    

    ModelBinder将绑定 访问令牌

    如果是,如何正确传递?

    我不知道你为什么想得到 访问令牌 在动作方法中。但是,如果确实需要模型绑定的访问令牌,至少有两种方法:

    一种方法是改变你的动作方法来获得 Authorization

    public async Task<ActionResult> GetAccount2Async([FromHeader] string authorization) 
    {
        if (String.IsNullOrEmpty(authorization)) { /* */ }
        if (!authorization.StartsWith("accessToken",StringComparison.OrdinalIgnoreCase)) { /* */ }
    
        var token = authorization.Substring("accessToken".Length).Trim();
        // ...
    }
    

    当您发送头为的请求时,它将起作用 Authorization : accessToken xxx_yyy_zzz

    然而,上面的方法不是很好和干净。更好的方法是创建自定义 ModelBinder .

    首先,让我们创建一个伪类来保存accessToken值:

    public class AccessTokenAuthorizationHeader
    {
        public string TokenValue { get; set; }
    }
    

    public class AuthorizationHeaderBinder : IModelBinder
    {
        const string DEFAULT_ACCESS_TOKEN_AUTH_HEADER_PREFIX = "accessToken";
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); }
    
            var modelName = bindingContext.BinderModelName;
            if (string.IsNullOrEmpty(modelName)) { modelName = DEFAULT_ACCESS_TOKEN_AUTH_HEADER_PREFIX; }
    
            var authorization = bindingContext.HttpContext.Request.Headers["Authorization"].FirstOrDefault();
            if (String.IsNullOrWhiteSpace(authorization)) {
                return Task.CompletedTask;
            }
            if (!authorization.StartsWith(modelName, StringComparison.OrdinalIgnoreCase)) {
                return Task.CompletedTask;
            }
            var token = authorization.Substring(modelName.Length).Trim();
    
            bindingContext.Result = ModelBindingResult.Success(new AccessTokenAuthorizationHeader() {
                TokenValue =token,
            });
            return Task.CompletedTask;
        }
    }
    

    最后,装饰上一层 AccessTokenAuthorizationHeader 用一个 ModelBinderAttribute :

    [ModelBinder(BinderType =typeof(AuthorizationHeaderBinder))]
    public class AccessTokenAuthorizationHeader
    {
        public string TokenValue { get; set; }
    }
    

    [HttpGet("Account3")]
    public async Task<ActionResult> GetAccount3Async(AccessTokenAuthorizationHeader accessToken) {
        var result =new JsonResult(accessToken?.TokenValue);
        return result;
    }
    

    让我们用一个requset来测试它:

    GET https://localhost:44323/api/values/account3 HTTP/1.1
    Authorization : accessToken 111111
    

    HTTP/1.1 200 OK
    Transfer-Encoding: chunked
    Content-Type: application/json; charset=utf-8
    Server: Kestrel
    X-SourceFiles: =?UTF-8?B?RDpccmVwb3J0XDIwMThcOVw5LTEzXFNPLkF1dGhvcml6YXRpb25IZWFkZXJcQXBwXEFwcFxhcGlcdmFsdWVzXGFjY291bnQz?=
    X-Powered-By: ASP.NET
    Date: Thu, 13 Sep 2018 01:54:25 GMT
    
    "111111"