Node.js单点登录SSO详解:Session、JWT、CORS让登录更简单

文章目录

      • 一、SSO介绍
        • 1、使用SSO的好处
      • 二、中间件介绍
        • 1、Express
          • 安装
          • 导入
          • 使用
        • 2、cors
          • 安装
          • 导入
          • 配置
        • 3、express-session
          • 安装
          • 导入
          • 配置
          • 使用
        • 4、jsonwebtoken
          • 安装
          • 导入
          • 使用
        • 5、jwt和session对比
      • 三、SSO实现方案
        • 1、安装依赖
        • 2、结构
        • 3、实现原理
      • 三、示例代码
        • 1、nodejs端 server/index.js
        • 2、vueA项目app.vue
        • 3、vueB项目app.vue
        • 4、登录页面login.html

一、SSO介绍

单点登录(Single Sign On),简称为 SSO,是比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

在这里插入图片描述

如图所示,图中有4个系统,分别是Application1、Application2、Application3、和SSO。Application1、Application2、Application3没有登录模块,而SSO只有登录模块,没有其他的业务模块,当Application1、Application2、Application3需要登录时,将跳到SSO系统,SSO系统完成登录,其他的应用系统也就随之登录了。这完全符合我们对单点登录(SSO)的定义。

1、使用SSO的好处
  • 方便用户 用户使用应用系统时,能够一次登录,多次使用。用户不再需要每次输入用户名称和用户密码,也不需要牢记多套用户名称和用户密码。单点登录平台能够改善用户使用应用系统的体验。
  • 方便管理员 系统管理员只需要维护一套统一的用户账号,方便、简单。相比之下,系统管理员以前需要管理很多套的用户账号。每一个应用系统就有一套用户账号,不仅给管理上带来不方便,而且,也容易出现管理漏洞。
  • 简化应用系统开发 开发新的应用系统时,可以直接使用单点登录平台的用户认证服务,简化开发流程。单点登录平台通过提供统一的认证平台,实现单点登录。因此,应用系统并不需要开发用户认证程序。

在这里插入图片描述

二、中间件介绍

1、Express

Express 是一个保持最小规模的灵活的 Node.js Web 应用程序开发框架,为 Web 和移动应用程序提供一组强大的功能。

安装
npm install express
导入
const express = require('express')
使用
const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.post('/', (req, res) => {
  res.send('Got a POST request')
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})
2、cors

cors 是 Express 的一个第三方中间件。通过安装和配置 cors 中间件,可以很方便地解决跨域问题。

CORS (Cross-Origin Resource Sharing,跨域资源共享)由一系列 HTTP 响应头组成,这些 HTTP 响应头决定浏览器是否阻止前端 JS 代码跨域获取资源。

安装
npm install cors
导入
const cors = require('cors')
配置
  • 启用所有 CORS 请求
app.use(cors())
  • 指定URL配置
app.use(cors({ origin: 'http://127.0.0.1:5500' }))
  • 为单个路由启用 CORS
app.get('/data', cors(), (req, res) => {
  res.json({
    name: 'cors in node.js',
    language: 'JavaScript',
    server: 'Express.js',
  })
})
  • 使用选项配置 CORS
const options = {
  origin: 'http://127.0.0.1:5500',
  methods: 'GET, PUT',
}
app.use(cors(options))
  • 使用函数配置动态 CORS 源
const options = {
  origin: dynamicConfiguration(),
  methods: 'GET, PUT',
}
 
const dynamicConfiguration = async (req) => {
  const db = getDB() // simulating database object
  let origin = await db.getOrigin(req.headers) //simulating fetching origin from DB based on the headers
  return origin
}
 
app.use(cors(options))
3、express-session

express-session中间件将会话数据存储在服务器上;它仅将会话标识(而非会话数据)保存在 cookie 中。从1.5.0版本开始, express-session不再依赖cookie-parser,直接通过req/res读取/写入;默认存储位置内存存储(服务器端)

