什么是单点登录?怎么使用Nodejs实现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())//处理登录相关的逻辑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>统一登录</h1> <form method="post"> <div>用户名: <input type="text" name="name"/></div> <div>密码 <input type="text" name="password" /></div> <div><input type="submit" value=https://www.52zixue.com/zhanzhang/webqd/js/04/23/70557/'登录'>

校验用户信息, 创建令牌
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、创建用户雨SSO认证中心的会话(这时会把信息保存到cookie中), 同时创建授权令牌token const token="passport"; await ctx.cookies.set('token',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:'用户名或密码错误' } }})从认证服务器携带令牌跳转回应用A
令牌校验 返回资源
应用A
app.use(views(path.join(__dirname,'./views')),{ extension:'ejs' })//...const system=process.env.SERVER_NAMErouter.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 = https://www.52zixue.com/zhanzhang/webqd/js/04/23/70557/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('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||8888app.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

推荐阅读