1. 程式人生 > >Web API與OAuth:既生access token,何生refresh token

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

在前一篇博文中,我們基於 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的時候,這篇博文能夠幫你減少折騰的時間。

【參考資料】

相關推薦

Web APIOAuthaccess tokenrefresh token

在前一篇博文中,我們基於 ASP.NET Web API 與 OWIN OAuth 以 Resource Owner Password Credentials Grant 的授權方式( grant_type=password )獲取到了 access token,並以這個 token 成功呼叫了與當前使用者(

ASP.NET Web APIOwin OAuth使用Access Toke呼叫受保護的API(二)

在前一篇博文中,我們使用OAuth的Client Credential Grant授權方式,在服務端通過CNBlogsAuthorizationServerProvider(Authorization Server的一個實現)成功發放了Access Token,並在客戶

ASP.NET Web API執行提示找到了該請求匹配的多個操作的解決方法

ASP.NET Web API執行提示:找到了與該請求匹配的多個操作。 開啟App_Start目錄下的WebApiConfig.cs配置檔案可以看到,路由中預設的配置資訊。 config.Routes.MapHttpRoute( name: "DefaultApi", r

web API簡介(三)客戶端儲存之Web Storage API

mark gpo .com htm tor b-s href 簡介 logs 概述 前篇:web API簡介(二):客戶端儲存之document.cookie API 客戶端儲存從某一方面來說和動態網站差不多。動態網站是用服務端來儲存數據,而客戶端儲存是用客戶端來儲存數據。

web API簡介(四)客戶端儲存之IndexedDB API

特性 通知 name com 模式 upgrade doesn 需要 code 概述 前篇:web API簡介(三):客戶端儲存之Web Storage API 客戶端儲存從某一方面來說和動態網站差不多。動態網站是用服務端來儲存數據,而客戶端儲存是用客戶端來儲存數據。 In

Web API 持續整合PostMan+Newman+Jenkins(圖文講解)

本文由葡萄城技術團隊於部落格園原創並首發 轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。 上篇文章我們已經完成了API測試工具選型,接下來是一系列週期性的開發測試過程:介面開發、檢出程式碼、執行測試、記錄結果、傳送報告。為了快速發現問題,並減少重複過

曾——曾國藩左宗棠一生恩怨考

一   曾國藩與左宗棠的首次見面,是在咸豐二年(1852)十二月二十一日傍晚。   曾國藩回湖南本是為母親辦喪事。沒想到恰逢太平軍橫掃兩湖,皇帝命他出任幫辦湖南團練大臣。曾國藩趕到長沙。   到了館舍,換過衣服,匆匆洗了把臉,曾國藩就坐下來,與前來迎接的湖南巡撫張亮基及

Web API 持續整合PostMan+Newman+Jenkins

作者:葡萄城技術開發團隊 www.cnblogs.com/powertoolsteam/p/9881398.html   上篇文章我們已經完成了API測試工具選型,接下來是一系列週期性的開發測試過程:介面開發、檢出程式碼、執行測試、記錄結果、傳送報告。為了快速發

Web編程基礎CSS、JavaScript、jQuery (婕著) 完整pdf掃描版

3.2 jquery 知識點 網頁特效 3.1 OS 文本 scrip clas 《Web編程基礎:CSS、JavaScript、jQuery》緊密圍繞網頁設計師在進行Web前臺開發中實際需要掌握的技術,全面介紹了使用DIV+CSS、JavaScript、jQuery進行W

重磅報告 | 《解構重組開啟智慧經濟》數字經濟下一站

2019年1月6日,由阿里巴巴集團發起、阿里研究院主辦的第四屆新經濟智庫大會在北京舉行。本屆大會以“啟航智慧經濟”為主題,聚焦“科技變革、經濟動力、展望未來”等相關話題,與國內外思想者共同尋找開啟“智慧經濟”的密碼。阿里研究院在會上釋出《解構與重組:開啟智慧經濟》報告。 2018年前後,是兩個重要時段的轉換

MahoutSpark MLlib ?

Apache Mahout與Spark MLlib均是Apache下的專案,都是機器學習演算法庫

挖坑指南百度鷹眼web api的使用問題總結

前言 公司想做外勤管理的工具,要記錄員工的出行軌跡,研究了一下,發現高德、騰訊都沒有相關的API,只有百度地圖有一個鷹眼的服務。那就試試唄~ 開始 貨比三家,先來看看其他兩家的解決方案。 高德地圖:https://lbs.amap.com/dev/demo/path-recor

循序漸進學.Net Core Web Api開發系列【0】序言目錄

一、序言        我大約在2003年時候開始接觸到.NET,最初在.NET framework 1.1版本下寫過程式碼,曾經做過WinForm和ASP.NET開發。大約在2010年的時候轉型JAVA環境,這麼多年來,雖然工作已面向JAVA方向,但沒有放棄過.NET,一直保持關注,內心由衷覺得C#是我接

循序漸進學.Net Core Web Api開發系列【16】應用安全續-加密解密

系列目錄 一、概述 應用安全除了使用者許可權認證外,還要考慮到資料安全,傳輸安全、系統漏洞等方面。本篇文章重點討論資料儲存安全和傳輸安全,主要技術手段就是加密和解密。 二、基本概念 資訊在傳輸和儲存的過程中有洩密的風險,加密的目的就是解決這些風險。 1、資訊儲存在資料庫中,如果資料庫

003.springboot web快速構建較複雜的RESTful API單元測試

說明 通過實現訂單的增刪改查,初步瞭解web相關的構建和使用 REStful API規劃 請求型別 URL 說明 GET /orders

《物聯網框架ServerSuperIO教程》-22.Web端對傳感器實時監測控制。附v3.6.8版本支持WebSocket

實時數據 title bmp 角色 1.4 增加 str 通訊 git 1.ServerSuperIO v3.6.8更新內容 1.1 增加WebSocket服務端功能,支持自控模式、並發模式、單例模式,不支持輪詢模式1.2 接收數據緩存與現有的IO實例分離。1.3 優化代

品嘗阿裏雲容器服務初步嘗試ASP.NET Core Web API站點的Docker自動化部署

asp.net title build 阿裏雲服務器 arm web 容器服務 倉庫 acs 部署場景是這樣的,我們基於 ASP.NET Core 2.0 Preview 1 開發了一個用於管理緩存的 Web API ,想通過阿裏雲容器服務基於 Docker 部署為內網服務

使用 Swagger 自動生成 ASP.NET Core Web API 的文檔、在線幫助測試文檔(ASP.NET Core Web API 自動成文檔)

地址 .cn 名稱 cor 生成文檔 def pos 構建 回車 對於開發人員來說,構建一個消費應用程序時去了解各種各樣的 API 是一個巨大的挑戰。在你的 Web API 項目中使用 Swagger 的 .NET Core 封裝 Swashbuckle 可以幫助你創建良好

Dynamics CRM 2015/2016 Web APIUnbound Action 和 Bound Action

account token com red wait font content soap消息 clas 上篇文章介紹了Bound/Unbound Function。今天我們來看看Action吧。像我之前說的:Function和Action之前的差別能夠簡單理解為。Fun

Dynamics CRM 2015/2016 Web API聚合查詢

當前 沒有 access 今天 接下來 fetch b2c send 查詢 各位小夥伴們,今天是博主2016年發的第一篇文章。首先祝大家新年快樂。工資Double,哈哈。今天我們來看一個比較重要的Feature--使用Web API運行FetchXML查詢!對的,各位。