成人怡红院-成人怡红院视频在线观看-成人影视大全-成人影院203nnxyz-美女毛片在线看-美女免费黄

站長資訊網(wǎng)
最全最豐富的資訊網(wǎng)站

什么是單點登錄?怎么使用Nodejs實現(xiàn)SSO

什么是單點登錄?下面本篇文章給大家給大家介紹一下單點登錄原理,聊聊使用Node實現(xiàn)單點登錄SSO的方法,希望對大家有所幫助!

什么是單點登錄?怎么使用Nodejs實現(xiàn)SSO

什么是單點登錄

隨著公司業(yè)務的增多,必然會產(chǎn)生各個不同的系統(tǒng),如果每個系統(tǒng)都需要單獨登錄的話就會很不方便。

因此產(chǎn)生了單點登錄這樣的解決方案,單點登錄全稱 Single Sign On,簡稱SSO,意思是在多個系統(tǒng)應用群中登錄一個系統(tǒng),便可在其他所有系統(tǒng)中得到授權(quán)而無需再次登錄。

比如小明今天登錄了淘寶,如果沒有登錄,就會被要求輸入認證信息(用戶名密碼等),登錄過后再去訪問天貓的頁面時就不需要登錄可以直接訪問。

單點登錄原理

什么是單點登錄?怎么使用Nodejs實現(xiàn)SSO

SSO 需要有一個獨立的認證中心,只有獨立的驗證中心能接受用戶的用戶名密碼等安全信息,其他系統(tǒng)不提供登錄入口,只接受認證中心的間接授權(quán)。 整個過程可以簡單的用上圖描述:

  • 當用戶登錄訪問應用A時,應用A發(fā)現(xiàn)用戶未登錄,跳轉(zhuǎn)至SSO認證中心,并將自己的地址作為參數(shù)方便回調(diào)

  • SSO認證中心發(fā)現(xiàn)用戶沒有登錄過,將用戶引導至登錄頁面;用戶填寫用戶名密碼提交登錄申請;SSO認證中心校驗用戶信息,創(chuàng)建用戶雨SSO認證中心的會話(這時會把信息保存到cookie中),同時創(chuàng)建授權(quán)令牌token

  • sso認證中心帶著令牌跳轉(zhuǎn)到最初的請求地址(應用A)

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

  • 應用A創(chuàng)建與用戶之間的會話,展示資源并維持用戶登錄態(tài)

  • 當用戶訪問應用B時,發(fā)現(xiàn)用戶未登錄(SSO認證服務器與應用A應用B不是同一個域,不能提供登錄態(tài)),跳轉(zhuǎn)到SSO認證中心,并將自己的地址和之前和SSO認證中心會話的cookie信息帶入

  • SSO認證中心發(fā)現(xiàn)用戶已登錄,跳轉(zhuǎn)回應用B地址,并附上令牌token

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

  • 應用B創(chuàng)建與用戶之間的會話,展示資源并維持用戶登錄態(tài)

NodeJS 演示

三個不同的服務

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

什么是單點登錄?怎么使用Nodejs實現(xiàn)SSO

這里端口號 8383的服務是SSO認證服務器,其余的 :8686 和 :8787 分別代表應用A與應用B。

其實應用A與應用B的代碼幾乎一樣,如上圖所示我們可以通過穿參的方式來設(shè)置不同的端口及應用名。

先來看下效果

什么是單點登錄?怎么使用Nodejs實現(xiàn)SSO

首次訪問跳轉(zhuǎn)至登錄頁

應用A判斷登錄態(tài),跳轉(zhuǎn)到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');  //模版引擎相關(guān)配置 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的登錄狀態(tài)     let user=ctx.session.user     if(user){      //...     }     else //1、當用戶登錄訪問應用A時,應用A發(fā)現(xiàn)用戶未登錄(應為服務器沒有保存對應的session)     {       let token=ctx.query.token       //第一次登錄url上也不會有令牌       if(!token)       {       //1、跳轉(zhuǎn)到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}`)  })

認證服務器判斷登錄態(tài),渲染登錄頁

認證服務器SSO

認證服務器的目錄結(jié)構(gòu)如下 主要處理兩個功能,一是登錄邏輯,二是之后驗證令牌的有效性,分別有路由 login.js 和 check-token.js 處理

