sso单点登录

单点登录(Single Sign-On,简称SSO)是一种身份认证和访问控制的机制,允许用户使用一组凭据(如用户名和密码)登录后,其它多个系统项目可直接访问。

单点登录的优点:

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

完整代码已上传github:https://github.com/benxiaohaihuiwan/SingleSignOn

需要安装的插件

js 复制代码
npm i express 启动服务编写接口
npm i express-session  操作cookie
npm i jsonwebtoken  生成token
npm i cors 解决跨域

需要建立A项目,B项目,server项目,来验证单点登录效果。A,B项目可以直接使用 npm init vite 建立项目,这里我建立的都是vue项目。根目录下建sso.html,A,B项目统一用的登录页面

  • 首先说server项目,建一个index.js文件,顺便安装上述说的四个插件。
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 = {
  // A 项目的 appId
  as6s2ipA: {
    url: "http://localhost:5188",
    name: "vue",
    secretKey: "%Y&*VGHJKLsjkas",
    token: "",
  },
  // B 项目的 appId
  bs789ipB: {
    url: "http://localhost:5189",
    secretKey: "%Y&*FRTYGUHJIOKL",
    name: "react",
    token: "",
  },
};

// 搭建服务
const app = express();

// 解析客户端发送的数据
app.use(express.json());

// 解决跨域
app.use(cors());

// 生成一个 cookie
app.use(
  session({
    secret: "$%^&*()_+DFGHJKL",
    cookie: {
      maxAge: 1000 * 60 * 60 * 24 * 7, //过期时间
    },
  })
);

// 生成token
const getToken = (appId) => {
  /**
   * 1 第一个参数就是荷载,存我们的信息
   * 2 第二个参数是一个密钥,记录在服务器中,在验证时需要用到此参数
   * 3 第三个参数通常是从redis取,设置过期时间,在这里先不设置
   */
  return jwt.sign({ appId }, appToMapUrl[appId].secretKey);
};
/**
 * 一进到页面,就调用登录接口
 * 1 登录过,就返回一个token
 * 2 没登录过,则跳转到登录页面
 */
app.get("/login", (req, res) => {
  const appId = req.query.appId;
  const url = appToMapUrl[appId].url
  if (req.session.username) {
    // 若是有值,证明登录过
    let token;
    if (appToMapUrl[appId].token) {
      // 第一个项目访问
      token = appToMapUrl[appId].token;
    } else {
      // 后面项目访问
      token = getToken(appId);

      appToMapUrl[appId].token = token;
    }
    // 如果 登录过 则 重定向
    res.redirect(`${url}?token=${token}`);
    return;
  }
  const html = fs.readFileSync("../sso.html", "utf-8");

  res.send(html);
});

// 登录成功接口
app.get("/loginSuccess", (req, res) => {
  const { username, password, appId } = req.query;
  // 实际情况下,需要在判断下账号密码是否对应。

  // 生成响应的token
  const token = getToken(appId);
  appToMapUrl[appId].token = token; // 存一份token值
  req.session.username = username; // 存一个标识证明登录过
  const url = appToMapUrl[appId].url; // 获取 url
  // 登录后,重定向页面
  res.redirect(`${url}?token=${token}`);
  //   console.log(username, password, appId);

  res.send("ok");
});

// 服务
app.listen(3000, () => {
  console.log("启动一个3000的服务");
});
  • sso.html 登录页面,进行登录
html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    登录页面
    <form action="/loginSuccess" method="get">
      <label for="username">
        账号:<input name="username" id="username" type="text" />
      </label>
      <label for="password"
        >密码:<input name="password" id="password" type="password"
      /></label>
      <!-- 隐藏appId 作为参数传递 登录的时候 -->
      <label for="appId"
        ><input name="appId" value="" id="appId" type="hidden"
      /></label>
      <button type="submit" id="button">登录</button>
    </form>
    <script>
      // 获取 appId 的参数,提交表单时候要传递
      const appId = location.search.split("=")[1];
      document.getElementById("appId").value = appId;
    </script>
  </body>
</html>
  • A项目 App.vue
html 复制代码
<script setup lang="ts">
// 获取token值
const token = location.search.split("=")[1];

// 若是没有 token,则才调用登录接口
if (!token) {
  // 调用登录接口,跳转页面 传递appId 让用户知道是从那个项目上跳转, 数值需要与appToMapUrl对应
  fetch("http://localhost:3000/login?appId=as6s2ipA").then((res) => {
    location.href = res.url;
  });
}
// else {
//   // 存储下 token
//   localStorage.setItem("token", token);
// }
</script>

<template>
  <div>项目A页面</div>
</template>
  • B项目 App.vue
html 复制代码
<script setup>
// 获取token值
const token = location.search.split("=")[1];

// 若是没有 token,则才调用登录接口
if (!token) {
  // 调用登录接口,跳转页面 传递appId 让用户知道是从那个项目上跳转的,, 数值需要与appToMapUrl对应
  fetch("http://localhost:3000/login?appId=bs789ipB").then((res) => {
    location.href = res.url;
  });
}
// else {
//   // 存储下 token
//   localStorage.setItem("token", token);
// }
</script>

<template>
  <div>项目B页面</div>
</template>
相关推荐
一颗松鼠3 分钟前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript
小远yyds23 分钟前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
程序媛小果42 分钟前
基于java+SpringBoot+Vue的宠物咖啡馆平台设计与实现
java·vue.js·spring boot
小光学长1 小时前
基于vue框架的的流浪宠物救助系统25128(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。
数据库·vue.js·宠物
阿伟来咯~1 小时前
记录学习react的一些内容
javascript·学习·react.js
吕彬-前端1 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱1 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai1 小时前
uniapp
前端·javascript·vue.js·uni-app
也无晴也无风雨2 小时前
在JS中, 0 == [0] 吗
开发语言·javascript
bysking2 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js