zoukankan      html  css  js  c++  java
  • 5. 使用IdentityServer4 实现 基于OIDC 的内部MVC客户端认证

    1. 概述

    • 本例在上一个示例的基础上,为客户端登录过程增加Consent 确认过程.

    • Consent 页面适合外部客户端登录时需要我们授权中心授权的场景。

    • 本示例继续使用上一个示例中隐式授权码(Implicit)模式,当用户访问受保护的客户端页面时,MVC客户端会重定向到登录页,输入用户名密码后再重定向到Conset 页面。

    2. AuthServer 端增加Consent控制器和视图

    View 核心代码:

    @model ConsentViewModel
    
    <div class="page-consent">
        <div class="lead">
    <!-- client logo-->
            @if (Model.ClientLogoUrl != null)
            {
    
                <div class="client-logo"><img src="@Model.ClientLogoUrl"></div>
            }
            <h1>
                @Model.ClientName
                <small class="text-muted">is requesting your permission</small>
            </h1>
            <p>Uncheck the permissions you do not wish to grant.</p>
        </div>
    
        <div class="row">
            <div class="col-sm-8">
            </div>
        </div>
    
        <form asp-action="Index">
            <input type="hidden" asp-for="ReturnUrl" />
            <div class="row">
                <div class="col-sm-8">
         <!-- client scopes-->
                    @if (Model.IdentityScopes.Any())
                    {
                        <div class="form-group">
                            <div class="card">
                                <div class="card-header">
                                    <span class="glyphicon glyphicon-user"></span>
                                    Personal Information
                                </div>
                                <ul class="list-group list-group-flush">
                                    @foreach (var scope in Model.IdentityScopes)
                                    {
                                        <partial name="_ScopeListItem" model="@scope" />
                                    }
                                </ul>
                            </div>
                        </div>
                    }
    
                    @if (Model.ApiScopes.Any())
                    {
                        <div class="form-group">
                            <div class="card">
                                <div class="card-header">
                                    <span class="glyphicon glyphicon-tasks"></span>
                                    Application Access
                                </div>
                                <ul class="list-group list-group-flush">
                                    @foreach (var scope in Model.ApiScopes)
                                    {
                                        <partial name="_ScopeListItem" model="scope" />
                                    }
                                </ul>
                            </div>
                        </div>
                    }
    
                    <div class="form-group">
                        <div class="card">
                            <div class="card-header">
                                <span class="glyphicon glyphicon-tasks"></span>
                                Description
                            </div>
                            <div class="card-body">
                                <input class="form-control" placeholder="Description or name of device" asp-for="Description" autofocus>
                            </div>
                        </div>
                    </div>
    
                    @if (Model.AllowRememberConsent)
                    {
                        <div class="form-group">
                            <div class="form-check">
                                <input class="form-check-input" asp-for="RememberConsent">
                                <label class="form-check-label" asp-for="RememberConsent">
                                    <strong>Remember My Decision</strong>
                                </label>
                            </div>
                        </div>
                    }
                </div>
            </div>
    
            <div class="row">
                <div class="col-sm-4">
                    <button name="button" value="yes" class="btn btn-primary" autofocus>Yes, Allow</button>
                    <button name="button" value="no" class="btn btn-secondary">No, Do Not Allow</button>
                </div>
                <div class="col-sm-4 col-lg-auto">
                    @if (Model.ClientUrl != null)
                    {
                        <a class="btn btn-outline-info" href="@Model.ClientUrl">
                            <span class="glyphicon glyphicon-info-sign"></span>
                            <strong>@Model.ClientName</strong>
                        </a>
                    }
                </div>
            </div>
        </form>
    </div>
    

    Conroller Post 逻辑

            private async Task<ProcessConsentResult> ProcessConsent(ConsentInputModel model)
            {
                var result = new ProcessConsentResult();
    
                //  使用 IIdentityServerInteractionService  从returnUrl中得到请求上下文
                var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
                if (request == null) return result;
    
                ConsentResponse grantedConsent = null;
    
                //  当用户选择拒绝授权时 页面需要重定向到mvc客户端
                if (model?.Button == "no")
                {
                    grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied };
    
                    // 使用ids4 eventservice 发布拒绝事件
                    await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues));
                }
                // 当用户选择允许授权
                else if (model?.Button == "yes")
                {
                    // 授权客户端读取用户勾选的scope,即resourceowner 授予 第三方服务 访问 resource 的权限
                    if (model.ScopesConsented != null && model.ScopesConsented.Any())
                    {
                        var scopes = model.ScopesConsented;
                        if (ConsentOptions.EnableOfflineAccess == false)
                        {
                            scopes = scopes.Where(x => x != IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess);
                        }
    
                        grantedConsent = new ConsentResponse
                        {
                            RememberConsent = model.RememberConsent,
                            ScopesValuesConsented = scopes.ToArray(),
                            Description = model.Description
                        };
    
                        // emit event
                        await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent));
                    }
                    else
                    {
                        result.ValidationError = ConsentOptions.MustChooseOneErrorMessage;
                    }
                }
                else
                {
                    result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage;
                }
    
                if (grantedConsent != null)
                {
                    // communicate outcome of consent back to identityserver
                    await _interaction.GrantConsentAsync(request, grantedConsent);
    
                    // indicate that's it ok to redirect back to authorization endpoint
                    result.RedirectUri = model.ReturnUrl;
                    result.Client = request.Client;
                }
                else
                {
                    // we need to redisplay the consent UI
                    result.ViewModel = await BuildViewModelAsync(model.ReturnUrl, model);
                }
    
                return result;
            }
    
    // consent 确认表单提交
            [HttpPost]
            [ValidateAntiForgeryToken]
            public async Task<IActionResult> Index(ConsentInputModel model)
            {
                var result = await ProcessConsent(model);
    
                if (result.IsRedirect)
                {
                    var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);             
                   
                    return Redirect(result.RedirectUri);
                }
    
                if (result.HasValidationError)
                {
                    ModelState.AddModelError(string.Empty, result.ValidationError);
                }
    
                if (result.ShowView)
                {
                    return View("Index", result.ViewModel);
                }
    
                return View("Error");
            }
    
    

    3 测试

    • 打开localhost:5010 重定向到登录页,然后从localhost:5030 登录成功

    • 授权成功 重定向到 localhost:5030 首页

    问题

    • consent 同意之后的内部处理流程?

    • consent 拒绝之后客户端处理?

  • 相关阅读:
    C语言基础10
    swift笔记06
    C语言基础09
    C语言基础08
    C语言基础07
    C语言基础06
    swift笔记05
    Swift笔记4
    C语言基础05
    [转]一个清华计算机博士生的退学申请
  • 原文地址:https://www.cnblogs.com/aimigi/p/14005676.html
Copyright © 2011-2022 走看看