• .NET Core IdentityServer4实战 第六章-Consent授权页


      在identityServer4中登陆页面只要是成功了,就会注册一个Cookie在服务器资源上,像现在大部分的网站第三方授权,都是经过一个页面,然后选需要的功能,IdentityServer4也给我们提供了,只要你登陆成功,就会跳转到Consent/Index(Get)中,所以我们只要在其中做手脚就好了。

      在编写代码之前我们要知道IdentityServer的三个接口, IClientStore 是存放客户端信息的, IResourceStore 是存放资源API信息的,这两个接口都是在IdentityServer4的Stores的命名空间下,还有一个接口是 IIdentityServerInteractionService 用于与IdentityServer通信的服务,主要涉及用户交互。它可以从依赖注入系统获得,通常作为构造函数参数注入到IdentityServer的用户界面的MVC控制器中。

      下面我们创建一个Consent控制器在认证服务器上,名为 ConsentController ,在其中我们需要将这三个接口通过构造函数构造进来。

    public class ConsentController : Controller
        {
            private readonly IClientStore _clientStore;
            private readonly IResourceStore _resourceStore;
            private readonly IIdentityServerInteractionService _identityServerInteractionService;
            public ConsentController(
              IClientStore clientStore,
              IResourceStore resourceStore, 
              IIdentityServerInteractionService identityServerInteractionService)
            {
                _clientStore = clientStore;
                _resourceStore = resourceStore;
                _identityServerInteractionService = identityServerInteractionService;
            }
    }

    在控制器中,因为登陆成功是从Account控制器调过来的,那个时候还带着ReturnUrl这个而参数,我们在这个控制器中也需要ReturnUrl,所以在Get方法中写上该参数,要不然跳转不过来的。

    public async Task<IActionResult> Index(string returnUrl)
            {
                var model =await BuildConsentViewModel(returnUrl);return View(model);
            }

    其中调用了 BuildConsentViewModel 方法用于返回一个consent对象,其中我们使用 _identityServerInteractionService 接口获取了上下文,然后再通过其余的两个接口找到它客户端还有资源api的信息。然后再调用了自定义的 CreateConsentViewModel 对象创建了consent对象。

    /// <summary>
            /// 返回一个consent对象
            /// </summary>
            private async Task<ConsentVm> BuildConsentViewModel(string returlUrl)
            {
                //获取验证上下文
                var request = await _identityServerInteractionService.GetAuthorizationContextAsync(returlUrl);
                if (request == null)
                    return null;
                //根据上下文获取client的信息以及资源Api的信息
                var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId);
                var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested);
                //创建consent对象
                var vm =  CreateConsentViewModel(request,client,resources);
                vm.ReturnUrl = returlUrl;
                return vm;
            }

     在其中创建对象并返回,只不过在获取ResourceScopes的时候,它是一个ApiResource,所以需要先转换成Scopes然呢再Select一下变成我们的ViewModel.

    /// <summary>
            /// 创建consent对象
            /// </summary>
            private ConsentVm CreateConsentViewModel(AuthorizationRequest request,Client client,Resources resources)
            {
                var vm = new ConsentVm(); 
                vm.ClientId = client.ClientId;
                vm.Logo = client.LogoUri;
                vm.ClientName = client.ClientName;
                vm.ClientUrl = client.ClientUri;//客户端url
                vm.RemeberConsent = client.AllowRememberConsent;//是否记住信息
                vm.IdentityScopes = resources.IdentityResources.Select(i=>CreateScopeViewModel(i));
                vm.ResourceScopes = resources.ApiResources.SelectMany(u => u.Scopes).Select(x => CreatesScoreViewModel(x));
                return vm;
            }
            public ScopeVm CreatesScoreViewModel(Scope scope)
            {
                return new ScopeVm
                {
                    name = scope.Name,
                    DisplayName = scope.DisplayName,
                    Description = scope.Description,
                    Required = scope.Required,
                    Checked = scope.Required,
                    Emphasize = scope.Emphasize
                };
            }
            private ScopeVm CreateScopeViewModel(IdentityResource identityResource)
            {
                return new ScopeVm
                {
                    name = identityResource.Name,
                    DisplayName = identityResource.DisplayName,
                    Description = identityResource.Description,
                    Required = identityResource.Required,
                    Checked = identityResource.Required,
                    Emphasize = identityResource.Emphasize
                };
            }

    以上我们的控制器就完成了,现在我们搞一下视图,在视图中我们就是简单做一下,使用ConsentVm作为视图绑定对象,在之中我遇到了一个Bug,我用 @Html.Partial("_ScopeListItem", item); 的时候突然就报错了,在页面上显示一个Task一大堆的错误信息,我也不知道啥情况(望大佬解决),换成不是异步的就行了。

    <p>Consent Page</p>
    @using mvcWebFirstSolucation.Models;
    @model ConsentVm
    <div class="row page-header">
        <div class="col-sm-10">
            @if (!string.IsNullOrWhiteSpace(Model.Logo))
            {
                <div>
                    <img src="@Model.Logo" /> 
                </div>
            }
            <h1>
                @Model.ClientName
                <small>欢迎来到第三方授权</small>
            </h1>
    
        </div>
    </div>
    <div class="row">
        <div class="col-sm-8">
            <form asp-action="Index">
                <input type="hidden" asp-for="ReturnUrl" />
                <div class="panel">
                    <div class="panel-heading">
                        <span class="glyphicon glyphicon-tasks"></span>
                        用户信息
                    </div>
                    <ul class="list-group">
                        @foreach (var item in Model.IdentityScopes)
                        {
                            @Html.Partial("_ScopeListItem", item);
                        }
                    </ul>
                </div>
                <div class="panel">
                        <div class="panel-heading">
                            <span class="glyphicon glyphicon-tasks"></span>
                            应用权限
                        </div>
                        <ul class="list-group">
                            @foreach (var item in Model.ResourceScopes)
                            {
                                @Html.Partial("_ScopeListItem", item);
                            }
                        </ul>
                    </div>
    
                <div>
                    <label>
                        <input type="checkbox" asp-for="RemeberConsent" />
                        <strong>记住我的选择</strong>
                    </label>
                </div>
                <div>
                    <button value="yes" class="btn btn-primary" name="button"  autofocus>同意</button>
                    <button value="no" name="button">取消</button>
                    @if (!string.IsNullOrEmpty(Model.ClientUrl))
                    {
                        <a href="@Model.ClientUrl" class="pull-right btn btn-default">
                            <span class="glyphicon glyphicon-info-sign"></span>
                            <strong>@Model.ClientUrl</strong>
                        </a>
                    }
                </div>
            </form>
        </div>
    </div>

    下面是局部视图的定义,传过来的对象是 ResourceScopes 和 IdentityScopes ,但他们都是对应ScopeVm,在其中呢就是把他们哪些权限列出来,然后勾选,在它的父页面已经做了post提交,所以我们还得弄个控制器。

    @using mvcWebFirstSolucation.Models;
    @model ScopeVm
    
    <li>
        <label>
            <input type="checkbox" 
                   name="ScopesConsented"
                   id="scopes_@Model.name" 
                   value="@Model.name" 
                   checked="@Model.Checked"
                   disabled="@Model.Required"/>
    
            @if (Model.Required)
            {   
                <input type="hidden" name="ScopesConsented" value="@Model.name" />
            }
    
            <strong>@Model.name</strong>
            @if (Model.Emphasize)
            {
                <span class="glyphicon glyphicon-exclamation-sign"></span>
            }
        </label>
        @if (!string.IsNullOrEmpty(Model.Description))
        {
            <div>
                <label for="scopes_@Model.name">@Model.Description</label>
            </div>
        }
    </li>

     这个方法的参数是我们所自定义的实体,其中有按钮还有返回的地址,在其中我们判断了是否选择OK,选择不那就直接赋一个拒绝的指令,如果ok那么就直接判断是否有这个权力,因为我们在config中进行了配置,然后如果有,呢么就直接添加,在不==null的清空下,我们根据 returlUrl 这个字符串获取了请求信息,然后通过 GrantConsentAsync 方法直接同意了授权,然后直接跳转过去,就成功了。

            [HttpPost]
            public async Task<IActionResult> Index(InputConsentViewModel viewmodel)
            {
                // viewmodel.ReturlUrl
                ConsentResponse consentResponse = null;
                if (viewmodel.Button =="no")
                {
                    consentResponse = ConsentResponse.Denied;
                }
                else
                {
                    if (viewmodel.ScopesConsented !=null && viewmodel.ScopesConsented.Any()) 
                    {
                        consentResponse = new ConsentResponse
                        {
                            RememberConsent = viewmodel.RemeberConsent,
                            ScopesConsented = viewmodel.ScopesConsented
                        };
                    }
                }
                if (consentResponse != null)
                {
                    var request = await _identityServerInteractionService.GetAuthorizationContextAsync(viewmodel.ReturnUrl);
                    await _identityServerInteractionService.GrantConsentAsync(request, consentResponse);
                    return Redirect(viewmodel.ReturnUrl);
                }
                return View(await BuildConsentViewModel(viewmodel.ReturnUrl));
            }

    最后,在调试的时候一定要Client的 RequireConsent 设置为true.

     

  • 相关阅读:
    javascript中万恶的function
    Windows7下如何安装部署秋色园CYQBlog源码V1.0网站
    Extjs2.2:Panel里面嵌入Excel表格
    Extjs做界面很酷;感谢博客园给我一个展示的机会;借此向大家展示一下EXTJS的魅力
    16Aspx.com改进版Extjs简单版酒店管理系统提供下载!
    Ext2.2+ASP.NET开发框架已完成欢迎大家下载!
    Extj+Asp.net开发框架V1.1树的操作
    Ext2.2程序开发实战(1)登录界面
    扩展欧几里得定理
    C语言 统计整数二进制表示中1的个数
  • 原文地址:https://www.cnblogs.com/ZaraNet/p/11146224.html
Copyright © 2020-2023  润新知