上两节介绍完 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:\express\JavaScript_Client\node_modules\oidc-client\dist 有两个 JS 文件
我们只需使用这两个文件. 把这两个文件复制到 D:\express\JavaScript_Client\public\ javascripts 目录下
(2) 添加测试用的 html 文件
使用 VSCode 打开 JavaScript_Client 文件夹, 在 public(D:\express\JavaScript_Client\public) 下新建 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 + '\r\n';
- });
- }
- 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 + '\r\n';
- });
- }
使用 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() {
- Windows.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://github.com/Bingjian-Zhu/Mvc-HybridFlow.git https://github.com/Bingjian-Zhu/Mvc-HybridFlow
JavaScript 客户端源码地址:
来源: https://www.cnblogs.com/FireworksEasyCool/p/10197620.html