nodejs实现单点登录系统

nodejs实现单点登录系统单点登录 SSO SingleSignOn 就是把 2 个及以上的业务系统中的登录功能剥离出来 形成一个新的系统 做到一次登录后在任意的业务系统中都无需登录的效果 效果如图所示 第一次访问 www a com 首页跳转到 www c com 3000 登录页面 登录成功后跳转 www a com 首页再次访问 www a com 首页 无需登录直接跳转访问 www b com 首页 无需登录直接跳转源码 https github com wantao666 sso nodejs 详细设计 文章目

单点登录SSO(Single Sign On),就是把2个及以上的业务系统中的登录功能剥离出来,形成一个新的系统,做到一次登录后在任意的业务系统中都无需登录的效果。

一. 基础知识

1.1 同源策略

源 = 协议 + 域名 +端口

以http://www.a.com为例:

  • https://www.a.com ❌(协议不同)
  • http://www.b.com ❌(域名不同)
  • http://www.a.com:3000 ❌(端口不同)

同源策略是浏览器的行为,它通过确保应用下的资源只能被本应用访问,来保证安全。

1.2 会话机制

由于http协议是无状态协议(客户端和服务器端数据交换完毕,会关闭连接,下次请求重新建立连接),但我们需要做记住密码等功能时,很明显需要将会话记录下来。

常用的会话跟踪就是cookie和session,简单的理解它们就是可以存放key,value的数据结构,区别在于cookie保存在客户端,session保存在服务器端。

二. 单点登录

1. 同父域SSO

同父域,如www.app1.aaa.com,www.app2.aaa.com这两个服务器都是在.aaa.com的父域名。
默认情况下,两个服务器下页面之间的cookie是互相访问不到的。

但是我们可以通过设置cookie的domain属性为共通的父域名,使得两个服务器下页面之间的cookie可以相互访问到。

