这是一个简单示例,用于演示运行在浏览器上的JavaScript客户端(也可称之为SPA)如何接入IdentityServer4。
用户首先登入IdentityServer,并获得IdentityServer颁发的access_token,然后在调用API的时候携带该 access_token,最后在退出IdentityServer登录。
创建一个简单JavaScript客户端应用
其实也就是先建立一个空项目,比如空的ASP.NET Core项目,或是 Node.js项目也行,这里是首先创建了一个空的ASP.NET Core项目
dotnet new web -n JavaScriptClient
修改托管项目的默认端口号
注意修改Properties/launchSettings.json 中的绑定地址,修改之前的 http://localhost:5003/为 http://0.0.0.0:5003, 也是之前在Docker容器运行出现绑定异常无法处理,才如此处理。
{
"profiles": {
"JavaScriptClient": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "http://0.0.0.0:5003",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
添加静态文件中间件
由于该项目主要是用于搭建一个跑在浏览器中的客户端应用,asp.net core 的主要作用就是托管静态资源,比如 HTML 和JS,静态文件中间件 也就是处理这个事情的。
在startup.cs 中注册中间件。
public void Configure(IApplicationBuilder app)
{
app.UseDefaultFiles();
app.UseStaticFiles();
}
静态文件中间件默认托管~/wwwroot目录下的静态文件,这个文件夹用来保存 HTML和JavaScript,如果没有该目录创建这个目录。
引用oidc-client
oidc-client是一个用于处理OpenID Connect (OIDC) 和 OAuth2 协议的软件开发包。可以通过npm、bower或是直接下载安装。
- 采用 npm 安装 oidc-client
npm i oidc-client
copy node_modules\oidc-client\dist* wwwroot
这个命令也就是下载最新的oidc-client引用集,然后拷贝 javascript文件到~/wwwroot目录下。
添加你的HTML和JavaScript文件
这里会有两个HTML文件和一个项目级的JavaScript脚本文件(另外还有oidc-client.js引用库),~/wwwroot目录中添加 index.html,callback.html还有就是app.js。
index.html
这是个简单的用户首页,包含三个按钮:登录,退出和调用API。
该文件引用了两个JavaScript脚本文件。包含一个PRE标签用于输出信息。
<!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="oidc-client.js"></script>
<script src="app.js"></script>
</body>
</html>
app.js
这个文件包含了应用的所有业务逻辑处理。首先是一个通用的信息输出方法Log, 调用该方法将信息输出到 PRE标签中。
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';
});
}
在app.js中添加三个按钮的注册文件。
document.getElementById("login").addEventListener("click", login, false);
document.getElementById("api").addEventListener("click", api, false);
document.getElementById("logout").addEventListener("click", logout, false);
下面就是采用oidc-client引用库中的UserManager来处理OpenID Connect协议,填写配置,初始化UserManager
var config = {
authority: "http://localhost:5000",
client_id: "js",
redirect_uri: "http://localhost:5003/callback.html",
response_type: "code",
scope:"openid profile api1",
post_logout_redirect_uri : "http://localhost:5003/index.html",
};
var mgr = new Oidc.UserManager(config);
接下来UserManager 提供了一个getUser的方法验证当前是否登录。返回的User 对象包含一个profile 属性,包含了所有的用户声明。
mgr.getUser().then(function (user) {
if (user) {
log("User logged in", user.profile);
}
else {
log("User not logged in");
}
});
接下来实现了 login、api、logout 三个方法。
UserManager 提供了signinRedirect 方法用于跳转到登录界面,signoutRedirect 退出登录。上面代码中获得的User 对象包含一个access_token ,可以用于访问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();
}
callback.html
这是页面是用来登录成功后跳转地址 redirect_uri ,通过这个页面完成了OpenID Connect协议的登录握手。一旦登录完成再次跳转回 index.html 页面。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<script src="oidc-client.js"></script>
<script>
new Oidc.UserManager({response_mode:"query"}).signinRedirectCallback().then(function() {
window.location = "index.html";
}).catch(function(e) {
console.error(e);
});
</script>
</body>
</html>
代码:https://github.com/daijinming/refine-javascriptclient
演示地址:http://114.116.96.150:5003