目錄
什麼是單一登入
單一登入原則
NodeJS 示範
统一登录
登录成功!
首頁 web前端 js教程 什麼是單一登入?怎麼使用Nodejs實作SSO

什麼是單一登入?怎麼使用Nodejs實作SSO

Apr 22, 2022 pm 09:13 PM
nodejs

什麼是單一登入?以下這篇文章跟大家介紹一下單一登入原理,聊聊使用Node實作單一登入SSO的方法,希望對大家有幫助!

什麼是單一登入?怎麼使用Nodejs實作SSO

什麼是單一登入

#隨著公司業務的增多,必然會產生各個不同的系統,如果每個系統都需要單獨登入的話就會很不方便。

因此產生了單一登入這樣的解決方案,單一登入全名為Single Sign On,簡稱SSO,意思是在多個系統應用群組中登入一個系統,便可在其他所有系統中得到授權而無需再次登入。

例如小明今天登入了淘寶,如果沒有登錄,就會被要求輸入認證資訊(使用者名稱密碼等),登入過後再去造訪天貓的頁面時就不需要登入可以直接存取。

單一登入原則

什麼是單一登入?怎麼使用Nodejs實作SSO

#SSO 需要有一個獨立的認證中心,只有獨立的驗證中心能接受使用者的用戶名密碼等安全訊息,其他系統不提供登入入口,只接受認證中心的間接授權。 整個過程可以簡單的用上圖描述:

  • 當使用者登入存取應用程式A時,應用程式A發現使用者未登入,跳轉至SSO認證中心,並將自己的位址作為參數方便回調

  • SSO認證中心發現用戶沒有登入過,將用戶引導至登入頁面;用戶填寫用戶名密碼提交登入申請;SSO認證中心校驗用戶信息,建立用戶雨SSO認證中心的會話(這時會把資訊儲存到cookie中),同時建立授權令牌token

  • sso認證中心帶著令牌跳到最初的請求位址(應用A)

  • 應用A拿到令牌去SSO認證中心認證是否有效,如果返回有效註冊應用A

  • 應用A創建與使用者之間的會話,展示資源並維持使用者登入狀態

  • 當使用者存取應用程式B時,發現使用者未登入(SSO認證伺服器與應用A應用B不是同一個網域,不能提供登錄態),跳到SSO認證中心,並將自己的地址和之前和SSO認證中心會話的cookie資訊帶入

  • SSO認證中心發現用戶已登錄,跳轉回應用B位址,並附上令牌token

  • 同樣的應用程式B拿到令牌去SSO認證中心認證是否有效,如果回傳有效註冊應用B

  • 應用程式B建立與使用者之間的會話,展示資源並維持使用者登入態

NodeJS 示範

三個不同的服務

這裡我們需要啟動三個服務來分別模擬應用A,SSO認證伺服器和應用B

什麼是單一登入?怎麼使用Nodejs實作SSO

#這裡連接埠號碼8383的服務是SSO認證伺服器,其餘的:8686 和:8787 分別代表應用A與應用B。

其實應用A與應用B的程式碼幾乎一樣,如上圖所示我們可以透過穿參的方式來設定不同的連接埠及應用程式名稱。

先來看下效果

什麼是單一登入?怎麼使用Nodejs實作SSO

#第一次造訪跳轉至登入頁

什麼是單一登入?怎麼使用Nodejs實作SSO


##應用A判斷登入態,跳到SSO認證伺服器

應用A

const Koa=require('koa');
const Router=require('koa-router')
const views=require('koa-views')
const static=require('koa-static')
const path=require('path');
const app=new Koa();
const router=new Router();
const session=require('koa-session')
const koa2Req=require('koa2-request');

//模版引擎相关配置
app.use(views(path.join(__dirname,'./views')),{
    extension:'ejs'
  })
app.keys=['key']

const keyMap={
  '8686':'koa:sess8686',
  '8787':'koa:sess8787'
}
const CONFIG={
    key:keyMap[process.env.PORT] || 'koa:sess',
    maxAge:1000*60*60*24,
    httpOnly:true
}
app.use(session(CONFIG,app))

const system=process.env.SERVER_NAME
router.get("/",async (ctx)=>{
    //通过 session来判断 应用A的登录状态
    let user=ctx.session.user
    if(user){
     //...
    }
    else //1、当用户登录访问应用A时,应用A发现用户未登录(应为服务器没有保存对应的session)
    {
      let token=ctx.query.token
      //第一次登录url上也不会有令牌
      if(!token)
      {
      //1、跳转到SSO认证服务器
       ctx.redirect(`http://localhost:8383/login?redirectUrl=${ctx.host+ctx.originalUrl}`)
      }
      else
      {
        //...
      }
    }
})
app.use(router.routes())