安装
npm install express-session
导入
const session = require('express-session')
配置
app.use(session({
    secret: 'YOUR_SESSION_SECRET',//加密字符串。 使用该字符串来加密session数据,自定义
    resave: false,//强制保存session即使它并没有变化
    saveUninitialized: true,//强制将未初始化的session存储。当新建了一个session且未设定属性或值时,它就处于未初始化状态。
    cookie: {
        maxAge: 30 * 60 * 1000
    }
}))
使用
//设置
req.session.userName = userName;
//获取
const userName = req.session.userName
4、jsonwebtoken

JSON Web Token(JWT)是一种用于在web上传递信息的标准,它以JSON格式表示信息,通常用于身份验证和授权。

JWT由三个部分组成:Header(头部)、Payload(负载)和Signature(签名)。它们用点号分隔开,形成了一个JWT令牌。

在现代web应用中,用户身份认证是非常重要且必不可少的一环。而使用Node.js和Express框架,可以方便地实现用户身份认证。而在这个过程中,jsonwebtoken这个基于JWT协议的模块可以帮助我们实现安全且可靠的身份认证机制,可以让我们轻松地生成、解析和验证JWT。

安装
npm install jsonwebtoken
导入
const jwt = require('jsonwebtoken')
使用
// 生成token
const token = jwt.sign({
    id: 'appId',
    name: 'zhangsan',
    secret: 'YOUR_SECRET_KEY'
}, '123456', {
    expiresIn: '2h'
})
    
//验证token
jwt.verify(token, 'shhhhh', (err, decoded) => {
  if (err) {
    console.error('无效的令牌');
  } else {
    // 使用解码后的令牌数据
    console.log(decoded);
  }
});
5、jwt和session对比
对比因素JWTSession
存储存储在客户端,不需要服务器保持会话状态。存储在服务器,需要服务器维护会话信息。
安全性加密较严密,但如果token被窃取,攻击者可以任意使用。如果sessionID被窃取,攻击者可以冒充用户登陆。
性能在每次请求时需要验证和解码token,性能较差。只需查找sessionID就能获取会话信息,性能较好。
扩展性在多服务器或者跨域环境中更易扩展。在多服务器环境中需要同步session,扩展性较差。
数据大小JWT的大小比sessionID大,因此需要更多的带宽。sessionID大小稳定,对带宽需求较小。
到期时间可以为每个token设置不同的过期时间。所有session的过期时间通常相同。
客户端存储位置可以存储在Cookie, LocalStorage, SessionStorage中存储在Cookie中。
跨域问题无跨域问题,且对于移动应用而言友好。跨域问题复杂,需要服务器支持CORS。
状态无状态,服务器不需要保存用户信息。有状态,服务器需要保存用户信息。
使用场景用于认证和信息交换,尤其适合单页应用(SPA)和前后端分离的项目。主要用于记录用户状态,适配传统的后端渲染的Web服务。

三、SSO实现方案

1、安装依赖

启动服务:express
操作cookie:express-session
生成token:jsonwebtoken
解决跨域:cors

npm install express
npm install express-session
npm install jsonwebtoken
npm install cors
2、结构

vueA项目:使用vite创建项目
vueB项目:使用vite创建项目
nodejs端:server/index.js
登录页面:login.html

3、实现原理
  • 用户首次访问系统A或B时,需要进行登录。
  • 系统统A或B带着appId信息重定向登录页面。
  • 认证系统验证用户登录信息。
  • 验证通过后,设置session值,用户返回一个token。
  • 认证系统带着token重定向给系统A或B,得知用户是已登录状态。
  • 系统A或B正常进入系统。
  • 用户再访问另一个系统时。
  • 通过session值,得知用户是已登录状态。
  • 认证系统带着token重定向给系统。
  • 系统正常进入系统。

在这里插入图片描述

三、示例代码

1、nodejs端 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 = {
    'fd8xIoDC': {
        url: 'http://localhost:5173',
        name: 'appA',
        secret: '123456',
        token: ''
    },
    'DDkq0YYh': {
        url: 'http://localhost:5174',
        name: 'appB',
        secret: '789102',
        token: ''
    }
}


