一、尝试获取 Claim 研究
-
之前获取到的 Claims
image.png
在 Client 端的 Startup 构造函数添加代码:
public Startup(IConfiguration configuration)
{
Configuration = configuration;
// 清除默认的 Claim Maper
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
}
再运行看输出:
Identity token:eyJhbGciOiJSUzI1NiIsImtpZCI6ImM5ODExYjU4NDg5Yzk4Y2RlOTNlYWM2NmJmMjVhNzUzIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MzM0NjYwODcsImV4cCI6MTUzMzQ2NjM4NywiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzMTkiLCJhdWQiOiJqdW5ndW9ndW9pbWFnZWdhbGxlcnljbGllbnQiLCJub25jZSI6IjYzNjY5MDYyODc5MTI0ODAyNC5OV0U1TkRrMU4yRXROVGMxTnkwMFlXWXpMVGxrTnpRdE1EaGhZemxrWW1WbE1XRTBOelptWWpCbVpUSXRZelJpWkMwME9Ea3pMV0kxWXpndE9UTTJPV1EyTldJNU1HVmgiLCJpYXQiOjE1MzM0NjYwODcsImF0X2hhc2giOiJtek1wNXM4dXdsMkdSTU1GTnN4bmV3Iiwic2lkIjoiMjYxN2JjZTBjZTkxOTJhOTZiYzhlZTdlZTU0ZmRmMzMiLCJzdWIiOiJiNzUzOTY5NC05N2U3LTRkZmUtODRkYS1iNDI1NmUxZmY1YzciLCJhdXRoX3RpbWUiOjE1MzM0NjYwODMsImlkcCI6ImxvY2FsIiwiYW1yIjpbInB3ZCJdfQ.DN52cfSDP4NNkP9r97bZJ66A_W9IiQLcf8hLLl7w9xbKE7HDNgD7aM36nHckxQ3gYi87p2aaMhUQ7xEth9K9e-Fu_KbdLQ9BUgSA3LrCqbEP2aiHcw6tqnhu85Ic15rDO5z926VLYOFNGE8ukbojZT9uwmJsPLLrrY4jO_I7mp_uUF4P4qoCXZ9IOaIHsHtTciYBW1O4D5uqU91osyRSOuQKwaT4DJy87dmAX-Xg7oN_9qKl227dZxaJ6ickkKCcjn5uSQzP2Zej3XvM8k2Q149Pn4uWabCyVXWDO8oouZkI4SQMu7jbGrZsJ6-TxBujjpB844DOo-f3pAbPn0FiwQ
Claim type : sid - Claim value : 2617bce0ce9192a96bc8ee7ee54fdf33
Claim type : sub - Claim value : b7539694-97e7-4dfe-84da-b4256e1ff5c7
Claim type : idp - Claim value : local
Claim type : name - Claim value : Frank Hawk
Claim type : given_name - Claim value : Frank
Claim type : family_name - Claim value : Hawk
发现没有了以前的 amr,编辑 ConfigureServices
中的 AddOpenIdConnect
, 添加代码
// 从 filter 中移除 amr, 代表需要 get 【amr】 的值
options.ClaimActions.Remove("amr");
// 不需要 sid 和 idp 的值
options.ClaimActions.DeleteClaim("sid");
options.ClaimActions.DeleteClaim("idp");
在运行
Identity token:eyJhbGciOiJSUzI1NiIsImtpZCI6ImM5ODExYjU4NDg5Yzk4Y2RlOTNlYWM2NmJmMjVhNzUzIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MzM0NjY1NzMsImV4cCI6MTUzMzQ2Njg3MywiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzMTkiLCJhdWQiOiJqdW5ndW9ndW9pbWFnZWdhbGxlcnljbGllbnQiLCJub25jZSI6IjYzNjY5MDYzMjc4MTMyMzA2Ny5OREk1TjJVeVlUY3RNVFJrTnkwMFpXTTJMVGhsTkdJdFlqVTBaVEU0Tm1VNU56TTVOekk1WVRJeE16UXRZbVF3TXkwME5HTXpMVGxpT0RFdFkyRXhaamd5TmpabU5HTTAiLCJpYXQiOjE1MzM0NjY1NzMsImF0X2hhc2giOiJpWUZ6aXhKSmxyNUl5cWlCbDJlYmhBIiwic2lkIjoiNzlhZTkxNzE1MTVjMDg4MTJjNmFmN2RiOGE4ODVlN2UiLCJzdWIiOiJiNzUzOTY5NC05N2U3LTRkZmUtODRkYS1iNDI1NmUxZmY1YzciLCJhdXRoX3RpbWUiOjE1MzM0NjY1NTksImlkcCI6ImxvY2FsIiwiYW1yIjpbInB3ZCJdfQ.kO-J-KNCltyMwKLNu-rJEx2ZDwSmexKEYFP4kG43ZSqLTdjqw13viIEWP42TnDfAaE341K7pzUYLPQJ0jB8jRwaLsrXo_Mab25Zt-KaVUGts3xCWq4pfqeKZv1w59OnHg2bRdWdNKtfXwJMG9D2H3dCd-a9gYH786W9F43kepw2smHWXPjoOW3o-bI5rHbV0o5tAfjdsrKsTIk26rnGB11zq3K_AG6lCZyycsKbi1lBDJw7-LuvY49vdaN-IEsC04ST7RdFL-BYNVHbg-ifXpN3gFx_qvWV8ku25UratTS5zyKZSgfQ4oI2IPSiMSEEzQOl0eHuTvK9znsDJ2G0H8A
Claim type : sub - Claim value : b7539694-97e7-4dfe-84da-b4256e1ff5c7
Claim type : amr - Claim value : pwd
Claim type : name - Claim value : Frank Hawk
Claim type : given_name - Claim value : Frank
Claim type : family_name - Claim value : Hawk
可以看到 amr
又回来了,而且 idp
和sid
被移除
- 查看 asp net core OpenIdConnecOptions源代码
image.png
默认只有这几种 Claim mapping, 所以即使options.ClaimActions.Remove("address");
也无法生效,因为本身没有 mapping
,那么,我们仿照添加如下代码options.ClaimActions.MapUniqueJsonKey("address", "address");
, 再运行查看,得到了 address 的值
Claim type : sub - Claim value : b7539694-97e7-4dfe-84da-b4256e1ff5c7
Claim type : amr - Claim value : pwd
Claim type : name - Claim value : Frank Hawk
Claim type : given_name - Claim value : Frank
Claim type : family_name - Claim value : Hawk
Claim type : profile - Claim value : https://frank.com
Claim type : address - Claim value : USA. LA
二、手动获取 UserInfo-----新增一个订购实体图画的页面,在页面中显示登录用户的地址
- 新增一个Model类
OrderFrameViewModel
public class OrderFrameViewModel
{
public string Address { get; private set; } = string.Empty;
public OrderFrameViewModel(string address)
{
Address = address;
}
}
- 新建页面
Views -> Gallery 新增 View :OrderFrame
仅用来显示 ViewModel 中的地址
@model ImageGallery.Client.ViewModels.OrderFrameViewModel
<div class="container">
<div class="h3 bottomMarginDefault">选择一张你喜欢的图片,定做一个实体画</div>
<div class="text bottomMarginSmall">系统记录您的地址为:</div>
<div class="text text-info bottomMarginSmall">@Model.Address</div>
<div class="text">如果这个地址不正确,请联系我们。</div>
</div>
- 修改
_Layout.cshtml
添加导航
<li><a asp-area="" asp-controller="Gallery" asp-action="OrderFrame">订购实体版图画</a></li>
- 接下来编写 Controller 里面的 action【另一种手动获取 address 的方法】
- nuget package 安装
IdentityModel
image.png
public async Task<IActionResult> OrderFrame()
{
// 获取 UserInfoEndpoint
var discoveryClient = new DiscoveryClient("https://localhost:44319");
var metaDataResponse = await discoveryClient.GetAsync();
var userInfoClient = new UserInfoClient(metaDataResponse.UserInfoEndpoint);
// 获取 AccessToken
var accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
// 获取 UserInfo
var response = await userInfoClient.GetAsync(accessToken);
if (response.IsError)
{
throw new Exception("Problem accesing the UserInfo endpoint");
}
var address = response.Claims.FirstOrDefault(c => c.Type == "address")?.Value;
return View(new OrderFrameViewModel(address));
}
运行,点击 OrderFrame 页,地址成功显示
image.png
三、新增 ClaimType
- 修改 IDP Config 类
- GetIdentityResources() 返回新增两个
new IdentityResource("guoguoextrainfo", "Guo's extra memo", new List<string>(){"extra"}),
new IdentityResource("roles", "Your role(s)", new List<string>(){"role"})
- 在 GetClients() 中把scope添加进去
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Address,
"roles",
"guoguoextrainfo"
}
- GetUsers() 里面给 user 赋相应的值
new Claim("role","admin"),
new Claim("extra","俊果果是网站管理员")
- 修改 Client 的 Starup 类
- ConfigureServices::AddAuthentication 添加代码
options.Scope.Add("roles");
options.Scope.Add("guoguoextrainfo");
options.ClaimActions.MapUniqueJsonKey("role", "role");
options.ClaimActions.MapUniqueJsonKey("extra", "extra");
- 运行
-
登录后的授权界面
image.png - 输出日志
Identity token:eyJhbGciOiJSUzI1NiIsImtpZCI6ImM5ODExYjU4NDg5Yzk4Y2RlOTNlYWM2NmJmMjVhNzUzIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MzM0NzIyMTgsImV4cCI6MTUzMzQ3MjUxOCwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzMTkiLCJhdWQiOiJqdW5ndW9ndW9pbWFnZWdhbGxlcnljbGllbnQiLCJub25jZSI6IjYzNjY5MDY4NTY0OTA0NjAwNC5aamsyTWpSallUTXRNREJqTkMwME5EQTNMV0V4WmpVdE9HRXpNelE1T1RVM1ltSTROek14WkRCbU4yRXRNemsxTVMwME1XSXpMVGxpWmpNdE0yWmlZamt4TnpZM01UZGoiLCJpYXQiOjE1MzM0NzIyMTgsImF0X2hhc2giOiJnT1pJMl8zR2h4TWpiRUxHTUdPbXlRIiwic2lkIjoiNWFhODBkYWE1NzBlNWFkODk5NjlmODAyOWNhZjY5YWEiLCJzdWIiOiJiNzUzOTY5NC05N2U3LTRkZmUtODRkYS1iNDI1NmUxZmY1YzciLCJhdXRoX3RpbWUiOjE1MzM0NzE4MDMsImlkcCI6ImxvY2FsIiwiYW1yIjpbInB3ZCJdfQ.tpBYUSM16EDUNKT4XHcUqWgFkkX8DnNc9RlGqVf2Tkg70fdHfSuNgukhhuEK2vCusdIyE9ovtQwCHhrjNoqg2t1Xa9fFmNdcKY2JeDsS_1_dQCUQzvAWCXiRwNiRNeeer6bgMkJWCMat5ERclUthiM5t3d6mipMZDXuUioMZsxnhSeHk21_Eod8qme-yeakEFYGwzKOf19LBsJtVkfBDWc1pgIH86h2TjRBCbNXL96W1QUUq_RS1GvCfo4GY-WqKlvzLebKs9TcISDnoYXt6PmVYvQPw7gbTeMW91XxPWrqJgiKvVnY7wnPRxxfZDqvNa3-mnh15Z4GeVZgyPole9w
Claim type : sub - Claim value : b7539694-97e7-4dfe-84da-b4256e1ff5c7
Claim type : amr - Claim value : pwd
Claim type : name - Claim value : Frank Hawk
Claim type : given_name - Claim value : Frank
Claim type : family_name - Claim value : Hawk
Claim type : profile - Claim value : https://frank.com
Claim type : address - Claim value : USA. LA
Claim type : role - Claim value : FreeUser
Claim type : extra - Claim value : Frank 才14岁就上大学了,厉害
信息全部取出!
四、利用 role claim 确定权限
- 修改 OderFrame
- 修改
OrderFrameViewModel
, 添加属性
public string ExtraInfo { get; set; }
public string Role { get; set; }
- 修改
OrderFrame.cshtml
@if (@Model.Role == "FreeUser")
{
<div class="text">您还不是付费用户哦,请先成为会员吧!</div>
}
else
{
<div class="text">系统已记录请求,货品将择日发出!</div>
}
- 修改
Controller::OrderFrame
方法
// 获取 Model
var model = new OrderFrameViewModel(User.Claims.FirstOrDefault(c => c.Type == "address")?.Value);
model.ExtraInfo = User.Claims.FirstOrDefault(c => c.Type == "extra")?.Value;
model.Role = User.Claims.FirstOrDefault(c => c.Type == "role")?.Value;
// 传入 View
return View(model);
再次运行,以不同角色的人登录,会显示不同的界面
- 新增 admin page, 管理员可用
- 修改
_Layout
页面, nav bar 新增按钮
@if (User.IsInRole("admin"))
{
<li><a asp-area="" asp-controller="Gallery" asp-action="ManageSite">管理网站</a></li>
}
- 为该按钮新增的 action 添加 View 和 Controller 处理逻辑
- 修改
Startup
的配置,添加
options.TokenValidationParameters = new TokenValidationParameters()
{
NameClaimType = JwtClaimTypes.GivenName,
RoleClaimType = "role"
};
运行后,只有配置的 role 为 admin 的用户登录时才能看到 按钮
但是,非管理员也可以登陆后直接复制该页面的链接访问进去
要避免这个问题, 将 ManageSite
打上如下所示属性标记
[Authorize(Roles = "admin,administrator")]
public async Task<IActionResult> ManageSite()
{
return View();
}
再用无权限账户 copy 管理页面得到提示
发现被自动重定向到了
AccountController
的 AccessDenied
action
五、实现一个 AuthorizationController
- 新增 Controller
public class AuthorizationController : Controller
{
public IActionResult AccessDenied()
{
return View();
}
}
- 新增 View
@{
ViewBag.Title = "访问受限";
}
<h2>您当前无权访问此页面</h2>
<div class="container">
<div>是否要<a asp-controller="Gallery" asp-action="Logout">注销并登陆其他账户</a>?</div>
</div>
- 在
Starup
的ConfigureServices
中将该网址配置到Cookie中
image.png
完成后运行,会发现权限提示页面更新了
image.png
网友评论