Maison > interface Web > js tutoriel > le corps du texte

Parlons de la façon de mettre en œuvre l'authentification unique (SSO) basée sur Node

青灯夜游
Libérer: 2022-12-06 19:51:09
avant
1865 Les gens l'ont consulté

Qu'est-ce que l'authentification unique ? Quel est le principe ? Comment y parvenir ? L'article suivant vous présentera l'authentification unique et expliquera comment utiliser Node pour mettre en œuvre l'authentification unique SSO. J'espère qu'il vous sera utile !

Parlons de la façon de mettre en œuvre l'authentification unique (SSO) basée sur Node

【Tutoriels associés recommandés : Tutoriel vidéo Nodejs, Enseignement de la programmation

Qu'est-ce que l'authentification unique

À mesure que l'activité de l'entreprise augmente, différents systèmes seront inévitablement produits si chacun le ferait. être très gênant si le système nécessite des connexions séparées.

Une solution telle que l'authentification unique est née. Le nom complet de l'authentification unique est Single Sign On, ou SSO en abrégé, cela signifie que si vous vous connectez à un système dans plusieurs groupes d'applications système, vous pouvez l'être. autorisé dans tous les autres systèmes sans vous reconnecter.

Par exemple, Xiao Ming s'est connecté à Taobao aujourd'hui. S'il ne s'est pas connecté, il lui sera demandé de saisir des informations d'authentification (nom d'utilisateur, mot de passe, etc.). Après s'être connecté, lorsqu'il visitera la page de Tmall, il pourra y accéder. directement sans vous connecter.

Principe de l'authentification unique

Parlons de la façon de mettre en œuvre l'authentification unique (SSO) basée sur Node

SSO nécessite un centre de certification indépendant. Seul le centre de vérification indépendant peut accepter le nom d'utilisateur, le mot de passe et d'autres informations de sécurité de l'utilisateur. Les autres systèmes ne fournissent pas d'entrées de connexion et acceptent uniquement. ceux du centre de certification. Autorisation indirecte. L'ensemble du processus peut être simplement décrit avec l'image ci-dessus :

  • Lorsque l'utilisateur se connecte pour accéder à l'application A, l'application A constate que l'utilisateur n'est pas connecté, accède au centre d'authentification SSO et utilise sa propre adresse comme un paramètre pour faciliter les rappels

  • SSO Le centre d'authentification constate que l'utilisateur ne s'est pas connecté auparavant et dirige l'utilisateur vers la page de connexion, l'utilisateur remplit le nom d'utilisateur et le mot de passe pour soumettre une demande de connexion ; le centre d'authentification SSO vérifie ; les informations utilisateur et crée une session entre l'utilisateur et le centre d'authentification SSO (les informations sont enregistrées dans un cookie à ce moment-là), et crée un jeton d'autorisation en même temps

  • le centre de certification sso passe à la demande d'origine adresse (application A) avec le jeton

  • L'application A obtient le jeton et se rend au centre de certification SSO pour vérifier s'il est valide S'il revient Enregistrez effectivement l'application A

  • L'application A crée une session avec l'utilisateur. , affiche les ressources et maintient l'état de connexion de l'utilisateur

  • Lorsque l'utilisateur accède à l'application B, il s'avère que l'utilisateur n'est pas connecté (le serveur d'authentification SSO et l'application A et l'application B ne sont pas le même domaine, ne peuvent pas fournir l'état de connexion) , accédez au centre de certification SSO et apportez votre adresse et les informations de cookie de la session précédente avec le centre de certification SSO

  • Le centre de certification SSO constate que l'utilisateur est connecté et revient à l'adresse de l'application B, et joignez le jeton token

  • La même application B obtient le jeton et se rend au centre de certification SSO pour vérifier s'il est valide. Si elle renvoie une application enregistrée valide B

  • , l'application B crée une session avec l'utilisateur et affiche. ressources et maintenir le statut de connexion de l'utilisateur

Démonstration NodeJS

Trois services différents

Ici, nous devons démarrer trois services pour simuler respectivement l'application A, le serveur d'authentification SSO et l'application B

Parlons de la façon de mettre en œuvre l'authentification unique (SSO) basée sur Node

Le service avec le numéro de port 8383 voici le serveur d'authentification SSO, et le reste : 8686 et :8787 représentent respectivement l'application A et l'application B.

En fait, les codes de l'application A et de l'application B sont presque les mêmes. Comme le montre la figure ci-dessus, nous pouvons définir différents ports et noms d'application en passant des paramètres.

Jetons d'abord un coup d'œil à l'effet

Parlons de la façon de mettre en œuvre l'authentification unique (SSO) basée sur Node

La première visite accède à la page de connexion

Appliquez A pour déterminer l'état de connexion, accédez au serveur d'authentification SSO

Appliquez 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}`)

})
Copier après la connexion

Serveur d'authentification pour déterminer l'état de connexion, afficher la page de connexion

Serveur d'authentification SSO

La structure de répertoires du serveur d'authentification est la suivante Il gère principalement deux fonctions, l'une est la logique de connexion et l'autre est la vérification ultérieure de la validité du jeton, qui sont gérées respectivement par le routage login.js et check-token.js

Parlons de la façon de mettre en œuvre l'authentification unique (SSO) basée sur Node

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`)
})
Copier après la connexion

Tout à l'heure, nous avons commencé à partir de l'application A, nous passons à http://localhost:8383/login?redirectUrl=localhost:8686 pour voir la logique de connexion
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
Copier après la connexion

Page de connexion

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>统一登录</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>
Copier après la connexion

Vérifiez les informations de l'utilisateur et créer un jeton

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>登录成功!</h1>"
       }
   }else{
       ctx.response.body={
           error:1,
           msg:&#39;用户名或密码错误&#39;
       }
   }
})
Copier après la connexion

transporte le jeton du serveur d'authentification et revient à l'application A

La vérification du jeton renvoie la ressource

L'application 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}`)

})
Copier après la connexion

traite le jeton de vérification dans le SSO correspondant La logique de la carte
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
Copier après la connexion

Auth/service/index.js

module.exports={
    isTokenVailid: function(token){
      if(token && token===&#39;passport&#39;){
          return true
      }
      return false
    }
}
Copier après la connexion

Maintenant, l'utilisateur peut accéder à l'application A normalement et les informations de connexion de l'utilisateur sont disponibles sur le serveur SSO et le serveur d'application 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}`)

})
Copier après la connexion

从认证服务器携带令牌跳转回应用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>登录成功!</h1>"
    }
  }else{
    //...渲染登录页面
  }
})
//..
Copier après la connexion

令牌校验 返回资源

这里的逻辑和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}`)

})
Copier après la connexion

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

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

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

作者:YoYo君

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:juejin.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!