// 创建服务器
const app = express()


// 解析post请求体
app.use(express.json())

// 跨域
app.use(cors())

// 创建session配置项,注册为express-session中间件
app.use(session({
    secret: '123456',//加密字符串。 使用该字符串来加密session数据,自定义
    resave: false,//强制保存session即使它并没有变化
    saveUninitialized: true,//强制将未初始化的session存储。当新建了一个session且未设定属性或值时,它就处于未初始化状态。
    cookie: {
        maxAge: 30 * 60 * 1000
    }
}))


//获取token
const getToken = (appId) => {
    const appInfo = appToMapUrl[appId]
    if (!appInfo) {
        return null;
    }
    // 生成token
    const token = jwt.sign({
        id: appId,
        name: appInfo.name,
        secret: appInfo.secret
    }, '123456', {
        expiresIn: 60 * 60
    })
    return token;
}


//是否登录
app.get('/login', (req, res) => {
    const {appId} = req.query
    if (!appId) {
        return res.send('请输入appId')
    }
    // 判断是否登录
    if (req.session.userName) {
        let token
        if (appToMapUrl[appId].token) {
            // 获取token
            token = appToMapUrl[appId].token
        } else {
            // 生成token
            token = getToken(appId)
            // 存入appToMapUrl
            appToMapUrl[appId].token = token
        }
        // 跳转
        res.redirect(`${appToMapUrl[appId].url}?token=${token}`)
        return;
    } else {
        // 读取登录页面
        const html = fs.readFileSync('./login.html', 'utf-8')
        res.send(html)
    }
})

// 解析表单数据
app.use(express.urlencoded({ extended: true }));

// 登录
app.post('/protected', (req, res) => {
    const {username,password,appId} = req.body
    if (username === 'admin' && password === '123456') {
        const token = getToken(appId);
        // 存入appToMapUrl
        appToMapUrl[appId].token = token;
        //存入session,证明已经登录
        req.session.userName = username;
        res.redirect(`${appToMapUrl[appId].url}?token=${token}`)
    } else {
        res.send('用户名或密码错误')
    }
})


// 监听端口
app.listen(3000, () => {
    console.log('http://localhost:3000')
})
2、vueA项目app.vue
<script setup lang="ts">
const token = location.search.split('token=')[1]
if (!token) {
  fetch('http://localhost:3000/login?appId=fd8xIoDC').then(res => {
    location.href = res.url
  })
} else {
  localStorage.setItem('token', token)
}
</script>

<template>
  <div>这里是appA</div>

</template>

<style scoped>
</style>
3、vueB项目app.vue
<script setup>
const token = location.search.split('token=')[1]
if (!token) {
  fetch('http://localhost:3000/login?appId=DDkq0YYh').then(res => {
    location.href = res.url
  })
} else {
  localStorage.setItem('token', token)
}
</script>

<template>
  <div>这里是appB</div>
</template>

<style scoped>
</style>
4、登录页面login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登录</title>
</head>
<body>
    <div>
        <h1>登录页面</h1>
        <form action="/protected" method="post">
            <input type="text" name="username">
            <input type="password" name="password">
            <input type="hidden" name="appId">
            <input type="submit" value="登录">
        </form>
    </div>
    <script>
        const appId = location.search.split('?')[1].split('=')[1]
        document.querySelector('input[name="appId"]').value = appId
    </script>
</body>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/728440.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

求最小生成树的新算法

不管 prim 算法还是 kruskal 算法都基于 “当前可见最短边” 作贪心策略&#xff0c;但这并不适合分布式并行操作&#xff0c;比方说所有节点一起构建最小生成树&#xff0c;这些算法都显得同步开销过大&#xff0c;甚至导出错误的结果。 最近研究并构建最大流多路径传输协议的…

【配置】Notion自动化备份到github方案

