ChuanGoing 2019-11-24
asp.net core系列已經來到了第五篇,通過之前的基礎介紹,我們了解了整個流程,實現,並且簡單的介紹了實現,結合上一篇的和本篇將要介紹的權限,大致的可以形成一個簡單的後端系統架構。當然這些都是零散的一些技術概念的介紹,後面如果有時間的話,我想詳細的介紹下如何利用領域驅動來實現一個實際案例。
話不多講,下面來看下本篇的學習曲線:
1.認識Identityserver4
2.Identityserver4實現認證與授權
3.自定義權限的實現
認識Identityserver4
關於Identityserver4(ids4)的概念介紹,請查看一文。我這裏要說的是,asp.net core 下的ids4集成了認證與授權兩大功能,使得我們非常方便的實現一個開放的認證與授權平台,比如公司內部多個系統的集成登錄(單點登錄)/第三方系統數據共享/統一的認證中心等。整個業務流程大致為:
1.用戶首先的有用戶中心的賬號信息,因此需要註冊一個賬號
2.用戶訪問某個站點應用,需要去到用戶中心認證
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")
};
}
GetApiResources
這裏配置了兩個Api資源
3.AddInMemoryIdentityResources:OpenID Connect相關認證信息配置
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile()
};
}
GetIdentityResources
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;
}
GetClients
分別對象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")
//}
}
};
}
GetUsers
定義兩個測試用戶,注意這裏的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;
}
}
PermissionAttribute
此處,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 = "你沒有訪問該資源的權限" };
}
}
}
PermissionFilter
同時,啟用權限過濾器配置
部分代碼略過,詳細的請查看篇尾的源碼鏈接
利用第二節的認證授權得到的token,我們用postman測試下:
過濾器切面成功工作
還記得第一節說的SubjectId么?這裏利用這個openid,去內部系統去匹配相關用戶信息,相關業務就不深入了,有興趣的朋友可以下載示例完善下
至此,整個權限認證、授權、自定義權限介紹完。
WebApi詳細代碼在Github的 的Domain分支可以找到,AuthServer詳細代碼在中。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】
※帶您來了解什麼是 USB CONNECTOR ?
※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象
※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!
※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化
※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益