How to implement single sign-on based on Node

12-28-2022

The editor in this article will introduce in detail how to implement single sign-on based on Node. The content is detailed, the steps are clear, and the details are handled properly. I hope this article on how to implement single sign-on based on Node can help you solve your doubts. Slowly deepen your thinking, let's learn new knowledge together.

What is single sign-on

With the increase of the company's business, there will inevitably be different systems, if each system needs to log in separately It will be very inconvenient.

Therefore, a solution such as single sign-on came into being. Authorize without logging in again.

For example, Xiao Ming logged in to Taobao today. If he did not log in, he would be asked to enter authentication information (username, password, etc.).

Single sign-on principle


SSO requires an independent authentication center, only an independent authentication center can accept the user's For security information such as user names and passwords, other systems do not provide login entries, and only accept indirect authorization from the authentication center. The whole process can be simply described by the above figure:

  • When the user logs in to access application A, application A finds that the user is not logged in, jumps to the SSO authentication center, and sends its own address As a parameter, it is convenient to call back

  • The SSO authentication center finds that the user has not logged in, and guides the user to the login page; the user fills in the user name and password to submit the login application; the SSO authentication center verifies the user information , create a session of the user rain SSO authentication center (the information will be saved in the cookie at this time), and create an authorization token token at the same time

  • sso authentication center jumps with the token Go to the original request address (application A)

  • Application A gets the token and goes to the SSO authentication center to verify whether it is valid, and if it returns a valid registration application A

  • Application A creates a session with the user, displays resources and maintains the user's login status

  • When the user accesses application B, it is found that the user is not logged in ( The SSO authentication server is not in the same domain as application A and application B, and cannot provide login state), jump to the SSO authentication center, and bring your own address and the cookie information of the previous session with the SSO authentication center

  • The SSO authentication center finds that the user has logged in, jumps back to the application B address, and attaches the token token

  • The same application B gets the token Go to the SSO authentication center to verify whether it is valid, if returnedBack to valid registration application B

  • Application B creates a session with the user, displays resources and maintains the user's login status

NodeJS demo

Three different services

Here we need to start three services to simulate application A and SSO respectively Authentication server and application B

jik0ynnuz5l6.jpg

The service with port number 8383 here is the SSO authentication server, and the rest: 8686 and :8787 represent application A and application B respectively.

In fact, the codes of application A and application B are almost the same. As shown in the figure above, we can set different ports and application names by passing parameters.

Look at the effect first


The first visit jumps to the login page

Application A judges the login status and jumps to the SSO authentication server

Application 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'); //template engine related configuration 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)=>{ //Judge the login status of application A through session let user=ctx.session.user if(user){ //... } Else //1. When the user logs in to access application A, application A finds that the user is not logged in (it should be because the server does not save the corresponding session) { Let token=ctx.query.token //There will be no token on the first login url if(!token) { //1. Jump to the SSO authentication server 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}`) })


The authentication server judges the login status and renders the login page

Authentication server SSO

The directory structure of the authentication server is as follows It mainly deals with two functions, one is the login logic, and the other is to verify the validity of the token later, which are processed by routing login.js and check-token.js respectively


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()) //Process login related logic router. use('/login', login. 

routes()) // logic to handle token validation 

router.use('/check_token', checkToken.routes()) 

app. use(router. routes()) app.listen(8383,()=>{ console.log(`app listen at 8383`) })


Just now we jumped from application A to http://localhost:8383/login?redirectUrl=localhost:8686 to see login Logic in
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'); //Judging the login status of application A from the cookie if(token && service.isTokenVailid(token)){ //. . . If you have logged in }else{ //2. The SSO authentication center finds that the user has not logged in, so the login page is rendered; await ctx.render('login.ejs',{ extension:'ejs' }) } }) //. . .

module.exports=router


login page

Auth/views/login.ejs


Verify user information, create token

Auth/routes/login.js


router.post('/',async (ctx)=>{ //2. The user fills in the user name and password to submit the login application; 

const body=ctx.request.body; 

const {name,password}=body; //2. The SSO authentication center verifies the user information, 

if(name==="admin" && password==="123456"){ //2. Create a user session with the SSO authentication center (the information will be saved in the cookie at this time), and create an authorization token token at the same time const token="passport";   await ctx.cookies.set('token',token,{     maxAge: 1000*60*60*24*30, httpOnly: true }) 

If(ctx.query.redirectUrl){ //3. The sso authentication center takes the token and jumps to the original request address (application A) ctx.redirect(`${ctx.protocol}://${ctx.query.redirectUrl}?token=${token}`) //The jumpback address is http://localhost:8686/?token=passport }else{ ctx.body="

Successful login!

" } }else{ ctx.response.body={ error: 1, msg: 'Username or password error' } } })



Jump back to application A with the token from the authentication server

The token verification returns Resource

Application 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 // At this time, application A is still not logged in, but there is a token on the url http://localhost:8686/?token=passport { Let token=ctx.query.token if(!token) { //...jump to the SSO login page } else //The logic here goes when jumping back to application A { //ajax request 4. Application A gets the token and goes to the SSO authentication center to verify whether it is valid. If it returns a valid registration application 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               }           //Register the session after passing the verification, and render the page //5. Application A creates a session with the user, displays resources and maintains the user's login status         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}`) })


The corresponding SSO logic for processing authentication tokens
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 } //When token is password if(service.isTokenVailid(token)){ result.error=0; result.userId='admin' } ctx.body=result the }) module.exports=router


Auth/service/index.js


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


So far, the user has been able to access application A normally, and both the SSO server and the application A server have the information that the user has logged in.

Access application B

Jump to SSO authentication server with cookie

Application B


//... router.get("/", async (ctx)=>{ 

let user=ctx.session.user 

if(user){ //... }else{ Let token=ctx.query.token //...  if(!token) { // There is also neither session nor token, jump to the SSO authentication server //6. When the user accesses application B, it is found that the user has not logged in (the SSO authentication server is not in the same domain as application A and application B, and cannot provide login status), jumps to the SSO authentication center, and compares his address with the previous and The cookie information of the SSO authentication center session is brought in ctx.redirect(`http://localhost:8383/login?redirectUrl=${ctx.host+ctx.originalUrl}`) } else { //. . . part of the verification token } } }) 

app. use(router. routes()) 

const port=process.env.PORT||8888  

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


Jump back to application B with the token from the authentication server

SSO authentication server, carry it when logging in again cookie, so the login page will not be requested again Auth/routes/login


//... router.get('/', async (ctx) => { 

const cookies=ctx.cookies; 

const token=cookies. get('token'); //7. The SSO authentication center finds that the user has logged in, jumps back to the application B address, and attaches the token token 

if(token && service.isTokenVailid(token)){ const redirectUrl=ctx.query.redirectUrl; if(redirectUrl){ //Jump back to application B with the token ctx.redirect(`${ctx.protocol}://${redirectUrl}?token=${token}`) }else{ ctx.body="

Successful login!

" } }else{ //...render the login page } }) //..




Copyright Description:No reproduction without permission。

Knowledge sharing community for developers。

Let more developers benefit from it。

Help developers share knowledge through the Internet。

Follow us