最稳的pk10计划iphone 北京pk10计划手机软件 北京pk10数字的规律 超神手机版pk10软件 pk10北京赛车9码技巧 pk10四期倍投计划表 pk10极速赛车论坛 北京赛车冠军怎样选5码 北京赛车系统下载安装 pk10教程视频 北京pk10选号公式 北京赛车pk10赚钱技巧 北京赛车怎么提升概率 pk10技巧北京快三 北京pk10大小计划
VB.net 2010 視頻教程 VB.net 2010 視頻教程 VB.net 2010 視頻教程
SQL Server 2008 視頻教程 c#入門經典教程 Visual Basic從門到精通視頻教程
當前位置:
首頁 > 數據庫 > Access數據庫 >
  • access數據庫之Web API與OAuth:既生access token,何生

  • 2019-07-02 21:39 來源:未知

在前一篇博文中,我們基于 ASP.NET Web API 與 OWIN OAuth 以 Resource Owner Password Credentials Grant 的授權方式( grant_type=password )獲取到了 access token,并以這個 token 成功調用了與當前用戶(resource owner)關聯的 Web API。

本以為搞定了 access token 就搞定了 Web API 的驗證與授權問題,可是發現 OAuth 中還有一種 token,叫 refresh token。開始的時候很是納悶,access token 已經能解決問題,為什么要搞定兩套 token,refresh token 有啥用?在納悶之下,發出了這樣的感慨:既生 access token,何生 refresh token?

后來看了一些資料,有點明白了。refresh token 是專用于刷新 access token 的 token。

為什么要刷新 access token 呢?一是因為 access token 是有過期時間的,到了過期時間這個 access token 就失效,需要刷新;二是因為一個 access token 會關聯一定的用戶權限,如果用戶授權更改了,這個 access token 需要被刷新以關聯新的權限。

為什么要專門用一個 token 去更新 access token 呢?如果沒有 refresh token,也可以刷新 access token,但每次刷新都要用戶輸入登錄用戶名與密碼,多麻煩。有了 refresh token,可以減少這個麻煩,客戶端直接用 refresh token 去更新 access token,無需用戶進行額外的操作。

兩個為什么也許沒有解釋清楚 refresh token 的用途,下面我們用示例代碼在 ASP.NET Web API 與 OWIN OAuth 中實際體驗一下,或許有更直觀的認識。

(一)Refresh token 的生成、發放、保存

實現一個 RefreshTokenProvider ,比如 CNBlogsRefreshTokenProvider。

需要重載 Microsoft.Owin.Security.Infrastructure.AuthenticationTokenProvider 中的 Create() 與 Receive() 方法(或者直接實現 IAuthenticationTokenProvider 接口),示例代碼如下:

復制代碼
public class CNBlogsRefreshTokenProvider : AuthenticationTokenProvider
{
    private static ConcurrentDictionary<string, string> _refreshTokens = new ConcurrentDictionary<string, string>();

    public override void Create(AuthenticationTokenCreateContext context)
    {
        string tokenValue = Guid.NewGuid().ToString("n");

        context.Ticket.Properties.IssuedUtc = DateTime.UtcNow;        
        context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddDays(60);

        _refreshTokens[tokenValue] = context.SerializeTicket();

        context.SetToken(tokenValue);
    }

    public override void Receive(AuthenticationTokenReceiveContext context)
    {
        string value;
        if (_refreshTokens.TryRemove(context.Token, out value))
        {
            context.DeserializeTicket(value);
        }
    }
}
復制代碼

(注:后來采用的是重載CreateAsync()方法)

然后應用這個 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": "7f7edd15cba043c29d487235c2276eb1"
}
復制代碼

成功拿到了 access token。

(四)測試客戶端用 refresh token 刷新 access token

客戶端測試代碼如下:

復制代碼
public async Task GetAccessTokenByRefreshTokenTest()
{
    var clientId = "[clientId]";
    var clientSecret = "[clientSecret]";

    var parameters = new Dictionary<string, string>();
    parameters.Add("grant_type", "refresh_token");
    parameters.Add("refresh_token", "7f7edd15cba043c29d487235c2276eb1");

    _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

Katana source code

Enable OAuth Refresh Tokens in AngularJS App using ASP .NET Web API 2, and Owin

pk10赛车冠军技巧
最稳的pk10计划iphone 北京pk10计划手机软件 北京pk10数字的规律 超神手机版pk10软件 pk10北京赛车9码技巧 pk10四期倍投计划表 pk10极速赛车论坛 北京赛车冠军怎样选5码 北京赛车系统下载安装 pk10教程视频 北京pk10选号公式 北京赛车pk10赚钱技巧 北京赛车怎么提升概率 pk10技巧北京快三 北京pk10大小计划
海口滴滴哪个时间段好赚钱 北京麻将手机游戏 浙江11选5推荐号 狂野之血哪一关最赚钱 全民一起玩捕鱼 2019年狗狗币行情涨幅涨跃 财智家庭理财官网 全民捕鱼点卷 快速时时的套路