const port=process.env.PORT||8888

app.listen(port,()=>{
    console.log(`app ${system} running at ${port}`)

})
登入後複製

認證伺服器判斷登入態,渲染登入頁面

認證伺服器SSO

認證伺服器的目錄結構如下 主要處理兩個功能,一是登入邏輯,二是之後驗證令牌的有效性,分別有路由login.js 和check-token.js 處理

Auth /index.js

const Koa=require('koa');
const Router=require('koa-router')
const views=require('koa-views')
const path=require('path');
const app=new Koa();
const router=new Router();
const login=require("./routes/login")
const checkToken=require('./routes/check-token')
const bodyparser=require('koa-bodyparser')

app.use(views(path.join(__dirname,'./views')),{
    extension:'ejs'
  })
app.use(bodyparser())
//处理登录相关的逻辑
router.use('/login',login.routes())
//处理令牌验证的逻辑
router.use('/check_token',checkToken.routes())
app.use(router.routes())

app.listen(8383,()=>{
    console.log(`app listen at 8383`)
})
登入後複製

剛才我們從應用程式A跳到http://localhost:8383/login?redirectUrl=localhost:8686來看login中的邏輯

Auth /routes/login.js

const service = require("../service");
const router=require("koa-router")()


router.get('/',async (ctx)=>{
  const cookies=ctx.cookies;
  const token=cookies.get('token');
  //从cookie中判断应用A的登录态
  if(token && service.isTokenVailid(token)){
    //。。。如果有登录过
  }else{
    //2、SSO认证中心发现用户没有登录过,于是渲染登录页面登录页面;
    await ctx.render('login.ejs',{
        extension:'ejs'
     })
  }
})

//。。。
module.exports=router
登入後複製

登入頁面


Auth/views/login.ejs

<html>
<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>统一登录</title>
</head>
<body>
    <h1 id="统一登录">统一登录</h1>
    <form method="post">
       <div>用户名: <input type="text" name="name"/></div>
       <div>密码  <input type="text" name="password" /></div>
       <div><input type="submit" value=&#39;登录&#39;></div>
    </form>
</body>
</html>
登入後複製

校驗使用者訊息,建立令牌

Auth/routes/login.js

router.post(&#39;/&#39;,async (ctx)=>{
//2、用户填写用户名密码提交登录申请;
   const body=ctx.request.body;
   const {name,password}=body;
    //2、SSO认证中心校验用户信息,
   if(name==="admin" && password==="123456"){
    //2、创建用户雨SSO认证中心的会话(这时会把信息保存到cookie中),同时创建授权令牌token
       const token="passport";
       await ctx.cookies.set(&#39;token&#39;,token,{
           maxAge:1000*60*60*24*30,
           httpOnly:true
       })
       if(ctx.query.redirectUrl){
       //3、sso认证中心带着令牌跳转到最初的请求地址(应用A)
           ctx.redirect(`${ctx.protocol}://${ctx.query.redirectUrl}?token=${token}`)
           //回跳地址是 http://localhost:8686/?token=passport
       }else{
           ctx.body="<h1 id="登录成功">登录成功!</h1>"
       }
   }else{
       ctx.response.body={
           error:1,
           msg:&#39;用户名或密码错误&#39;
       }
   }
})
登入後複製
##從認證伺服器攜帶令牌跳轉回應用A

令牌校驗回傳資源

應用A###
app.use(views(path.join(__dirname,&#39;./views&#39;)),{
    extension:&#39;ejs&#39;
  })

//...

const system=process.env.SERVER_NAME
router.get("/",async (ctx)=>{
    let user=ctx.session.user
    if(user){
      //...
    }
    else
    //这时应用A依旧没有登录态 但url上有了令牌 http://localhost:8686/?token=passport
   {
      let token=ctx.query.token
      if(!token)
      {
        //...跳转去SSO登录页面
      }
      else 
      //跳回应用A时走这里的逻辑
      {
        //ajax请求 4. 应用A拿到令牌去SSO认证中心认证是否有效,如果返回有效注册应用A
        const url=`://localhost:8383/check_token?token=${token}&t=${new Date().getTime()}`
        let data = await koa2Req(ctx.protocol + url);
        if(data && data.body){
            try {
                const body=JSON.parse(data.body)
                const {error,userId}=body;
                // console.log(error,userId) 0,admin
                if(error==0){
                    if(!userId){
                        ctx.redirect(`http://localhost:8383/login?redirectUrl=${ctx.host+ctx.originalUrl}`)
                        return
                    }
                    //验证通过后注册session,渲染页面
                    //5. 应用A创建与用户之间的会话,展示资源并维持用户登录态
                    ctx.session.user=userId;
                    await ctx.render(&#39;index.ejs&#39;,{
                        user:userId,
                        system
                    })
                }else{
                    ctx.redirect(`http://localhost:8383/login?redirectUrl=${ctx.host+ctx.originalUrl}`)
                }
            } catch (error) {console.log(error)}

            
        }
      }
    }
})
app.use(router.routes())

