按照OAuth2.0的4種授權方式,接下來應該介紹隱藏式(implicit),與之對應的OpenId Connect Flow的Implicit Flow,但是IdentityServer4官方最新文檔沒有明言,只是給了Adding a JavaScript client的章節,而且根據內部代碼實現,還是采用的授權碼,並沒有使用Implicit Flow保護SPA,雖然不知道原因,但是我們還是按照官方最新文檔的來介紹,在之前的文檔,一個版本號為relase的文檔,有Implicit Flow的介紹,感興趣的童鞋,可以讀一下,最新的文檔編號是latest,從應用的實際代碼比較,差別並不大,唯一的差別可能就是原理,但是不去抓包查看相關報文,並無法感覺。
1.創建客戶端
這里我們按照官方教程來,使用ASP.NET Core空項目,用內置服務器來承載客戶端靜態文件。
1.1 創建項目
md JavaScript
cd JavaScript
dotnet new web
dotnet sln add .\JavaScript\JavaScript.csproj
1.2 修改launchSettings.json
{
"profiles": {
"JavaScript": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "http://localhost:6003",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
1.3 添加 ‘靜態文件中間件’
該項目是為客戶端運行而設計的,我們只需要ASP.NET Core提供構成我們的應用程序的靜態HTML和JavaScript文件,靜態文件中間件就是為此設計的。
注冊靜態文件中間件,同時刪除其他代碼。
Startup.Configure
public void Configure(IApplicationBuilder app)
{
app.UseDefaultFiles();
app.UseStaticFiles();
}
這個中間件現在將提供應用程序的~/wwwroot文件夾中的靜態文件。這是我們將放置HTML和JavaScript文件的地方。空項目中不存這個目錄,所以需要創建這個目錄。
1.4 oidc-client library下載
在上篇,我們使用了一個庫去處理OpenID Connect 協議,在JavaScript中,我們同樣需要類似的庫,只不過現在需要這個庫能夠在JavaScript中使用且瀏覽器運行(因為node.js是服務端),https://github.com/IdentityModel/oidc-client-js
我們用npm下載
npm i oidc-client
copy .\node_modules\oidc-client\dist\* .\wwwroot\
1.5 添加html和js文件
兩個html文件和一個除上面的oidc-client之外的js文件組成我們JavaScript應用(SPA)
- index.html
- callback.html
- app.js
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="oidc-client.js"></script>
<script src="app.js"></script>
</body>
</html>
3個登錄按鈕,引入兩個js文件
app.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';
});
}
//register click event handlers to the three buttons
document.getElementById("login").addEventListener("click", login, false);
document.getElementById("api").addEventListener("click", api, false);
document.getElementById("logout").addEventListener("click", logout, false);
//UserManager from the oidc-client to manage the OpenID Connect protocol
var config = {
authority: "http://localhost:5001",
client_id: "js",
redirect_uri: "http://localhost:6003/callback.html",
response_type: "code",
scope: "openid profile api1",
post_logout_redirect_uri: "http://localhost:6003/index.html",
};
var mgr = new Oidc.UserManager(config);
/*
* UserManager provides a getUser API to know if the user is
* logged into the JavaScript application.
* It uses a JavaScript Promise to return the results asynchronously.
* The returned User object has a profile property which contains
* the claims for the user.
* Add this code to detect if the user is logged into
* the JavaScript application:
*/
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:6001/api/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();
}
- 對3個按鈕進行監聽,並觸發不同的事件:addEventListener
- 登錄
- 退出
- 調用api
callback.html
這個HTML文件是用戶登錄到IdentityServer后指定的redirect_uri頁面,它將與IdentityServer完成OpenID Connect協議的登錄握手。此代碼全部由我們在app.js中使用的UserManager類提供。登錄完成后,我們可以將用戶重定向回主頁面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>
2.在IdentityServer注冊客戶端
客戶端應用已經准備好,像其他的客戶端一樣,需要IdentityServer中添加客戶端
// JavaScript Client
new Client
{
ClientId = "js",
ClientName = "JavaScript Client",
// 注意看這里,GrantTypes還是選擇的Code
// GrantTypes.Implicit,
AllowedGrantTypes = GrantTypes.Code,
RequireClientSecret = false,
RedirectUris = { "http://localhost:6003/callback.html" },
PostLogoutRedirectUris = { "http://localhost:6003/index.html" },
AllowedCorsOrigins = { "http://localhost:6003" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
}
}
3.允許ajax跨域調用webapi
這個就需要在webapi項目中增加跨域配置
Startup.ConfigureServices
services.AddCors(options =>
{
// this defines a CORS policy called "default"
options.AddPolicy("default", policy =>
{
policy.WithOrigins("http://localhost:6003")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
添加CORS中間件
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseCors("default");
// ...
}
更多跨域配置,參考官方文檔
4.測試
運行IdentityServer
cd .\IdentityServer\
dotnet run
運行webapi
cd .\webapi\
dotnet run
VS運行SPA
啟動內置服務器,搭載靜態文件
登錄成功
調用api
注銷登錄
參考鏈接
http://docs.identityserver.io/en/latest/quickstarts/4_javascript_client.html
作者:Garfield
同步更新至個人博客:http://www.randyfield.cn/
本文版權歸作者所有,未經許可禁止轉載,否則保留追究法律責任的權利,若有需要請聯系287572291@qq.com