ChuanGoing 2019-11-24html
asp.net core系列已經來到了第五篇,經過以前的基礎介紹,咱們瞭解了事件訂閱/發佈的eventbus整個流程,初探dapper ORM實現,而且簡單的介紹了領域模型、領域倉儲及服務實現,結合上一篇的日誌、錯誤處理及事務和本篇將要介紹的權限,大體的能夠造成一個簡單的後端系統架構。固然這些都是零散的一些技術概念的介紹,後面若是有時間的話,我想詳細的介紹下如何利用領域驅動來實現一個實際案例。git
話很少講,下面來看下本篇的學習曲線:github
1.認識Identityserver4web
2.Identityserver4實現認證與受權後端
3.自定義權限的實現api
認識Identityserver4瀏覽器
關於Identityserver4(ids4)的概念介紹,請查看IdentityServer4 知多少-簡書一文。我這裏要說的是,asp.net core 下的ids4集成了認證與受權兩大功能,使得咱們很是方便的實現一個開放的認證與受權平臺,好比公司內部多個系統的集成登陸(單點登陸)/第三方系統數據共享/統一的認證中心等。整個業務流程大體爲:緩存
1.用戶首先的有用戶中心的帳號信息,所以須要註冊一個帳號架構
2.用戶訪問某個站點應用,須要去到用戶中心認證app
3.認證經過,用戶獲得其在用戶中心註冊的相應信息及其權限時限、範圍、大小
4.認證不經過,即非法用戶,提示用戶註冊
5.在第3步的前提下,若用戶訪問到另外一個站點(採用同一認證平臺),這時用戶能夠用以前認證經過後拿到的訪問令牌訪問此站點,若此令牌中包含此站點的相應權限便可以前登陸。
Identityserver4實現認證與受權
首先,新建一個asp.net core web 空項目,而且添加以下IdentityServer4 Nuget包
在ConfigureServices添加以下代碼
註冊IdentityServer中間件,以下5個配置分別表示:
1.AddDeveloperSigningCredential:開發模式下的簽名證書,開發環境啓用便可
2.AddInMemoryApiResources:相關資源配置
public static IEnumerable<ApiResource> GetApiResources() { return new List<ApiResource> { new ApiResource("WebApi", "ChuanGoingWebApi"), new ApiResource("ProductApi", "ChuanGoingWebProduct") }; }
這裏配置了兩個Api資源
3.AddInMemoryIdentityResources:OpenID Connect相關認證信息配置
public static IEnumerable<IdentityResource> GetIdentityResources() { return new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile() }; }
4.AddInMemoryClients:客戶端信息配置
public static IEnumerable<Client> GetClients(IConfiguration Configuration) { var OnlineConfig = Configuration.GetSection("OnlineClient"); var List = new List<Client> { new Client() { ClientId = "ClientCredentials", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("ClientSecret".Sha256()) }, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "WebApi", "ProductApi" }, AccessTokenLifetime = 10 * 60 * 1 }, new Client() { ClientId = "ResourceOwnerPassword", AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, ClientSecrets = { new Secret("ClientSecret".Sha256()) }, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "WebApi", "ProductApi" }, AccessTokenLifetime = 10 * 60 * 1 }, /* 隱式模式:https://localhost:6005/connect/authorize?client_id=Implicit&redirect_uri=http://localhost:5000/Home&response_type=token&scope=WebApi */ new Client() { ClientId = "Implicit", ClientName = "ImplicitClient", AllowedGrantTypes = GrantTypes.Implicit, ClientSecrets = { new Secret("ImplicitSecret".Sha256()) }, RedirectUris ={OnlineConfig.GetValue<string>("RedirectUris") }, PostLogoutRedirectUris = {OnlineConfig.GetValue<string>("LogoutRedirectUris") }, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "WebApi", "ProductApi" }, AccessTokenLifetime = 10 * 60 * 1, //容許將token經過瀏覽器傳遞 AllowAccessTokensViaBrowser=true }, /* * 受權碼模式:https://localhost:6005/connect/authorize?client_id=GrantCode&redirect_uri=http://localhost:5000/Home&response_type=code&scope=WebApi */ new Client() { //客戶端Id ClientId="GrantCode", ClientName="GrantCodeClient", //客戶端密碼 ClientSecrets={new Secret("CodeSecret".Sha256()) }, //客戶端受權類型,Code:受權碼模式 AllowedGrantTypes=GrantTypes.Code, //容許登陸後重定向的地址列表,能夠有多個 RedirectUris ={OnlineConfig.GetValue<string>("RedirectUris") }, //容許訪問的資源 AllowedScopes={ "WebApi", "ProductApi" } } }; return List; }
分別對象Auth2.0的四種模式,本篇將用到的是ResourceOwnerPassword模式,其餘幾種可在篇尾github連接查看源碼的實現
5.AddTestUsers:用戶配置,可結合緩存/持久化
public static List<TestUser> GetUsers() { return new List<TestUser> { new TestUser { SubjectId = Guid.NewGuid().ToString(), Username = "admin", Password = "123456" //Claims = new List<Claim> //{ // new Claim("name", "admin"), // new Claim("website", "https://www.cnblogs.com/chuangoing") //} }, new TestUser { SubjectId = Guid.NewGuid().ToString(), Username = "chuangoing", Password = "123456" //Claims = new List<Claim> //{ // new Claim("name", "chuangoing"), // new Claim("website", "https://github.com/chuangoing") //} } }; }
定義兩個測試用戶,注意這裏的SubjectId,用做用戶中心註冊的openid(認證惟一),後面將會用到
而後,Configure中添加app.UseIdentityServer();//啓用ids4
至此,ids4 服務完成
用postman測試下:
返回jwt accesstoken:
將token內容解碼,以下:
能夠看到,裏面包含咱們配置的ProductApi/WebApi的權限
將token信息加入到http的header中:
注意Bearer後面有個空格,訪問order的獲取訂單信息:
自定義權限的實現
這裏,咱們將api中的action分別定義一個權限代碼,用戶擁有了此action訪問權限(擁有此權限代碼)便可訪問,簡單實現以下:
1.定義權限特性標識,api的action指定某個標識
public class PermissionAttribute : Attribute { /// <summary> /// 權限代碼 /// </summary> public string Code { get; } /// <summary> /// /// </summary> /// <param name="code">權限代碼</param> public PermissionAttribute(string code) { Code = code; } }
此處,get action定義了訪問權限標識爲"XYZ"
一樣,咱們這裏須要用到一個權限過濾器,利用過濾器的Aop實現權限過濾業務處理:
public class PermissionFilter : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext context) { var user = context.HttpContext.User; if (user.Identity.IsAuthenticated) { //TODO:用戶自定義權限驗證 Guid userId = context.HttpContext.GetId(); bool right; #region 自定義權限驗證 //根據userId判斷用戶內部系統權限信息 //var userPermissions = repo.GetUserPermissions(userId); //var permissions = repo.GetPermissions(); var metas = context.ActionDescriptor.EndpointMetadata; foreach (var meta in metas) { if (meta is PermissionAttribute permission) { //if (!permissions.Any(p => permission.Code.Any(c => c == p.Code)) // && !userPermissions.Any(p => permission.Code.Any(c => c == p.Code))) //{ // throw new WebException(HttpStatusCode.Forbidden, MessageCodes.AccessDenied, "你沒有訪問該資源的權限"); //} //break; } } right = false; #endregion if (!right) { context.Result = new ContentResult() { StatusCode = (int)HttpStatusCode.Forbidden, Content = "你沒有訪問該資源的權限" }; } } }
同時,啓用權限過濾器配置
部分代碼略過,詳細的請查看篇尾的源碼連接
利用第二節的認證受權獲得的token,咱們用postman測試下:
過濾器切面成功工做
還記得第一節說的SubjectId麼?這裏利用這個openid,去內部系統去匹配相關用戶信息,相關業務就不深刻了,有興趣的朋友能夠下載示例完善下
至此,整個權限認證、受權、自定義權限介紹完。
WebApi詳細代碼在Github的https://github.com/ChuanGoing/Start.git 的Domain分支能夠找到,AuthServer詳細代碼在https://github.com/ChuanGoing/Demo/tree/master/ChuanGoing.AuthorizationServer中。