网站首页 > 技术文章 正文
在 Web 开发中,安全和身份管理至关重要。 在这种情况下,Keycloak 经常占据中心舞台。 尽管存在大量文档,但没有什么比实践经验更好的了。 本文介绍了我通过 Vanilla JavaScript 对 Keycloak 的探索。
注意:这是一项实验研究,而不是可用于生产的指南。
使用简单的 HTML5 页面和 id 为“root”的 div 设置 HTMLStart,以显示经过身份验证的用户信息。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>VANILLA JS AUTH</title>
</head>
<body>
<div id="root" />
<script src="./main.js"></script>
</body>
</html>
初始化
创建main.js并初始化baseUrl、realm和client_id等变量。
const baseUrl = "KEYCLOAK_URL";
const realm = "KEYCLOAK_REALM";
const client_id = "KEYCLOAK_CLIENT_ID";
const redirect_uri = `${location.protocol}//${location.host}/`;
随机状态和随机数生成
生成随机状态和随机数对于 OAuth 2.0 和 OpenID Connect 安全至关重要。 我们使用generateRandomState()来创建这些值。
const generateRandomState=()=> {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
重定向到Keycloak
未经身份验证的用户会使用redirectToKeycloak() 重定向到Keycloak。
const redirectToKeycloak = () => {
const state = generateRandomState();
const nonce = generateRandomState();
const urlParams = {
client_id,
redirect_uri,
response_mode: "fragment",
response_type: "code",
scope: "openid",
nonce,
state,
};
const conectionURI = new URL(`${baseUrl}/realms/${realm}/protocol/openid-connect/auth`);
for(const key of Object.keys(urlParams)){
conectionURI.searchParams.append(key, urlParams[key]);
}
window.location.href = conectionURI;
}
在此函数中,我们使用 baseurl 和领域构造一个新的 URL。 然后我们附加各种参数,如 client_id、redirect_uri、nonce 和 state。 response_type 参数设置为“code”,向 Keycloak 发出信号,表明我们正在请求授权代码。 稍后将使用此代码来获取令牌。 最后,我们将用户重定向到此 Keycloak URL。
token获取
身份验证成功后,Keycloak 会使用代码将您重定向回来。 使用 getToken(code) 获取令牌。
const getToken = async (code) => {
const tokenUri = new URL(`${baseUrl}/realms/${realm}/protocol/openid-connect/token`);
var body = new URLSearchParams();
body.append("client_id", client_id);
body.append("redirect_uri", redirect_uri);
body.append("grant_type", "authorization_code");
body.append("code", code);
const data = await fetch(tokenUri, {
method: 'post',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body,
});
const jsonData = await data.json();
sessionStorage.setItem("token", jsonData.access_token);
sessionStorage.setItem("id_token", jsonData.id_token);
sessionStorage.setItem("refresh_token", jsonData.refresh_token);
sessionStorage.setItem("session_state", jsonData.session_state);
history.replaceState({}, '', '/');
location.reload();
}
显示用户数据
从 sessionStorage 中获取令牌并显示用户详细信息。
const decodeJWT = (token) => {
const parts = token.split('.');
if (parts.length !== 3) throw new Error('Invalid JWT');
const decoded = atob(parts[1]);
const payload = JSON.parse(decoded);
return payload;
}
const generateUserProfile = () => {
const JWT = sessionStorage.getItem("token");
const payload = decodeJWT(JWT);
console.log(payload);
const div = document.createElement("div");
const userName = document.createElement("label");
userName.innerText = `--------${payload.given_name} ${payload.family_name} (${payload.email})--------`;
const logoutButton = document.createElement("button");
logoutButton.innerText = "Logout";
logoutButton.addEventListener("click", ()=>{
logout();
});
div.appendChild(userName);
div.appendChild(logoutButton);
return div;
}
注销
通过发送存储的 id_token 来启用用户注销。
const logout = () => {
const logoutURI = new URL(`${baseUrl}/realms/${realm}/protocol/openid-connect/logout`);
const id_token_hint = sessionStorage.getItem("id_token");
const urlParams = {
client_id,
post_logout_redirect_uri: redirect_uri,
id_token_hint,
};
for(const key of Object.keys(urlParams)){
logoutURI.searchParams.append(key, urlParams[key]);
}
sessionStorage.clear();
window.location.href = logoutURI;
}
事件处理
最后,我们处理窗口加载和不同的场景,例如拥有令牌或需要生成令牌。
window.addEventListener("load", ()=>{
const token = sessionStorage.getItem("token");
if(token!==null){
const __rootElement = document.querySelector("#root");
const __content = generateUserProfile();
__rootElement.appendChild(__content);
return;
}
const params = new URLSearchParams(window.location.hash.split("#")[1]);
const code = params.get("code");
if(code){
getToken(code);
return;
}
redirectToKeycloak();
});
最后的想法
这个 Vanilla JS-Keycloak 实验提供了对身份验证流程的基本了解,为更高级的应用程序奠定了基础。 请注意,在现实场景中,验证“状态”和“随机数”等额外的安全措施至关重要。 本文可为那些热衷于应用程序安全和身份管理的人提供实用指南。
猜你喜欢
- 2024-11-22 美国战略司令部发神秘字符后删除 网友懵:核武器密码?
- 2024-11-22 微信群聊名称可以备注了:仅个人可见
- 2024-11-22 LPL:新王诞生!TES3-2复仇JDG夺得LPL夏季赛冠军
- 2024-11-22 【提醒】刚刚,微信发布公告,不能在朋友圈发这些东西!
- 2024-11-22 JS 函数式概念: 管道 和 组合
- 2024-11-22 「夜读」你的人品,自己会说话
- 2024-11-22 鼻炎反复发作?不妨试试这8招
- 2024-11-22 什么是 JavaScript?
- 2024-11-22 楼梯踏步设计尺寸指引HJSJ-2022
- 2024-11-22 楼梯标准规范尺寸HJSJ-2021
- 标签列表
-
- content-disposition (47)
- nth-child (56)
- math.pow (44)
- 原型和原型链 (63)
- canvas mdn (36)
- css @media (49)
- promise mdn (39)
- readasdataurl (52)
- if-modified-since (49)
- css ::after (50)
- border-image-slice (40)
- flex mdn (37)
- .join (41)
- function.apply (60)
- input type number (64)
- weakmap (62)
- js arguments (45)
- js delete方法 (61)
- blob type (44)
- math.max.apply (51)
- js (44)
- firefox 3 (47)
- cssbox-sizing (52)
- js删除 (49)
- js for continue (56)
- 最新留言
-