5-OAuth2 & OpenID Connect &a

2018-08-05  本文已影响97人  俊果果

一、尝试获取 Claim 研究

  1. 之前获取到的 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 又回来了,而且 idpsid 被移除

  1. 查看 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-----新增一个订购实体图画的页面,在页面中显示登录用户的地址

  1. 新增一个Model类OrderFrameViewModel
    public class OrderFrameViewModel
    {
        public string Address { get; private set; } = string.Empty;

        public OrderFrameViewModel(string address)
        {
            Address = address;
        }
    }
  1. 新建页面
    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>
  1. 修改 _Layout.cshtml 添加导航

<li><a asp-area="" asp-controller="Gallery" asp-action="OrderFrame">订购实体版图画</a></li>

  1. 接下来编写 Controller 里面的 action【另一种手动获取 address 的方法】
        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

  1. 修改 IDP Config 类

new IdentityResource("guoguoextrainfo", "Guo's extra memo", new List<string>(){"extra"}),
new IdentityResource("roles", "Your role(s)", new List<string>(){"role"})

AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Address,
"roles",
"guoguoextrainfo"
}

new Claim("role","admin"),
new Claim("extra","俊果果是网站管理员")

  1. 修改 Client 的 Starup 类

options.Scope.Add("roles");
options.Scope.Add("guoguoextrainfo");
options.ClaimActions.MapUniqueJsonKey("role", "role");
options.ClaimActions.MapUniqueJsonKey("extra", "extra");

  1. 运行
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 确定权限

  1. 修改 OderFrame

public string ExtraInfo { get; set; }
public string Role { get; set; }

@if (@Model.Role == "FreeUser")
{
<div class="text">您还不是付费用户哦,请先成为会员吧!</div>
}
else
{
<div class="text">系统已记录请求,货品将择日发出!</div>
}

            // 获取 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);

再次运行,以不同角色的人登录,会显示不同的界面

  1. 新增 admin page, 管理员可用
                    @if (User.IsInRole("admin"))
                    {
                        <li><a asp-area="" asp-controller="Gallery" asp-action="ManageSite">管理网站</a></li>
                    }
                    options.TokenValidationParameters = new TokenValidationParameters()
                    {
                        NameClaimType = JwtClaimTypes.GivenName,
                        RoleClaimType = "role" 
                    };

运行后,只有配置的 role 为 admin 的用户登录时才能看到 按钮
但是,非管理员也可以登陆后直接复制该页面的链接访问进去
要避免这个问题, 将 ManageSite 打上如下所示属性标记

        [Authorize(Roles = "admin,administrator")]
        public async Task<IActionResult> ManageSite()
        {
            return View();
        }

再用无权限账户 copy 管理页面得到提示

image.png
发现被自动重定向到了 AccountControllerAccessDenied action

五、实现一个 AuthorizationController

  1. 新增 Controller
    public class AuthorizationController : Controller
    {
        public IActionResult AccessDenied()
        {
            return View();
        }
    }
  1. 新增 View
@{
    ViewBag.Title = "访问受限";
}

<h2>您当前无权访问此页面</h2>
<div class="container">
    <div>是否要<a asp-controller="Gallery" asp-action="Logout">注销并登陆其他账户</a>?</div>
</div>
  1. StarupConfigureServices 中将该网址配置到Cookie中
    image.png

完成后运行,会发现权限提示页面更新了


image.png

github 代码地址Claims 的使用

上一篇下一篇

猜你喜欢

热点阅读