步骤 打开notion网页&#xff0c;获取到需要的值 token_v2 找到请求getSpaces的 Cookie 值 token_v2 space_id 找到请求getSpaces的响应结果space,如下图&#xff1a; file_token 找个页面点击导出&#xff0c;之后拿到这个配置项 注意&#xff1a;配置项会过期&#xff0c…

内容安全复习 9 - 身份认证系统攻击与防御

文章目录 基于生物特征的身份认证系统概述基于生物特征的身份认证 人脸活体检测检测方法未解决问题 基于生物特征的身份认证系统概述 作用&#xff1a;判别用户的身份、保障信息系统安全。 是识别操作者身份的过程&#xff0c;要保证其**物理身份&#xff08;现实&#xff0…

『 Linux 』 进程间通信概述

文章目录 什么是进程间通信为什么要有进程间通信如何进行进程间通信 什么是进程间通信 进程间通信(IPC)指的是在操作系统重,允许两个或者多个进程之间传递信息或者数据的机制; 进程是操作系统重独立运行的实体,即进程间具有独立性,存在自己的地址空间; 因此进程间默认无法直接访…

mysql8.0找不到my.ini

报错问题解释&#xff1a; MySQL 8.0 在Windows系统中通常不需要 my.ini 文件&#xff0c;因为安装程序会在 %PROGRAMDATA%\MySQL\MySQL Server 8.0\ &#xff08;通常是 C:\ProgramData\MySQL\MySQL Server 8.0\&#xff09;创建默认的配置文件。如果你的系统中找不到 my.ini…

oracle 数据库导入dmp文件

荆轲刺秦王 从线上正式环境导出的 dmp 文件&#xff0c;导入到本地 oracle 数据库。 1. 创建用户: CREATE USER hf_chip IDENTIFIED BY hf_chip; 2. 授予 CONNECT 和 RESOURCE 基本权限给新用户。 GRANT CONNECT, RESOURCE TO hf_chip; 3. 创建表空间 CREATE TABLESPACE…

qt界面开发-01我的第一个qt程序

Qt 是一个跨平台的 C开发库。主要用来开发图形用户界面&#xff08;Graphical User Interface&#xff0c;简 称 GUI&#xff09;程序。 Qt 虽然经常被当做一个 GUI 库&#xff0c;用来开发图形界面应用程序&#xff0c;但这并不是 Qt 的全部&#xff1b; Qt 除了可以绘制漂亮的…

Mac磁盘满了去哪里删 Mac清理了回收站还是空间不足怎么办

在使用Mac电脑的过程中&#xff0c;我们经常会遇到磁盘空间不足的问题。尤其是当我们清理了回收站之后&#xff0c;发现剩余空间依然不足。如何有效解决Mac磁盘空间不足的问题&#xff0c;以及当清理回收站后空间仍然不足时应怎么办呢&#xff1f;本文将为大家介绍Mac磁盘满了去…

搭建预约咨询小程序,高效便捷新选择

一、预约咨询小程序是什么&#xff1f; 预约咨询小程序是一款适用于各种生活场景包括医疗、保洁、宠物护理、法律等方面的预约咨询类小程序。 二、这款小程序有什么亮点优势&#xff1f; 预约咨询小程序适用场景广泛&#xff0c;无论是心理咨询、法律咨询&#xff0c;还是宠物…

INFINI Labs 助力开源与教育:免费许可证计划全面升级

在数字化浪潮席卷全球的今天&#xff0c;INFINI Labs 深刻认识到开源项目和教育机构在技术创新与人才培养中的核心作用。因此&#xff0c;我们郑重推出全新升级的免费许可证计划&#xff0c;旨在全球范围内为开源社区和教育界提供有力支持&#xff0c;共同推动软件生态的繁荣与…

.NET C# 操作Neo4j图数据库

.NET C# 操作Neo4j图数据库 目录 .NET C# 操作Neo4j图数据库环境Code 环境 VisualStudio2022 .NET 6 Neo4j.Driver 5.21 Code // 连接设置 var uri "bolt://localhost:7687"; var user "neo4j"; var password "password"; // 请替换为你的…