什么是單點登錄?怎么使用Nodejs實現(xiàn)SSO

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()) //處理登錄相關(guān)的邏輯 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跳轉(zhuǎn)到 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的登錄態(tài)   if(token && service.isTokenVailid(token)){     //。。。如果有登錄過   }else{     //2、SSO認證中心發(fā)現(xiàn)用戶沒有登錄過,于是渲染登錄頁面登錄頁面;     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>統(tǒng)一登錄</title> </head> <body>     <h1>統(tǒng)一登錄</h1>     <form method="post">        <div>用戶名: <input type="text" name="name"/></div>        <div>密碼  <input type="text" name="password" /></div>        <div><input type="submit" value='登錄'></div>     </form> </body> </html>

校驗用戶信息,創(chuàng)建令牌

Auth/routes/login.js

router.post('/',async (ctx)=>{ //2、用戶填寫用戶名密碼提交登錄申請;    const body=ctx.request.body;    const {name,password}=body;     //2、SSO認證中心校驗用戶信息,    if(name==="admin" && password==="123456"){     //2、創(chuàng)建用戶雨SSO認證中心的會話(這時會把信息保存到cookie中),同時創(chuàng)建授權(quán)令牌token        const token="passport";        await ctx.cookies.set('token',token,{            maxAge:1000*60*60*24*30,            httpOnly:true        })        if(ctx.query.redirectUrl){        //3、sso認證中心帶著令牌跳轉(zhuǎn)到最初的請求地址(應用A)            ctx.redirect(`${ctx.protocol}://${ctx.query.redirectUrl}?token=${token}`)            //回跳地址是 http://localhost:8686/?token=passport        }else{            ctx.body="<h1>登錄成功!</h1>"        }    }else{        ctx.response.body={            error:1,            msg:'用戶名或密碼錯誤'        }    } })

從認證服務器攜帶令牌跳轉(zhuǎn)回應用A

令牌校驗 返回資源

應用A

app.use(views(path.join(__dirname,'./views')),{     extension:'ejs'   })  //...  const system=process.env.SERVER_NAME router.get("/",async (ctx)=>{     let user=ctx.session.user     if(user){       //...     }     else     //這時應用A依舊沒有登錄態(tài) 但url上有了令牌 http://localhost:8686/?token=passport    {       let token=ctx.query.token       if(!token)       {         //...跳轉(zhuǎn)去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創(chuàng)建與用戶之間的會話,展示資源并維持用戶登錄態(tài)                     ctx.session.user=userId;                     await ctx.render('index.ejs',{                         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('/',async (ctx)=>{   const token=ctx.query.token;   const result={       error:1   }   //當token 是 password時   if(service.isTokenVailid(token)){     result.error=0;     result.userId='admin'   }   ctx.body=result    })   module.exports=router

Auth/service/index.js

module.exports={     isTokenVailid: function(token){       if(token && token==='passport'){           return true       }       return false     } }

至此用戶已經(jīng)能正常訪問應用A,SSO服務器和應用A服務器上都有了用戶登錄過的信息。

訪問應用B

帶cookie跳轉(zhuǎn)至SSO認證服務器

應用B

//...  router.get("/",async (ctx)=>{     let user=ctx.session.user     if(user){       //...     }else{       let token=ctx.query.token       //...       if(!token)       {       //同樣既沒有session也沒有令牌,跳轉(zhuǎn)到SSO認證服務器       //6、當用戶訪問應用B時,發(fā)現(xiàn)用戶未登錄(SSO認證服務器與應用A應用B不是同一個域,不能提供登錄態(tài)),跳轉(zhuǎn)到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}`)  })

從認證服務器攜帶令牌跳轉(zhuǎn)回應用B

SSO認證服務器 ,再次登錄時攜帶了cookie,因此不會再請求登錄頁面 Auth/routes/login

//... router.get('/',async (ctx)=>{   const cookies=ctx.cookies;   const token=cookies.get('token');   //7. SSO認證中心發(fā)現(xiàn)用戶已登錄,跳轉(zhuǎn)回應用B地址,并附上令牌token   if(token && service.isTokenVailid(token)){     const redirectUrl=ctx.query.redirectUrl;     if(redirectUrl){        //帶著令牌跳轉(zhuǎn)回應用B         ctx.redirect(`${ctx.protocol}://${redirectUrl}?token=${token}`)     }else{         ctx.body="<h1>登錄成功!</h1>"     }   }else{     //...渲染登錄頁面   } }) //..

令牌校驗 返回資源

這里的邏輯和5,6兩步一樣,因為token容易偽造,所以要檢驗真?zhèn)巍?應用B

app.use(views(path.join(__dirname,'./views')),{     extension:'ejs'   })  //...  const system=process.env.SERVER_NAME router.get("/",async (ctx)=>{     let user=ctx.session.user     if(user){       //...     }     else     //這時應用B依舊沒有登錄態(tài) 但url上有了令牌 http://localhost:8787/?token=passport    {       let token=ctx.query.token       if(!token)       {         //...跳轉(zhuǎn)去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創(chuàng)建與用戶之間的會話,展示資源并維持用戶登錄態(tài)                     ctx.session.user=userId;                     await ctx.render('index.ejs',{                         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}`)  })

至此單點登錄的大部分邏輯都已經(jīng)完成,之后再session有效期內(nèi)再訪問頁面,就不需要再登錄,直接返回資源

router.get("/",async (ctx)=>{ //如果session中有用戶信息,說明已經(jīng)登錄過,直接返回請求資源     let user=ctx.session.user     if(user){         await ctx.render('index.ejs',{               user,               system         })     }     //...  })

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

作者:YoYo君

贊(0)
分享到: 更多 (0)
?
網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號
国产精品视频二区不卡| 国产亚洲欧美日韩精品一区二区| 国产成人无码AV在线播放不卡| 国产精品成人免费视频网站| 国精产品一二三四线| 久久国产加勒比精品无码| 老太BBWWBBWW高潮| 琪琪午夜伦伦电影理论片 | 国产乱子伦视频一区二区三区| 黑人巨大猛烈捣出白浆视频在线| 久久人人爽人人爽人人AV东京热 | AV未满十八禁免费网站| 成人爽A毛片在线视频淮北| 国产精品宅男擼66M3U8| 久久九九久精品国产综合一千收藏| 内射人妻无码色AV麻豆去百度搜| 日韩人妻无码一区二区三区| 性色ΑV一二三天美传媒| 亚洲综合欧美色五月俺也去| 8X8Ⅹ成人免费视频在线观看| 粉嫩Av网站在线观看| 狠狠色丁香久久综合婷婷| 男女作爱免费网站| 熟女体下毛毛黑森林| 亚洲精品乱码久久久久久不卡| 97久久国产露脸精品国产| 国产97在线 | 传媒有限公司| 激情偷乱人伦小说视频在线| 女的被弄到高潮娇喘喷水视频| 台湾无码AV一区二区三区| 亚洲欧美日韩综合一区| FRXXEE中国XXEE医生| 国产日韩在线欧美视频| 麻豆星空传媒果冻传媒大象 | 国产日产欧产精品精品推荐免费| 久久亚洲精品成人无码网站夜色| 日韩精品无码人妻一区二区三区| 亚洲成a人片在线观看无码专区| ASS十三小美女ASSPICS| 国产片AV国语在线观麻豆| 免费A级毛片无码A∨蜜芽| 挽起裙子迈开腿坐上MBA| 在线观看的AV网站| 国产AV无码专区亚洲AⅤ| 久久天天躁夜夜躁狠狠85| 色婷婷AV一区二区三区在线观看 | 久久久久精品国产三级| 日日摸夜夜添无码无码AV| 亚洲无码成人av| 俄罗斯性孕妇孕交| 久久亚洲AV午夜福利精品一区二| 色综合精品无码一区二区三区| 亚洲中文久久精品无码软件| 丰满少妇XXXⅩBBBB视频| 久久婷婷人人澡人人爽人人爱| 天美传媒MV免费观看软件的特点| 又硬又粗又长又爽免费看 | 亚洲AV成人综合网伊人APP| A级毛片100部免费观看| 极品AV麻豆国产在线观看| 日本熟妇色XXXXXBBB日本| 野花韩国高清免费神马| 国产国拍亚洲精品MV在线观看 | 久久国产精品99精品国产987 | 国产精品久久成人网站| 欧美丰满美乳XXⅩ高潮WWW| 亚洲国产精品成人精品无码区| 成人无遮挡裸免费视频在线观看 | 99999久久久久久亚洲| 精产国品一二三产区区| 首页 动漫 亚洲 欧美 日韩| 4D肉蒲团之性奴大战奶水| 精品国产一区二区三区色欲| 色噜噜狠狠色综合AV| 91精品人妻一区二区| 精品一区二区三区在线成人 | AV无码一区二区大桥久未| 精人妻无码一区二区三区蜜桃| 天堂8А√中文在线官网| A级毛片毛片免费观看丝瓜| 亚洲AV无码久久精品狠狠爱浪潮| 亚洲熟女综合一区二区三区| 国产精品国产三级国产AV主播 | 免费稀缺拗女一区二区| 亚洲国产精品无码久久九九大片| 国产成人MV在线播放| 人妻少妇乱孑伦无码专区蜜柚| 尤物AV无码色AV无码| 护士被弄到高潮喷水抽搐| 无码国产69精品久久久久APP | 中文字幕亚洲欧美日韩在线不卡| 精品国产AⅤ无码一区二区| 无码任你躁久久久久久| 俄罗斯人和欧洲人长相区别| 欧美最猛黑人XXXXX猛交| 中文字幕AV无码一区二区蜜芽三 | 亚洲人成精品久久久久| 国产无套码AⅤ在线观看 | 公的粗大挺进了我的密道视频| 人妻互换免费中文字幕| 91人人妻人人做人人爱| 老少配XXOO老少配| 亚洲色大成网站WWW看下面| 国产亚洲精品精华液| 无码人妻精品一区二区三区蜜桃91 | 日本亲与子乱人妻HD| JAPANESEⅩⅩⅩHD中文| 尿眼BDSM奇特虐| 中文字幕人妻丝袜乱一区三区 | 太多了太满了肚子装不下了| 超碰成人人人做人人爽| 人妻[21P]大胆| めんたいさんでぃふぇんすっ甘雨| 男女无遮挡猛进猛出免费视频 | 唔嗯啊欧美一级作爱网站| 国产成人无码AV片在线观看不卡| 色爽黄1000部免费软件下载| 成人无码H免费动漫在线观看| 人妻无码一区二区三区蜜桃| JAVAPARSER乱偷| 欧美最猛性XXXXX免费| WWW.COM.含羞草| 全国主要城市天气预报| 斑马视频电影免费观看| 琪琪无码午夜伦埋影院| 办公室撕开奶罩揉吮奶头H文| 人妻少妇精品久久| 薄白丝小仙女自慰喷水| 日本无码SM凌虐强制M字开腿| 成人无码特黄特黄AV片在线| 色综合色天天久久婷婷基地| 国产AV在线观看| 无人区码一码二码三码区别在哪里 | 好硬好涨老师受不了了| 亚洲狠狠婷婷综合久久蜜芽| 久久国产乱子伦免费精品| 亚洲伊人久久大香线蕉| 乱码一线二线三线新区破解欧| 18禁肉肉无遮挡无码网站| 欧美ZC0O人与善交| 白嫩无码人妻丰满熟妇啪啪区百度| 日韩成人无码AV| 国产AV无码专区亚洲AV麻豆| 无码人妻少妇色欲AV一区二区| 国产在线 | 传媒麻豆| 亚洲人成网线在线播放VA| 两个领导在车里吃我奶| 99热门精品一区二区三区无码 | 国产VA免费精品观看精品| 西西人体午夜大胆无码视频| 加比勒色综合久久| 伊人久久大香线蕉AV影院| 欧美成人精品一区二区三区色欲| 阿姨呀咿呀啊咿呀咿呀| 丝袜高潮流白浆潮喷在线播放| 国产美女被遭强高潮网站免费| 亚洲精品中文字幕久久久久下载| 麻豆亚洲国产成人精品无码区 | 精品久久久BBBB人妻| 张柏芝性XXXXXⅩ| 人妻丰满熟妇AV无码区不卡 | 成人污污污WWW网站免费| 午夜131美女爱做视频| 久久精品国产99久久丝袜蜜桃| 97久久香蕉国产线看观看| 日韩一区二区视频在线| 国产做出在线 | 传媒麻豆| 一区二区三区AV波多野结衣| 亲孑伦视频一区二区三区一| 国产精品成人3p一区二区三区 | 超薄肉色丝袜一区二区| 学生无码AV一区二区三区| 久久综合九色欧美综合狠狠| АⅤ天堂中文在线网| 性色AV极品无码专区亚洲AV| 久久婷婷五月综合色国产香蕉| 把腿张开老子臊烂你多p晓晓| 新JAPANESEVIDEO乱| 两个男用舌头到我的蕊花| 大伊香蕉精品视频在线天堂| 亚洲成A人片在线播放| 欧美成天堂网地址| 国产精品一区二区AV麻豆| 在线观看草莓视频MV的免费网站| 日韩一区二区视频在线| 精品人妻少妇嫩草Av无码专区| BDSM女囚BDSMTV| 性中国VODAFONEWIFI| 牛牛本精品99久久精品66| 国产精品高潮AV久久无码| 曰本BBWW高潮BBWR| 试看A级看一毛片二十分钟| 久久久久久精品国产亚洲AV麻豆| 成年免费视频黄网站在线观看| 亚洲国产AⅤ天堂久久| 欧美猛少妇性ⅩXXX| 韩国精品福利一区二区三区| JIZZ中国JIZZ在线观看| 亚洲国产成人爱AV网站| 日本极品白嫩ASSPICS|