上两节介绍完Hybrid模式在MVC下的使用,包括验证从数据获取的User和Claim对MVC的身份受权。本节将介绍Implicit模式在JavaScript应用程序中的使用,使用Node.js+Express构建JavaScript客户端,实现先后端分离。本节受权服务和资源服务器基于第四和第五节。javascript
(1)首先须要Node.js环境html
下载并安装Node.js,官网下载地址:https://nodejs.org/en/ java
输入指令:node –v 检测是否已安装Node.js,已安装会显示安装的Node.js版本node
(2)安装Expressgit
打开cmd,输入指令:npm install express-generator –ggithub
输入指令:express –h 已安装express会显示帮助文档web
(3)新建文件,建立JavaScript_Client应用程序数据库
新建文件夹(在D盘新建Express文件夹),cmd进入该文件夹。express
输入:express JavaScript_Client 在当前目录下建立一个名为JavaScript_Client的应用。目录结构以下:npm
(4)安装依赖包
输入:cd JavaScript_Client 进入JavaScript_Client目录
输入:npm install 安装依赖包
(5)启动并测试项目
输入:npm start
浏览器打开:http://localhost:3000
看到如下页面证实成功了。
(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(); }
如下对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 () { window.location = "index.html"; }).catch(function (e) { console.error(e); }); </script> </body> </html>
(8)修改服务端口为5003
(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