zoukankan      html  css  js  c++  java
  • IdentityServer4-前后端分离的授权验证(六)

           上两节介绍完Hybrid模式在MVC下的使用,包括验证从数据获取的User和Claim对MVC的身份授权。本节将介绍Implicit模式在JavaScript应用程序中的使用,使用Node.js+Express构建JavaScript客户端,实现前后端分离。本节授权服务和资源服务器基于第四和第五节。


    一、使用Node.js+Express搭建JavaScript客户端

          (1)首先需要Node.js环境

           下载并安装Node.js,官网下载地址:https://nodejs.org/en/ 

           输入指令:node –v  检测是否已安装Node.js,已安装会显示安装的Node.js版本

           

           (2)安装Express

           打开cmd,输入指令:npm install express-generator –g

           输入指令:express –h    已安装express会显示帮助文档

           

           (3)新建文件,创建JavaScript_Client应用程序

           新建文件夹(在D盘新建Express文件夹),cmd进入该文件夹。

           输入:express JavaScript_Client     在当前目录下创建一个名为JavaScript_Client的应用。目录结构如下:

           

           (4)安装依赖包

           输入:cd JavaScript_Client   进入JavaScript_Client目录

           输入:npm install   安装依赖包

           

           (5)启动并测试项目

           输入:npm start

           

           浏览器打开:http://localhost:3000 

           看到以下页面证明成功了。

           


    二、添加JavaScript客户端测试代码

          (1)安装oidc-client库

           输入:npm install oidc-client –save

           我们会发现在D:expressJavaScript_Client ode_modulesoidc-clientdist  有两个js文件

            

           我们只需使用这两个文件。把这两个文件复制到D:expressJavaScript_Clientpublic javascripts 目录下

          (2)添加测试用的HTML文件

           使用VSCode打开JavaScript_Client文件夹,在public(D:expressJavaScript_Clientpublic)下新建index.html文件。添加几个测试用的按钮。

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        <button id="login">Login</button>
        <button id="api">Call API</button>
        <button id="logout">Logout</button>
    
        <pre id="results"></pre>
    
        <script src=" javascripts/oidc-client.js"></script>
        <script src="app.js"></script>
    </body>
    </html> 

          (3)添加测试的js文件

           在public下新建app.js文件。

           黏贴以下代码

    /// <reference path=" javascripts/oidc-client.js" />
    
    function log() {
        document.getElementById('results').innerText = '';
    
        Array.prototype.forEach.call(arguments, function (msg) {
            if (msg instanceof Error) {
                msg = "Error: " + msg.message;
            }
            else if (typeof msg !== 'string') {
                msg = JSON.stringify(msg, null, 2);
            }
            document.getElementById('results').innerHTML += msg + '
    ';
        });
    }
    
    document.getElementById("login").addEventListener("click", login, false);
    document.getElementById("api").addEventListener("click", api, false);
    document.getElementById("logout").addEventListener("click", logout, false);
    
    var config = {
        authority: "http://localhost:5000",
        client_id: "js",
        redirect_uri: "http://localhost:5003/callback.html",
        response_type: "id_token token",
        scope:"openid profile api1",
        post_logout_redirect_uri : "http://localhost:5003/index.html",
    };
    var mgr = new Oidc.UserManager(config);
    
    mgr.getUser().then(function (user) {
        if (user) {
            log("User logged in", user.profile);
        }
        else {
            log("User not logged in");
        }
    });
    
    function login() {
        mgr.signinRedirect();
    }
    
    function api() {
        mgr.getUser().then(function (user) {
            var url = "http://localhost:5001/identity";
    
            var xhr = new XMLHttpRequest();
            xhr.open("GET", url);
            xhr.onload = function () {
                log(xhr.status, JSON.parse(xhr.responseText));
            }
            xhr.setRequestHeader("Authorization", "Bearer " + user.access_token);
            xhr.send();
        });
    }
    
    function logout() {
        mgr.signoutRedirect();
    }
    View Code

           以下对app.js代码进行分析

           App.js中log函数用来记录消息

    function log() {
        document.getElementById('results').innerText = '';
    
        Array.prototype.forEach.call(arguments, function (msg) {
            if (msg instanceof Error) {
                msg = "Error: " + msg.message;
            }
            else if (typeof msg !== 'string') {
                msg = JSON.stringify(msg, null, 2);
            }
            document.getElementById('results').innerHTML += msg + '
    ';
        });
    }

           使用oidc-client库中的UserManager类来管理OpenID连接协议。添加此代码以配置和实例化UserManager:

    var config = {
        authority: "http://localhost:5000",
        client_id: "js",
        redirect_uri: "http://localhost:5003/callback.html",
        response_type: "id_token token",
        scope:"openid profile api1",
        post_logout_redirect_uri : "http://localhost:5003/index.html",
    };
    var mgr = new Oidc.UserManager(config);

           接下来,UserManager提供一个getUser API来获取用户是否登录到JavaScript应用程序。返回的User对象有一个profile属性,其中包含用户的声明。添加此代码以检测用户是否登录到JavaScript应用程序:

    mgr.getUser().then(function (user) {
        if (user) {
            log("User logged in", user.profile);
        }
        else {
            log("User not logged in");
        }
    });

           接下来,我们要实现登录、api和注销功能。UserManager提供登录用户的signinRedirect和用户登出的signoutRedirect。我们在上述代码中获得的用户对象还有一个access_token属性,可以使用该属性对web API进行身份验证。access_token将通过Bearer模式传递给Web API。添加以下代码在我们的应用程序中实现这三个功能:

    function login() {
        mgr.signinRedirect();
    }
    
    function api() {
        mgr.getUser().then(function (user) {
            var url = "http://localhost:5001/identity";
    
            var xhr = new XMLHttpRequest();
            xhr.open("GET", url);
            xhr.onload = function () {
                log(xhr.status, JSON.parse(xhr.responseText));
            }
            xhr.setRequestHeader("Authorization", "Bearer " + user.access_token);
            xhr.send();
        });
    }
    
    function logout() {
        mgr.signoutRedirect();
    }

           (4)再新建一个callback.html。一旦用户登录到IdentityServer,这个HTML文件就是指定的redirect_uri页面。它将完成OpenID Connect协议与IdentityServer的登录握手。这里的代码都由我们前面使用的UserManager类提供。登录完成后,我们可以将用户重定向回index.html页面。添加此代码完成登录过程:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        <script src=" javascripts/oidc-client.js"></script>
        <script>
            new Oidc.UserManager().signinRedirectCallback().then(function () {
                window.location = "index.html";
            }).catch(function (e) {
                console.error(e);
            });
        </script>
    </body>
    </html>

           

          (8)修改服务端口为5003

           


    三、修改授权服务配置,资源服务器允许跨域调用API

          (1)修改授权服务配置

           在AuthServer项目,打开Config.cs文件,在GetClients中添加JavaScript客户端配置

                    // JavaScript Client
                    new Client
                    {
                        ClientId = "js",
                        ClientName = "JavaScript Client",
                        AllowedGrantTypes = GrantTypes.Implicit,
                        AllowAccessTokensViaBrowser = true,
    
                        RedirectUris = { "http://localhost:5003/callback.html" },
                        PostLogoutRedirectUris = { "http://localhost:5003/index.html" },
                        AllowedCorsOrigins = { "http://localhost:5003" },
    
                        AllowedScopes =
                        {
                            IdentityServerConstants.StandardScopes.OpenId,
                            IdentityServerConstants.StandardScopes.Profile,
                            "api1"
                        },
                    }

          (2)在资源服务配置允许跨域调用api

           在ResourceAPI项目,打开Startup.cs文件中的ConfigureServices方法,配置CORS,允许Ajax调用从http://localhost:5003调用http://localhost:5001的Web API。

     //JS-allow Ajax calls to be made from http://localhost:5003 to http://localhost:5001.
                services.AddCors(options =>
                {
                    //this defines a CORS policy called "default"
                    options.AddPolicy("default", policy =>
                    {
                        policy.WithOrigins("http://localhost:5003")
                            .AllowAnyHeader()
                            .AllowAnyMethod();
                    });
                });

           

           在Configure方法中将CORS中间件添加到管道中

     //JS-Add the CORS middleware to the pipeline in Configure:
    
                app.UseCors("default");

          (3)添加测试用的api接口

           添加IdentityController控制器

    [Route("[controller]")]
        public class IdentityController : ControllerBase
        {
            [Authorize(Roles ="admin")]
            [HttpGet]
            public IActionResult Get()
            {
                return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
            }
        }

          (4)测试

           运行AuthServer项目,运行ResourceAPI项目。

           在VSCode终端输入:npm start

           

           打开浏览器:http://localhost:5003/

           

           点击Login,使用账号:zhubingjian 密码:123  登录

           

           登录返回用户的Claims信息

            

            点击Call API,调用资源服务器的API接口

           

           成功获取接口返回的信息。


           通过这六节的内容,大概地介绍了IdentityServer4中Client的应用场景,包括MVC、前后端分离和服务端。

           此外还介绍了如何动态配置Client、如何验证从数据库中获取的User以及自定义Claims的方法。

           这个系列对IdentityServer4的介绍也是我博客的起点,写博客虽然很花时间,但是可以帮助我加深对知识点的理解。然而文中也体现到我对某些知识点的理解还是不到位的,望大家见谅。

           参考官网地址:https://identityserver4.readthedocs.io/en/release/quickstarts/7_javascript_client.html

           授权服务和资源服务源码地址:https://github.com/Bingjian-Zhu/Mvc-HybridFlow.git

           JavaScript客户端源码地址:https://github.com/Bingjian-Zhu/Identity-JavaScript_Client.git

  • 相关阅读:
    51 Nod 1068 Bash游戏v3
    51 Nod Bash 游戏v2
    51 Nod 1073 约瑟夫环
    UVA 12063 Zeros and ones 一道需要好好体会的好题
    51 Nod 1161 Partial sums
    2018中国大学生程序设计竞赛
    UVA 11971 Polygon
    UVA 10900 So do you want to be a 2^n-aire?
    UVA 11346 Possibility
    python with as 的用法
  • 原文地址:https://www.cnblogs.com/FireworksEasyCool/p/10197620.html
Copyright © 2011-2022 走看看