React+TS前台项目实战(十三)-- 全局常用响应式加载动画Loading组件封装

文章目录 前言Loading组件1. 功能分析2. 代码详细注释3. 使用方式4. 不同尺寸loading动画效果展示 总结 前言 高阶组件有几大优点&#xff0c;其中一个就是渲染劫持&#xff0c;如懒加载&#xff0c;是否显示该元素loading&#xff0c;这在项目中我们经常用到。毫无疑问&#…

无人机校企合作

有没有想过&#xff0c;无人机和校企合作能碰撞出怎样的火花&#xff1f;&#x1f525;今天就来给大家揭秘一下这个神秘组合&#xff01; 无人机&#xff0c;作为现代科技的代表&#xff0c;已经渗透到我们生活的方方面面。而校企合作&#xff0c;更是推动科技创新、培养人才的…

武汉工程大学24计算机考研数据,有学硕招收调剂,而专硕不招收调剂!

武汉工程大学是一所以工为主&#xff0c;覆盖工、理、管、经、文、法、艺术、医学、教育学等九大学科门类的多科性教学研究型大学&#xff0c;是湖北省重点建设高校、湖北省国内一流学科建设高校&#xff0c;入选卓越工程师教育培养计划、中西部高校基础能力建设工程、“新工科…

从零开始:使用ChatGPT快速创作引人入胜的博客内容

随着科技的飞速发展&#xff0c;人工智能逐渐渗透到我们生活的各个领域。无论是商业、教育还是娱乐&#xff0c;AI技术都在以惊人的速度改变着我们。特别是在内容创作领域&#xff0c;人工智能正发挥着越来越重要的作用。今天&#xff0c;我将和大家分享如何从零开始&#xff0…

微软Azure AI更新视频翻译和语音翻译 API 功能!企业适用TTS文本转语音

很高兴与大家分享 Azure AI 语音翻译产品套件的两个重大更新&#xff01; 分别是视频翻译和增强的实时语音翻译 API。 视频翻译&#xff08;批量&#xff09; 微软宣布推出视频翻译预览版&#xff0c;这是一项突破性的服务&#xff0c;旨在改变企业本地化视频内容的方式。 随着…

天润融通CEO吴强:把简单留给客户,复杂留给自己丨数据猿专访

大数据产业创新服务媒体 ——聚焦数据 改变商业 “这是一种‘无奈’&#xff0c;但更是一种责任”。 这句话听起来似乎有点悲情&#xff0c;但用来形容一家技术服务提供商&#xff0c;似乎也在情理之中。细想&#xff0c;实则充满商业智慧。 这句感慨&#xff0c;缘于数据猿专…

养车小程序系统源码,汽修源码,仿途虎养车系统源码,车辆保养小程序系统

用户端&#xff0b;商家端&#xff0b;师傅端 功能介绍: 支持下单上门服务、到店核销&#xff0c;支持单独选择项目、 也支持选择服务人员、和选择门店多种下单方式&#xff0c; 支持上门服务和到店核销两种服务方式&#xff0c;支持自营和多商家联营两种运营模式&#xff…

前端:Element UI 与 Vuetify 的选择

vuetify优势 1、多端适配&#xff0c;Vuetify完全按照Material设计规范进行开发&#xff0c;每一个组件都经过精心设计&#xff0c;具有模块化、响应式和优秀的性能。 使用独特和动态的 布局 自定义您的应用程序&#xff0c;并使用 SASS 变量 自定义您的组件的样式。只需要做下…

八大经典排序算法

前言 本片博客主要讲解一下八大排序算法的思想和排序的代码 &#x1f493; 个人主页&#xff1a;普通young man-CSDN博客 ⏩ 文章专栏&#xff1a;排序_普通young man的博客-CSDN博客 若有问题 评论区见&#x1f4dd; &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐文章 目录 …