当前位置:Gxlcms > 数据库问题 > Web API与OAuth:既生access token,何生refresh token

Web API与OAuth:既生access token,何生refresh token

时间:2021-07-01 10:21:17 帮助过:6人阅读

class CNBlogsRefreshTokenProvider : IAuthenticationTokenProvider { private static ConcurrentDictionary<string, AuthenticationTicket> _refreshTokens = new ConcurrentDictionary<string, AuthenticationTicket>(); public async Task CreateAsync(AuthenticationTokenCreateContext context) { var guid = Guid.NewGuid().ToString(); var refreshTokenProperties = new AuthenticationProperties(context.Ticket.Properties.Dictionary) { IssuedUtc = context.Ticket.Properties.IssuedUtc, ExpiresUtc = DateTime.UtcNow.AddYears(1) }; var refreshTokenTicket = new AuthenticationTicket(context.Ticket.Identity, refreshTokenProperties); _refreshTokens.TryAdd(guid, refreshTokenTicket); context.SetToken(guid); } public async Task ReceiveAsync(AuthenticationTokenReceiveContext context) { AuthenticationTicket ticket; if (_refreshTokens.TryRemove(context.Token, out ticket)) { context.SetTicket(ticket); } } }

然后应用这个 CNBlogsRefreshTokenProvider:

public void ConfigureAuth(IAppBuilder app)
{
    OAuthOptions = new OAuthAuthorizationServerOptions
    {
        TokenEndpointPath = new PathString("/token"),
        Provider = new CNBlogsAuthorizationServerProvider(),
        AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
        AllowInsecureHttp = true,
        RefreshTokenProvider = new CNBlogsRefreshTokenProvider()
    };

    app.UseOAuthBearerTokens(OAuthOptions);
}

(二)验证持有 refresh token 的客户端

重载 OAuthAuthorizationServerProvider.GrantRefreshToken() 方法,示例代码如下:

using Microsoft.Owin.Security.OAuth;

namespace OpenAPI.Providers
{
    public class CNBlogsAuthorizationServerProvider : OAuthAuthorizationServerProvider
    {
        public override async Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
        {
            var originalClient = context.Ticket.Properties.Dictionary["as:client_id"];
            var currentClient = context.ClientId;

            if (originalClient != currentClient)
            {
                context.Rejected();
                return;
            }

            var newId = new ClaimsIdentity(context.Ticket.Identity);
            newId.AddClaim(new Claim("newClaim", "refreshToken"));

            var newTicket = new AuthenticationTicket(newId, context.Ticket.Properties);
            context.Validated(newTicket);

            await base.GrantRefreshToken(context);
        }
    }
}

为了验证client_id,需要在 GrantClientCredentials() 重载方法中保存client_id至context.Ticket:

namespace OpenAPI.Providers
{
    public class CNBlogsAuthorizationServerProvider : OAuthAuthorizationServerProvider
    {
        public override async Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
        {
            var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);

            var props = new AuthenticationProperties(new Dictionary<string, string>
                {
                    { "as:client_id", context.ClientId }
                });
            var ticket = new AuthenticationTicket(oAuthIdentity, props);

            context.Validated(ticket);
        }    
    }
}

只需实现上面这些代码,其他的都由 Microsoft.Owin.Security.OAuth 帮你代劳了。

(三)测试客户端获取 refresh token

客户端获取 access token 与 refresh token 是一起的,示例代码如下:

[Fact]
public async Task GetAccessTokenTest()
{
    var clientId = "[clientId]";
    var clientSecret = "[clientSecret]";

    var parameters = new Dictionary<string, string>();
    parameters.Add("grant_type", "password");            
    parameters.Add("username", "[username]");
    parameters.Add("password", "[password]");

    _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
        "Basic",
        Convert.ToBase64String(Encoding.ASCII.GetBytes(clientId + ":" + clientSecret)));

    var response = await _httpClient.PostAsync("/token", new FormUrlEncodedContent(parameters));
    var responseValue = await response.Content.ReadAsStringAsync();

    Console.WriteLine(responseValue);
}

运行结果:

{ 
    "access_token": "D3VjxsFvr...",
    "token_type": "bearer",
    "expires_in": 86399,
    "refresh_token": "449ba200-4232-4763-b868-8c27d7583dec"
}

成功拿到了 access token。

(四)测试客户端用 refresh token 刷新 access token

客户端测试代码如下:

public async Task GetAccessTokenTest()
{
    var clientId = "[clientId]";
    var clientSecret = "[clientSecret]";

    var parameters = new Dictionary<string, string>();
    parameters.Add("grant_type", "refresh_token");
    parameters.Add("refresh_token", "ec9f14b6-e25e-46c0-ad30-9aa0562b694c");

    _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
        "Basic",
        Convert.ToBase64String(Encoding.ASCII.GetBytes(clientId + ":" + clientSecret)));

    var response = await _httpClient.PostAsync("/token", new FormUrlEncodedContent(parameters));
    var responseValue = await response.Content.ReadAsStringAsync();

    Console.WriteLine(responseValue);
}

注:这段客户端代码与前一步中客户端代码的主要区别是少了下面传递 resource owner 用户名与密码的代码,这就是 refresh token 的用途所在 —— 不需要用户名与密码就可以刷新 access token。

parameters.Add("username", "[username]");
parameters.Add("password", "[password]");

运行结果:

{
    "access_token": "[new access token]",
    "token_type": "bearer",
    "expires_in": 86399,
    "refresh_token": "[new refresh token]"
}

搞定!

看起来挺简单,却折腾了一天。 希望在你折腾OAuth的时候,这篇博文能够帮你减少折腾的时间。

【参考资料】

Adding Refresh Tokens to a Web API v2 Authorization Server

EmbeddedResourceOwnerFlowWithRefreshTokens

Web API与OAuth:既生access token,何生refresh token

标签:

人气教程排行