Nodejs 第六十四章(SSO单点登录)

news/2024/5/19 21:47:45/

单点登录

单点登录(Single Sign-On,简称SSO)是一种身份认证和访问控制的机制,允许用户使用一组凭据(如用户名和密码)登录到多个应用程序或系统,而无需为每个应用程序单独提供凭据

SSO的主要优点包括:

  1. 用户友好性:用户只需登录一次,即可访问多个应用程序,提供了更好的用户体验和便利性。
  2. 提高安全性:通过集中的身份验证,可以减少密码泄露和密码管理问题。此外,SSO还可以与其他身份验证机制(如多因素身份验证)结合使用,提供更强的安全性。
  3. 简化管理:SSO可以减少管理员的工作量,因为他们不需要为每个应用程序单独管理用户凭据和权限。

举例说明

小满科技,小满教育,都是小满旗下的公司,那么我需要给每套系统做一套登录注册,人员管理吗,那太费劲了,于是使用SSO单点登录,只需要在任意一个应用登录过,其他应用便是免登录的一个效果,如果过期了,在重新登录

但是每个应用是不同的,登录用的是一套,这时候可以模仿一下微信小程序的生成一个AppId作为应用ID,并且还可以创建一个secret,因为每个应用的权限可以不一样,所以最后生成的token也不一样,还需要一个url,登录之后重定向到该应用的地址,正规做法需要有一个后台管理系统用来控制这些,注册应用,删除应用,这里节约时间就写死了。

image.png

代码编写

  1. 安装的依赖
  • express 启动服务编写接口
  • express-session 操作cookie
  • jsonwebtoken 生成token
  • cors 跨域
  1. 目录结构
  • vue A项目 用vite创建一个就好 npm init vite
  • react B项目 用vite创建一个就好 npm init vite
  • server/index.js nodejs端
  • sso.html 登录页面

server/index.js

const appToMapUrl = {//A应用id'Rs6s2aHi': {url: "http://localhost:5173", //对应的应用地址secretKey: '%Y&*VGHJKLsjkas', //对应的secretKeytoken:"" //token},//B应用id'9LQ8Y3mB': {url: "http://localhost:5174", //对应的应用地址secretKey: '%Y&*FRTYGUHJIOKL', //对应的secretKeytoken:"" //token},
}

完整版代码

server/index.js

