IdentityServer4是基於ASP.NET Core實現的認證和受權框架,是對OpenID Connect和OAuth 2.0協議的實現。html
OpenID Connect:git
OpenID Connect由OpenID基金會於2014年發佈的一個開放標準, 是創建在OAuth 2.0協議上的一個簡單的身份標識層, OpenID Connect 兼容 OAuth 2.0. 實現身份認證(Authentication)
參考資料:https://openid.net/connect/
OpenID Connect文檔:https://openid.net/specs/openid-connect-discovery-1_0.htmlgithub
OAuth2.0:web
OAuth2.0是一個開放的工業標準的受權協議(Authorization),它容許用戶受權讓第三方應用直接訪問用戶在某一個服務中的特定資源,但不提供給第三方帳號及密碼信息
參考資料:http://www.javashuo.com/article/p-hgfzaqzn-r.html
OAuth2.0 文檔:https://tools.ietf.org/html/rfc6749#page-73sql
authentication: n. 證實;鑑定;證明 authorization: n. 受權,承認;批准,委任
前者是身份識別,鑑別你是誰;後者是受權許可,告訴你能夠作什麼。
舉個例子:你吭哧吭哧寫了一天的代碼,急於回家吃上一口媳婦作的熱飯。當你走到小區門口的時候你須要刷小區的門禁卡才能進入到小區裏面,而後再找到你家在哪一棟樓,幾單元幾號,而後掏出鑰匙開門才能回到家。在這個過程當中刷小區的門禁就是認證你是這個小區的人,拿你家的鑰匙開門就是受權的過程,若是你的認證不經過,那就不存在受權。數據庫
咱們先來了解一下OAuth2.0中的幾個關鍵概念:api
一個可以訪問受保護資源的實體。當資源全部者是一我的時,它被稱爲終端用戶安全
託管受保護資源的服務器,可以使用訪問令牌接受和響應受保護的資源請求服務器
表明資源全部者和其受權的應用程序來保護資源請求。術語客戶端並不意味着任何特定的實現特徵(例如,應用程序是否在服務器、桌面或其餘設備上執行)app
在成功驗證資源全部者並得到受權以後,服務器向客戶端發出訪問令牌。(受權服務器是用來管理Resource Owner,Resource Server,Client的中間人)
場景:小李想要打印(美圖快印)本身三年來發布在新浪微博相冊中和女友的照片,有沒有什麼方法他既不告訴工做人員本身的新浪微博用帳號和密碼又可以方便快捷的把照片給到美圖快印呢?(排除存U盤這種手工操做)
Authorization Server和Resource Server可使獨立的服務提供商,也能夠是在一塊兒的,好比例子中新浪微博既做受權服務器也用來存儲用戶的圖片資源。咱們能夠看到OAuth2解決的問題是:經過Authorization Server能夠提供一個訪問的憑據(token)給client(美圖快印的工做人員),使得client能夠在不知道Resource Owner以及Resource Server的用戶名和密碼的狀況下訪問到Resource Owner受保護的資源,它是一個完美的中間人。
OAuth2.0詳細內容請參考:http://www.javashuo.com/article/p-hgfzaqzn-r.html
基於OpenID Connect實現的獨立的認證服務實現對多平臺(web, native, mobile, services)的集中認證
爲各類類型的客戶機頒發api訪問令牌,例如服務器到服務器、web應用程序、spa和native/mobile程序
支持外部身份提供者,如Azure Active Directory、Google、Facebook等
IdentityServer4的許多方面能夠定製以知足您的須要,由於它是一個框架,而不是SaaS服務,因此能夠經過編寫代碼來調整實現,以適應不一樣的場景
使用許可的Apache2開源協議,容許在其之上構建商業產品,也做爲.NET基金會支持的項目 (https://dotnetfoundation.org/projects?type=project&ps=10&pn=6)
官方能夠對使用者提供部分的免費商業支持
IdentityServer
身份認證服務器是一個實現了OpenID Connect和OAuth 2.0協議的身份提供者,它負責向客戶端發佈安全令牌
User
使用註冊客戶端訪問資源的用戶
Client
客戶端從標識服務器請求令牌,要麼用於認證用戶(請求身份令牌),要麼用於訪問資源(請求訪問令牌)
客戶端必須首先在身份服務器上註冊,而後才能請求令牌
這裏的客戶端能夠是web應用程序、native mobile, desktop applications, SPA 等程序
Resource
資源是你想要用身份認證服務器保護的東西,如:用戶的身份數據或api
每一個資源都有一個唯一的名稱,客戶端使用這個名稱來指定他們想要訪問的資源
關於用戶的身份數據標識(也稱爲claim),例如姓名或電子郵件地址
Identity Token
身份令牌表明身份驗證過程的結果
Access Token
訪問令牌受權客戶端以容許訪問哪些API資源,訪問令牌包含客戶端和用戶的信息
咱們先來看一個簡單的例子,咱們有三個API ,Order, Product, Inventory,咱們利用IdentityServer4來實現對着三個API的認證和受權。首先咱們須要一個實現認證和受權的服務,而後外部要想訪問咱們的API就必須經過統一的認證和受權服務的任何才能夠,不然就是返回401: UnAuthorized ,未經受權的訪問。咱們既能夠將身份信息存儲到內存中,也能夠將其持久化到數據庫中,此處咱們使用內存模式快速的演示實現(示例代碼中也支持存儲到DB中,使用SqlLite + EF Core)
首先咱們須要安裝IdentityServer4的Nuget包,而後在ConfigureServices方法中添加以下代碼來初始化須要保護的API資源信息,代碼以下:
public void ConfigureServices(IServiceCollection services) { // config data in memory services.AddIdentityServer() .AddDeveloperSigningCredential() .AddInMemoryApiResources(InitMemoryData.GetApiResources()) .AddInMemoryClients(InitMemoryData.GetClients()) .AddTestUsers(InitMemoryData.GetUsers()); // config in DB //services.AddDbContext<IdentityServerDbContext>(options => // options.UseSqlite(sqliteConnection)); }
InitMemoryData 中的配置信息以下:
// scopes define the API resources in your system public static IEnumerable<ApiResource> GetApiResources() { return new List<ApiResource> { new ApiResource("inventoryapi", "this is inventory api"), new ApiResource("orderapi", "this is order api"), new ApiResource("productapi", "this is product api") }; } // clients want to access resources (aka scopes) public static IEnumerable<Client> GetClients() { // client credentials client return new List<Client> { new Client { ClientId = "inventory", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("inventorysecret".Sha256()) }, AllowedScopes = { "inventoryapi" } }, new Client { ClientId = "order", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("ordersecret".Sha256()) }, AllowedScopes = { "orderapi" } }, new Client { ClientId = "product", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("productsecret".Sha256()) }, AllowedScopes = { "productapi" } } }; }
咱們給IdentityServer4設置啓動端口5000,認證服務的地址就是:http://localhost:5000
而後認證Server端的代碼就行了,接下來咱們須要在API添加受權服務的配置,配置都很相似,咱們以OrderAPI爲例:
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMvcCore() .AddAuthorization() .AddJsonFormatters(); services.AddAuthentication("Bearer") .AddIdentityServerAuthentication(options => { options.Authority = "http://localhost:5000"; options.RequireHttpsMetadata = false; options.ApiName = "orderapi"; }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseAuthentication(); app.UseMvc(); }
這裏咱們配置的Authority地址就是認證受權的地址,AddAuthentication中的Bearer是Jwt Token的一種,具體可參考文章:http://www.javashuo.com/article/p-xtvsqjsu-e.html
在controller中添加簡單代碼來返回API的信息:
[Route("[controller]")] [Authorize] public class OrderController : ControllerBase { // GET api/order [HttpGet] public IActionResult Get() { var userIdentitys = from c in User.Claims select new UserIdentity { Type = c.Type, Value = c.Value }; var result = new UserIdentityModel() { Description = "Access user order api successfully", UserIdentitys = userIdentitys.ToList() }; return new JsonResult(result); } }
設置當前API的端口爲:5002
Product和Inventory中的配置和這個相似,端口信息以此設置爲5001,5003,一切就緒,讓咱們來測試一下結果:
啓動IdentotyServer,以及三個API,咱們使用Postman來請求api,下面站點就是IdentityServer的頁面了:
接着咱們來直接訪問OrderAPI就會發現返回 401 ,這說明目前咱們的API已經受保護了,沒有認證服務頒發的token,是直接訪問不了的。
咱們輸入地址:http://localhost:5000/.well-known/openid-configuration 能夠查看咱們當前認證受權服務的配置信息:
如今還差一步就能夠訪問咱們的OrderAPI了,那就是:客戶端傳入必要的信息給認證服務,生成必定格式的token,而後攜帶着這個token來訪問咱們的服務
傳入的三個參數分別是grant_type , client_sercret, client_id這幾個參數分別表明了咱們申請token時的受權方式是客戶端受權,密匙,clientid信息。咱們在前面介紹過IdentityServer4是對OAuth2.0的實現,因此具體參數的含義請參考以前OAuth2.0文章中的詳細介紹
http://www.javashuo.com/article/p-hgfzaqzn-r.html
此時咱們能夠看到認證服務給咱們返回了有效token,指定過時時間3600s ,token的類型是Bearer,而後咱們再攜帶這這個token去訪問服務試試看:
咱們能夠看到此時API 返回了咱們期待的正確結果,若是在1小時後再攜帶着這個token去訪問API就會提示token已過時,須要從新生成纔可以繼續訪問。看完這個例子是否是很簡單,很清爽呢