const port=process.env.PORT||8888

app.listen(port,()=>{
    console.log(`app ${system} running at ${port}`)

})
登入後複製
###與之對應的SSO中處理驗證令牌的邏輯###Auth/routes/check-token###
const router=require("koa-router")()
const service=require("../service")

router.get(&#39;/&#39;,async (ctx)=>{
  const token=ctx.query.token;
  const result={
      error:1
  }
  //当token 是 password时
  if(service.isTokenVailid(token)){
    result.error=0;
    result.userId=&#39;admin&#39;
  }
  ctx.body=result
 

})


module.exports=router
登入後複製
# ##Auth/service/index.js###
module.exports={
    isTokenVailid: function(token){
      if(token && token===&#39;passport&#39;){
          return true
      }
      return false
    }
}
登入後複製
###至此使用者已經能正常存取應用程式A,SSO伺服器和應用程式A伺服器上都有了使用者登入的資訊。 ############存取應用程式B###############帶cookie跳到SSO認證伺服器#########應用程式B###
//...

router.get("/",async (ctx)=>{
    let user=ctx.session.user
    if(user){
      //...
    }else{
      let token=ctx.query.token
      //...
      if(!token)
      {
      //同样既没有session也没有令牌,跳转到SSO认证服务器
      //6、当用户访问应用B时,发现用户未登录(SSO认证服务器与应用A应用B不是同一个域,不能提供登录态),跳转到SSO认证中心,并将自己的地址和之前和SSO认证中心会话的cookie信息带入
          ctx.redirect(`http://localhost:8383/login?redirectUrl=${ctx.host+ctx.originalUrl}`)
      }
      else
      {
        //。。。验证令牌的部分
      }
    }
})
app.use(router.routes())

const port=process.env.PORT||8888

app.listen(port,()=>{
    console.log(`app ${system} running at ${port}`)

})
登入後複製

从认证服务器携带令牌跳转回应用B

SSO认证服务器 ,再次登录时携带了cookie,因此不会再请求登录页面 Auth/routes/login

//...
router.get(&#39;/&#39;,async (ctx)=>{
  const cookies=ctx.cookies;
  const token=cookies.get(&#39;token&#39;);
  //7. SSO认证中心发现用户已登录,跳转回应用B地址,并附上令牌token
  if(token && service.isTokenVailid(token)){
    const redirectUrl=ctx.query.redirectUrl;
    if(redirectUrl){
       //带着令牌跳转回应用B
        ctx.redirect(`${ctx.protocol}://${redirectUrl}?token=${token}`)
    }else{
        ctx.body="<h1 id="登录成功">登录成功!</h1>"
    }
  }else{
    //...渲染登录页面
  }
})
//..
登入後複製

令牌校验 返回资源

这里的逻辑和5,6两步一样,因为token容易伪造,所以要检验真伪。 应用B

app.use(views(path.join(__dirname,&#39;./views&#39;)),{
    extension:&#39;ejs&#39;
  })

//...

const system=process.env.SERVER_NAME
router.get("/",async (ctx)=>{
    let user=ctx.session.user
    if(user){
      //...
    }
    else
    //这时应用B依旧没有登录态 但url上有了令牌 http://localhost:8787/?token=passport
   {
      let token=ctx.query.token
      if(!token)
      {
        //...跳转去SSO登录页面
      }
      else 
      //跳回应用B时走这里的逻辑
      {
        //ajax请求 8. 同样的应用B拿到令牌去SSO认证中心认证是否有效,如果返回有效注册应用B
        const url=`://localhost:8383/check_token?token=${token}&t=${new Date().getTime()}`
        let data = await koa2Req(ctx.protocol + url);
        if(data && data.body){
            try {
                const body=JSON.parse(data.body)
                const {error,userId}=body;
                // console.log(error,userId) 0,admin
                if(error==0){
                    if(!userId){
                        ctx.redirect(`http://localhost:8383/login?redirectUrl=${ctx.host+ctx.originalUrl}`)
                        return
                    }
                    //验证通过后注册session,渲染页面
                    //9. 应用B创建与用户之间的会话,展示资源并维持用户登录态
                    ctx.session.user=userId;
                    await ctx.render(&#39;index.ejs&#39;,{
                        user:userId,
                        system
                    })
                }else{
                    ctx.redirect(`http://localhost:8383/login?redirectUrl=${ctx.host+ctx.originalUrl}`)
                }
            } catch (error) {console.log(error)}

            
        }
      }
    }
})
app.use(router.routes())