import express from 'express'
import session from 'express-session'
import fs from 'node:fs'
import cors from 'cors'
import jwt from 'jsonwebtoken'const appToMapUrl = {'Rs6s2aHi': {url: "http://localhost:5173",name:'vue',secretKey: '%Y&*VGHJKLsjkas',token: ""},'9LQ8Y3mB': {url: "http://localhost:5174",secretKey: '%Y&*FRTYGUHJIOKL',name:'react',token: ""},
}
const app = express()
app.use(cors())
app.use(express.json())
app.use(session({secret: "$%^&*()_+DFGHJKL",cookie: {maxAge: 1000 * 60 * 60 * 24 * 7, //过期时间}
}))
const genToken = (appId) => {return jwt.sign({ appId }, appToMapUrl[appId].secretKey)
}
app.get('/login', (req, res) => {//注意看逻辑 如果登陆过 就走if 没有登录过就走下面的if (req.session.username) {//登录过const appId = req.query.appIdconst url = appToMapUrl[appId].urllet token;//登录过如果存过token就直接取 没有存过就生成一个 因为可能有多个引用A登录过读取Token   B没有登录过生成Token 存入映射表if (appToMapUrl[appId].token) {token = appToMapUrl[appId].token} else {token = genToken(appId)appToMapUrl[appId].token = token}res.redirect(url + '?token=' + token)return}//没有登录 返回一个登录页面htmlconst html = fs.readFileSync(`../sso.html`, 'utf-8')//返回登录页面res.send(html)
})
//提供protectd get接口 重定向到目标地址
app.get('/protectd', (req, res) => {const { appId,username,password } = req.query //获取应用标识const url = appToMapUrl[appId].url //读取要跳转的地址const token = genToken(appId) //生成tokenreq.session.username = username //存储用户名称 表示这个账号已经登录过了 下次无需登录appToMapUrl[appId].token = token //根据应用存入对应的tokenres.redirect(url + '?token=' + token) //定向到目标页面
})
//启动3000端口
app.listen(3000, () => {console.log('http://localhost:3000')
})

sso.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body>
<!--这里会调用protectd接口 并且会传入 账号 密码 和 appId appId会从地址栏读取--><form action="/protectd" method="get"><label for="username">账号:<input name="username" id="username" type="text"></label><label for="password">密码:<input name="password" id="password" type="password"></label><label for="appId"><input name="appId" value="" id="appId" type="hidden"></label><button type="submit" id="button">登录</button></form><script>//读取AppIdconst appId = location.search.split('=')[1]document.getElementById('appId').value = appId</script>
</body></html>

A 应用这里用Vue展示 App.vue

<template><h1>vue3</h1>
</template><script setup lang='ts'>
//如果有token代表登录过了 如果没有跳转到 登录页面也就是SSO 那个页面,并且地址栏携带AppID
const token = location.search.split('=')[1]
if (!token) {fetch('http://localhost:3000/login?appId=Rs6s2aHi').then(res => {location.href = res.url})
}
</script><style></style>

B应用使用React演示 App.tsx

import { useState } from 'react'
function App() {const [count, setCount] = useState(0)//逻辑其实一样的只是区分了不用应用的AppIdconst token = location.search.split('=')[1]if (!token) {fetch('http://localhost:3000/login?appId=9LQ8Y3mB').then(res => {location.href = res.url})}return (<><h1>react</h1></>)
}
export default App

http://www.ppmy.cn/news/1423395.html

相关文章

算法训练营第25天回溯(分割)

回溯算法&#xff08;分割&#xff09; 131.分割回文串 力扣题目链接(opens new window) 题目 给定一个字符串 s&#xff0c;将 s 分割成一些子串&#xff0c;使每个子串都是回文串。 返回 s 所有可能的分割方案。 示例: 输入: “aab” 输出: [ [“aa”,“b”], [“a”,“…

免费的壁纸api

# 联想壁纸 from enum import Enumimport requestsclass LenovoTopHeadersTypePage(Enum):"""头部页面类型wallpaper 精选wallpaperHot 热门wallpaperRank 排行"""wallpaper wallpaperwallpaperHot wallpaperHotwallpaperRank wallpaperRankc…

【NEUQ1007】C基础-计负均正

C基础-计负均正 描述 从键盘输入任意20个整型数&#xff0c;统计其中的负数个数并求所有正数的平均值。 输入描述 从键盘输入20个整型数&#xff0c;以空格分隔。 输出描述 输出负数的个数和所有正数的平均值&#xff0c;保留两位小数。 示例 输入 1 2 3 4 5 6 7 8 9 …

【python从入门到精通】-- 第五战:函数大总结

&#x1f308; 个人主页&#xff1a;白子寰 &#x1f525; 分类专栏&#xff1a;python从入门到精通&#xff0c;魔法指针&#xff0c;进阶C&#xff0c;C语言&#xff0c;C语言题集&#xff0c;C语言实现游戏&#x1f448; 希望得到您的订阅和支持~ &#x1f4a1; 坚持创作博文…

《八》QSplitter拆分器以及QDockWidget窗口详解

QSplitter简介 QSplitter拆分器允许用户通过拖动子部件之间的边界来控制它们的大小。 单个拆分器可以控制任意数量的小部件。QSplitter的典型用法是创建几个小部件&#xff0c;并使用insertWidget()或addWidget()添加它们。 常用方法 默认情况下&#xff0c;QSplitter会动态…

Java学习笔记零基础入门1

目录 第一章 Java概述 1.1 什么是程序 1.2 Java 技术体系平台 1.3 Java 重要特点 1.4 Java 的开发工具 4.1 工具选择 1.5 Java 运行机制及运行过程 5.1 Java 语言的特点&#xff1a;跨平台性 5.2 Java 核心机制-Java 虚拟机 [JVMjavavirtual machine] 1.6 什么是JDK&…

Spring Boot + 事务钩子函数,打造高效支付系统!

今天&#xff0c;我继续安利一个独门绝技&#xff1a;Spring 事务的钩子函数。 单纯的讲技术可能比较枯燥乏味。 接下来&#xff0c;我将以一个实际的案例来描述Spring事务钩子函数的正确使用姿势。 一、案例背景 拿支付系统相关的业务来举例。在支付系统中&#xff0c;我们…

【WebSocket连接异常】前端使用WebSocket子协议传递token时,Java后端的正确打开方式!!!

文章目录 1. 背景2. 代码实现和异常发现3. 解决异常3.1 从 URL入手3.2 从 WebSocket子协议的使用方式入手&#xff08;真正原因&#xff09; 4. 总结&#xff08;仍然存在的问题&#xff09; 前言&#xff1a; 本篇文章记录的是使用WebSocket进行双向通信时踩过的坑&#xff0c…

flex吃干抹净

Flex 布局是什么&#xff1f; Flex 是 Flexible Box 的缩写&#xff0c;意为"弹性布局"&#xff0c;用来为盒状模型提供最大的灵活性。 .box{display: flex;//行内元素也可以使用flex布局//display: inline-flex; }display: flex; 使元素呈现为块级元素&#xff0c;…

蓝桥杯---数组分割

https://www.dotcpp.com/oj/problem3171.html 测试用例分析&#xff1a; 2 2 6 6 2 1 6 这代表有两个测试用例。 第一测试用例: 数组: [6, 6]长度: 2 分析 数组中的所有元素都是偶数&#xff0c;因此任意组合的和都将是偶数。可能的组合及其和&#xff1a; 空集: 和 0 …

数据结构书后习题

p17 1&#xff0c; 个人解答&#xff1a; int DeleteMinElem(SqList &L,int &min) {int j 0;if (L.length 0){printf("error!");return 0;}int min L.data[0];for (int i 1; i < L.length; i){if (L.data[i] < min){min L.data[i];j i;}}L.dat…

图像分割:Pytorch实现UNet++进行医学细胞分割

图像分割&#xff1a;Pytorch实现UNet进行医学细胞分割 前言相关介绍项目结构具体步骤准备数据集读取数据集设置并解析相关参数定义网络模型定义损失函数定义优化器训练验证 参考 前言 由于本人水平有限&#xff0c;难免出现错漏&#xff0c;敬请批评改正。更多精彩内容&#x…

MySQL高负载排查方法最佳实践(15/16)

高负载排查方法 CPU占用率过高问题排查 使用mpstat查看cpu使用情况。 # mpstat 是一款 CPU 性能指标实时展示工具 # 能展示每个 CPU 核的资源视情况&#xff0c;同时还能将资源使用情况进行汇总展示 # 如果CPU0 的 %idle 已经为 0 &#xff0c;说明此核已经非常繁忙# 打印所…

网络基础-基于TCP协议的Socket通讯

一、Socket通讯基于TCP协议流程图 UDP 的 Socket 编程相对简单些不在介绍。 二、 服务端程序启动 服务端程序要先跑起来&#xff0c;然后等待客户端的连接和数据。 服务端程序首先调用 socket() 函数&#xff0c;创建网络协议为 IPv4&#xff0c;以及传输协议为 TCP 的…

将Ubuntu18.04默认的python3.6升级到python3.8

1、查看现有的 python3 版本 python3 --version 2、安装 python3.8 sudo apt install python3.8 3、将 python3.6 和 3.8 添加到 update-alternatives sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.6 1 sudo update-alternatives --insta…

AIDE:自动驾驶目标检测的自动数据引擎

AIDE&#xff1a;自动驾驶目标检测的自动数据引擎 摘要IntroductionRelated WorksMethodData FeederModel Updater4 Experiments 摘要 自动驾驶车辆&#xff08;AV&#xff09;系统依赖于健壮的感知模型作为安全保证的基石。然而&#xff0c;道路上遇到的物体表现出长尾分布&a…

2024-4-18 群讨论:关于异步HttpClient如何测试验证

以下来自本人拉的一个关于 Java 技术的讨论群。关注公众号&#xff1a;hashcon&#xff0c;私信进群拉你 群友问题&#xff1a;群友想尽量快的将请求发到三方接口&#xff0c;不考虑三方接口的压力。如何开发并验证&#xff1f; 思路&#xff1a; 肯定要使用 WebClient 这种…

未来计算机的发展趋势是什么?

未来计算机的发展趋势是多方面的,涵盖了硬件、软件、体系结构以及计算范式等多个层面。以下是一些预期的趋势: 1. 量子计算: 随着量子理论的不断成熟和技术的进步,量子计算机将可能解决传统计算机难以处理的问题,比如药物发现、材料科学、复杂系统模拟等领域。量子计算的…

gpt-6有望成为通用工具

OpenAI CEO山姆奥特曼&#xff08;Sam Altman&#xff09;在最新的博客访谈中&#xff0c;提到gpt-6有望成为通用工具。 奥特曼还认为&#xff0c;目前的模型不够聪明&#xff0c;“使用GPT-2进行科学研究曾被认为是不切实际的想法。而如今&#xff0c;虽然人们使用GPT-4进行科…

程序员购车指南

哈喽大家好&#xff0c;我是咸鱼。 爱车可以说是大部分男人的天性&#xff0c;而我对汽车的热情却远不及对手表的钟爱&#xff08;痴迷劳力士&#xff09;。以至于我的朋友掏出车钥匙指着上面的苹果树标志跟我介绍奔驰 AMG 系列的强劲性能和马力时&#xff0c;我只能尽量假装自…