router.get('/createCookie', async (ctx, next) => { 
    ctx.cookies.set('username', '123', { 
    maxAge: 60 * 60 * 1000, httpOnly: false, path: '/', domain:'.a.com' //设置domain为共通的父域名 }); ctx.body = "create cookie ok" }) router.get('/getCookie', async (ctx, next) => { 
    let username=ctx.cookies.get('username') if (username){ 
    ctx.body=username }else{ 
    ctx.body='no cookie' } }) 

在这里插入图片描述

2. 跨域SSO

当我们的域名为www.a.com,www.b.com时,无论怎样设置domain都没用了。

那么就要想办法将身份凭证(token)写入到所有域的cookie中

2.1 跨域写cookie
2.1.1 利用< script />标签跨域写cookie(jsonp)

在http://www.a.com/index.js中直接向https://www.c.com:3000/sso直接发送网络请求,是无法跨域写入cookie的。

 <script> $.ajax({ 
    url: 'https://www.c.com:3000/sso?key=username&value=123', method: 'get', }) </script> 

但是我们可以通过< script />标签发起跨域请求,写入cookie

<script src="https://www.c.com:3000/sso?key=username&value=123"></script> 

或者使用jquery jsonp的方式发起跨域请求,写入cookie,这种方式的原理也是通过< script />标签能够跨域实现的。

 $.ajax({ 
    url: 'https://www.c.com:3000/sso?key=username&value=123', method: 'get', dataType:'jsonp' }) 
const options = { 
    key: fs.readFileSync(path.join(__dirname, './https/privatekey.pem')), cert: fs.readFileSync(path.join(__dirname, './https/certificate.pem')), secureOptions: 'TLSv1_2_method' //force TLS version 1.2 } var server = https.createServer(options,app.callback()); //只能使用https协议写cookie router.get('/sso', async (ctx, next) => { 
    let { 
    key, value } = ctx.request.query ctx.cookies.set(key, value, { 
    maxAge: 60 * 60 * 1000, //有效时间,单位毫秒 httpOnly: false, //表示 cookie 是否仅通过 HTTP(S) 发送,, 且不提供给客户端 JavaScript (默认为 true). path: '/', sameSite: 'none', //限制第三方 Cookie secure: true //cookie是否仅通过 HTTPS 发送 }); ctx.body = 'create Cookie ok' }) 

注意:

  1. 浏览器未写入cookie报错his set-cookie was blocked due to http-only
    http-only:表示 cookie 是否仅通过 HTTP(S) 发送,, 且不提供给客户端 JavaScript (默认为 true).
    所以要将httpOnly设置为false.




  2. 浏览器未写入cookie报错this set-cookie was blocked due to user preference
    这个真的坑,因为我是无痕模式打开的浏览器,但是chrome浏览器默认无痕模式下禁用第三方cookie,修改为允许所有cookie就行了.
    在这里插入图片描述




  3. 浏览器未写入cookie报错this set cookie was blocked because it has the SameSite attribute but Secure not set
    需要设置sameSite和secure属性

  4. 浏览器未写入cookie报错server error Error: Cannot send secure cookie over unencrypted connection
    这个我觉得是koa框架写cookie的限制吧,它只能支持https写cookie…,于是我把www.c.com改为了https服务器.

2.1.2 p3p协议头实现IE浏览器跨域

上面说的jsonp的方式在chrome浏览器中完美运行,但是IE浏览器对于cookie更加严格,只用上面方式无法写入cookie,解决办法就是加上p3p的响应头。

router.get('/sso', async (ctx, next) => { 
    let { 
    key, value } = ctx.request.query ctx.cookies.set(key, value, { 
    maxAge: 60 * 60 * 1000, //有效时间,单位毫秒 httpOnly: false, path: '/', sameSite: 'none', secure: true }); ctx.set("P3P", "CP='CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR'") //p3p响应头 ctx.body = 'create Cookie ok' }) 
2.1.3 url参数实现跨域信息传递

访问http://www.c.com:3000/createToken?from=http://www.a.com/createCookie

www.c.com上生成token后将url重写,带上token,重定向到www.a.com

router.get('/createToken', async (ctx, next) => { 
    let { 
    from } = ctx.request.query let token = "123"; ctx.response.redirect(`${ 
     from}?token=${ 
     token}`) }) 

www.a.com上从url上获取token,存入cookie

router.get('/createCookie', async (ctx, next) => { 
    let { 
    token } = ctx.request.query ctx.cookies.set('token', token, { 
    maxAge: 60 * 60 * 1000, //有效时间,单位毫秒 httpOnly: false, path: '/', }); ctx.body = 'set cookie ok' }) 
2.2 跨域读cookie
2.2.1 利用< script />标签跨域读cookie(jsonp)

之前2.1.1利用< script />标签在www.a.com中写入了www.c.com的cookie(username,123),现在想要www.a.com请求的时候携带上www.c.com的cookie,也就是说要跨域读cookie.

<script src="https://www.c.com:3000/readCookie"></script> 

www.c.com

router.get('/readCookie', async (ctx, next) => { 
    let username = ctx.cookies.get('username') console.log('cookie', username) }) 

在这里插入图片描述
可以看到读取到了存储在www.a.com里面domain为www.c.com的cookie.

3. nodejs实现单点登录系统实战

在这里插入图片描述
效果如图所示:

  1. 第一次访问www.a.com首页
  2. 跳转到www.c.com:3000登录页面,登录成功后跳转www.a.com首页
  3. 再次访问www.a.com首页,无需登录直接跳转
  4. 访问www.b.com首页,无需登录直接跳转

源码: https://github.com/wantao666/sso-nodejs

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/224043.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午12:49
下一篇 2026年3月17日 下午12:50


相关推荐

  • NiFi ListSFTP精讲

    NiFi ListSFTP精讲序 since 2021 年 5 月 20 日 22 29auth Hadi 前言从去年年末开始接触使用到 NiFi 到现在为止已经将近半年 这里将一下关于 ListSFTP 类相关组件的使用 NiFi 可以当做 Flink 进行使用 但不是很推荐进行复杂计算的使用 对于我的使用场景来说主要是做数据采集和预处理相关的工作 负责数据流程的第一步 同时也做数据的转换操作比如流式转文件 文件转流式等等 那么获取数据是整个数据预处理的第一步 一般我们都是采用 List amp Fetch 的操作进行数据预处理

    2026年3月18日
    2
  • 针对Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1的解决方案

    针对Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1的解决方案背景 本项目使用 JDK1 8 编译 maven 工程的时候出现如下错误 Failedtoexec apache maven plugins maven compiler plugin 3 1pom 中如下配置 maven 插件 配置中声明使用 JDK1 8 org apache maven plugins maven compiler plugin 3

    2026年3月17日
    1
  • 极易成为网络攻击重点目标 “养龙虾”要警惕这五类安全风险

    极易成为网络攻击重点目标 “养龙虾”要警惕这五类安全风险

    2026年3月14日
    1
  • java保留两位小数4种方法「建议收藏」

    java保留两位小数4种方法「建议收藏」方法一:String的format方法(推荐)doublef=111231.5585;System.out.println(String.format(“%.2f”,f));方法二:DecimalFormat的format方法doublef=111231.5585;DecimalFormatdf=newDecimalFormat(“#.00”);System.out.println(df.format(f));以下内容了解即可,可以不用看方法三:BigDe

    2026年3月8日
    4
  • 安卓 自定义分辨率_android图片适配不同分辨率

    安卓 自定义分辨率_android图片适配不同分辨率Bluestacks是一款非常好用的电脑上的安卓模拟器,用Bluestacks来玩手机游戏是不少朋友的选择,但是Bluestacks默认分辨率都非常大,比如小编的默认分辨率就达到了1280*720。运行Bluestacks后,不仅字和图标很小,打开游戏、软件都显得很模糊,但是安卓界面本身又很大,好多空白的地方。那么多大的分辨率才合适呢?Bluestacks分辨率修改方法1.Bluestacks的合…

    2022年8月13日
    3
  • 渗透测试工具Burp Suite详解[通俗易懂]

    渗透测试工具Burp Suite详解[通俗易懂]BurpSuite的安装BurpSuite是一款集成化的渗透测试工具,包含了很多功能,可以帮助我们高效地完成对Web应用程序的渗透测试和攻击。BurpSuite由Java语言编写,基于Java自身的跨平台性,使这款软件学习和使用起来更方便。BurpSuite不像其他自动化测试工具,它需要手工配置一些参数,触发一些自动化流程,然后才会开始工作。BurpSuite可执行程序是Java文件类型的jar文件,免费版可以从官网下载。免费版的BurpSuite会有许多限制,无法使用很多高

    2022年8月12日
    10

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号