const port=process.env.PORT||8888

app.listen(port,()=>{
    console.log(`app ${system} running at ${port}`)

})
登入後複製

至此单点登录的大部分逻辑都已经完成,之后再session有效期内再访问页面,就不需要再登录,直接返回资源

router.get("/",async (ctx)=>{
//如果session中有用户信息,说明已经登录过,直接返回请求资源
    let user=ctx.session.user
    if(user){
        await ctx.render(&#39;index.ejs&#39;,{
              user,
              system
        })
    }
    //...
 })
登入後複製

原文地址:https://juejin.cn/post/7088343138905325582

作者:YoYo君

更多node相关知识,请访问:nodejs 教程

以上是什麼是單一登入?怎麼使用Nodejs實作SSO的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

nodejs和vuejs區別 nodejs和vuejs區別 Apr 21, 2024 am 04:17 AM

Node.js 是一種伺服器端 JavaScript 執行時,而 Vue.js 是一個客戶端 JavaScript 框架,用於建立互動式使用者介面。 Node.js 用於伺服器端開發,如後端服務 API 開發和資料處理,而 Vue.js 用於用戶端開發,如單一頁面應用程式和響應式使用者介面。

nodejs是後端框架嗎 nodejs是後端框架嗎 Apr 21, 2024 am 05:09 AM

Node.js 可作為後端框架使用,因為它提供高效能、可擴展性、跨平台支援、豐富的生態系統和易於開發等功能。

nodejs怎麼連接mysql資料庫 nodejs怎麼連接mysql資料庫 Apr 21, 2024 am 06:13 AM

要連接 MySQL 資料庫,需要遵循以下步驟:安裝 mysql2 驅動程式。使用 mysql2.createConnection() 建立連接對象,其中包含主機位址、連接埠、使用者名稱、密碼和資料庫名稱。使用 connection.query() 執行查詢。最後使用 connection.end() 結束連線。

nodejs安裝目錄裡的npm與npm.cmd檔有什麼差別 nodejs安裝目錄裡的npm與npm.cmd檔有什麼差別 Apr 21, 2024 am 05:18 AM

Node.js 安裝目錄中有兩個與 npm 相關的文件:npm 和 npm.cmd,區別如下:擴展名不同:npm 是可執行文件,npm.cmd 是命令視窗快捷方式。 Windows 使用者:npm.cmd 可以在命令提示字元中使用,npm 只能從命令列執行。相容性:npm.cmd 特定於 Windows 系統,npm 跨平台可用。使用建議:Windows 使用者使用 npm.cmd,其他作業系統使用 npm。

nodejs中的全域變數有哪些 nodejs中的全域變數有哪些 Apr 21, 2024 am 04:54 AM

Node.js 中存在以下全域變數:全域物件:global核心模組:process、console、require執行階段環境變數:__dirname、__filename、__line、__column常數:undefined、null、NaN、Infinity、-Infinity

nodejs和java的差別大嗎 nodejs和java的差別大嗎 Apr 21, 2024 am 06:12 AM

Node.js 和 Java 的主要差異在於設計和特性:事件驅動與執行緒驅動:Node.js 基於事件驅動,Java 基於執行緒驅動。單執行緒與多執行緒:Node.js 使用單執行緒事件循環,Java 使用多執行緒架構。執行時間環境:Node.js 在 V8 JavaScript 引擎上運行,而 Java 在 JVM 上運行。語法:Node.js 使用 JavaScript 語法,而 Java 使用 Java 語法。用途:Node.js 適用於 I/O 密集型任務,而 Java 適用於大型企業應用程式。

nodejs是後端開發語言嗎 nodejs是後端開發語言嗎 Apr 21, 2024 am 05:09 AM

是的,Node.js 是一種後端開發語言。它用於後端開發,包括處理伺服器端業務邏輯、管理資料庫連接和提供 API。

nodejs專案怎麼部署到伺服器 nodejs專案怎麼部署到伺服器 Apr 21, 2024 am 04:40 AM

Node.js 專案的伺服器部署步驟:準備部署環境:取得伺服器存取權限、安裝 Node.js、設定 Git 儲存庫。建置應用程式:使用 npm run build 產生可部署程式碼和相依性。上傳程式碼到伺服器:透過 Git 或檔案傳輸協定。安裝依賴項:SSH 登入伺服器並使用 npm install 安裝應用程式相依性。啟動應用程式:使用 node index.js 等命令啟動應用程序,或使用 pm2 等進程管理器。設定反向代理(可選):使用 Nginx 或 Apache 等反向代理路由流量到應用程式

See all articles