改装和授权标头

Posted

技术标签:

【中文标题】改装和授权标头【英文标题】:Refit and authorization header 【发布时间】:2019-03-24 01:21:35 【问题描述】:

目前,我正在向我的请求添加一个授权标头,如下所示:

文件:SomeFile.cs

public interface ITestApi

    [Get("/api/test/id")]
    Task<string> GetTest([Header("Authorization")] string authorization, int id);

    [Get("/api/tests/")]
    Task<string> GetTests([Header("Authorization")] string authorization);

文件:SomeOtherFile.cs

var username = "xxx";
var password = "yyy";
var authHeader = "Basic " + Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));
var baseAddress = "https://my.test.net";
ITestApi myApi = RestService.For<ITestApi>(baseAddress);

int id = 123;
var test= myApi.GetTest(authHeader, id);

var tests = myApi.GetTests(authHeader);

有没有办法在全局范围内设置 authHeader,这样我就不必将它作为参数传递给每个调用? (我使用的是改装版本 4.6.48)。换句话说,我希望能够像这样进行调用:

var test= myApi.GetTest(id);

var tests = myApi.GetTests();

【问题讨论】:

【参考方案1】:

我找到了解决办法:

[Headers("Authorization: Basic")]
public interface ITestApi

    [Get("/api/test/id")]
    Task<string> GetTest(int id);

    [Get("/api/tests/")]
    Task<string> GetTests();



var username = "xxx";
var password = "yyy";
var authHeader = Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));
var baseAddress = "https://my.test.net";

var refitSettings = new RefitSettings()

    AuthorizationHeaderValueGetter = () => Task.FromResult(authHeader)
;

ITestApi myApi = RestService.For<ITestApi>(baseAddress, refitSettings);

int id = 123;
var test= myApi.GetTest(id);

var tests = myApi.GetTests();

【讨论】:

太棒了!这里使AuthorizationHeaderValueGetter 部分工作而不被忽略的重要部分是接口上的属性。 [Headers("Authorization: Basic")] 必须存在,包括其中的 : &lt;scheme&gt; 部分,以便 Refit 调用 AuthorizationHeaderValueGetter 如果我们想要一个动态的授权方案怎么办?本例中的“基本”。【参考方案2】:

thd 的回答对我不起作用,因为 Refit 当前是 simply ignoring AuthorizationHeaderValueGetter 并且请求不包含身份验证标头。

我可以通过为我的HttpClient 提供默认身份验证标头来使其工作:

string token = await GetTokenAsync().ConfigureAwait(false);
string endpointUrl = Environment.GetEnvironmentVariable(endpointVariable) ??
                     defaultEndpointUrl ?? DefaultEndpointUrl;

var client = new HttpClient(new HttpClientHandler())

    BaseAddress = new Uri(endpointUrl)
;
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

//TODO: remove as this is ignored anyway
//var refitSettings = new RefitSettings
//
//    AuthorizationHeaderValueGetter = () => Task.FromResult($"token")
//;

var builder = RequestBuilder.ForType<TApiClient>();
return RestService.For(client, builder);

GetTokenAsync 如下所示:

public static async Task<string> GetAccessTokenAsync()

    // theoretically the token could have expired during the tests, but there is very low chance
    // so returning the same token if one was generated
    lock (SyncLock)
    
        if (!string.IsNullOrWhiteSpace(Token))
            return Token;
    

    var client = new HttpClient();
    var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
    
        Address = $"IdentityServerUrl/connect/token",
        ClientId = "MY_CLIENT_APITEST",
        ClientSecret = IdentityServerPass,
        Scope = "api.read"

    ).ConfigureAwait(false);
    tokenResponse.HttpResponse.EnsureSuccessStatusCode();

    lock (SyncLock)
    
        Token = tokenResponse.AccessToken;
        return Token;
    

【讨论】:

并使用 OAuth 令牌grant_type=client_credentials &amp;client_id=example_client_id &amp;client_secret=example_client_secret &amp;scope=user.read ? 什么是GetTokenAsync @Kiquenet 用于从认证服务器获取有效令牌。 @Kiquenet 我终于记得搜索此代码并将其添加到答案中。 太棒了!你是我的偶像【参考方案3】:

改装改变了它的方法。现在它使用默认的 .NET HttpHandler

var handler = new AuthHandler(credentials.Username, credentials.Password);
var githubApi = RestService.For<IGithubApi>(new HttpClient(handler)
                
                    BaseAddress = new Uri("https://api.github.com")
                );


public class AuthHandler : HttpClientHandler

    private readonly string _username;
    private readonly string _password;

    public AuthHandler(string username, 
        string password)
    
        _username = username;
        _password = password;
    

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, 
        CancellationToken cancellationToken)
    
        request.Headers.Add("User-Agent", "<your user name or app name>");
        request.Headers.Authorization = new AuthenticationHeaderValue("Basic",
            Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(
                $"_username:_password")));

        return await base.SendAsync(request, cancellationToken)
            .ConfigureAwait(false);
    

【讨论】:

我无法让它工作。似乎 refit 删除了标题中的授权。在没有 DI 的 Xamarin 上测试,直接。有什么建议吗?

以上是关于改装和授权标头的主要内容,如果未能解决你的问题,请参考以下文章

HTTP 规范:代理授权和授权标头

OAuth 2.0 授权标头

RESTKit 0.20 - 注销和清除授权标头

为啥缺少授权标头?

将 Fetch 与授权标头和 CORS 一起使用

HTTP标头设置授权标头阻止数据加载