ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

用户认证session与token中,前后端的交互方式

2022-05-06 01:35:06  阅读:146  来源: 互联网

标签:交互方式 express res app req token session


之前写过一篇关于用户认证的,不过是在理论方面https://www.cnblogs.com/sanhuamao/p/16027260.html。
最近去看了两个课程,里面涉及到了用户认证实战方面,自己写的时候遇到了许多问题,所以想写篇文章记录一下。

本文涉及技术栈有nodejs、express、fetch、xhr、localstorage。前置知识可参考我的笔记https://github.com/sanhuamao1/nodejs或https://gitee.com/chenxiangzhi/nodejs,下面的实例保存在目录04用户认证中。

用户认证方式

  • session方式的用户认证适合服务器端渲染的web开发模式,该模式有利于SEO,常见于主要功能是展示而没有复杂的交互的网站。
  • token方式的用户认证适合前后端分离的web开发模式,该模式不利于SEO,常见于交互强的后台管理系统。是跨域资源共享的解决方案。

01 session认证

过程:客户端发送用户信息给服务端——服务端验证通过后,将数据通过session保存起来,然后把对应的cookie返回给客户端——客户端再次发送请求时携带cookie——服务端对比cookie,验证用户。

要点

  • 该实例将页面及接口设置为同源,通过express.static()实现
  • 第三方中间件express-session
  • 共三个文件:app.js(服务器)、index.html(后台页)、login.html(登录页)

app.js

const express=require('express')
const app=express()
//解析请求体
app.use(express.urlencoded({ extended: false }))
app.use(express.json())
// 静态资源
app.use(express.static('public'))

//使用中间件express-session
const session=require('express-session')
app.use(
    session({
        secret: 'itheima',
        resave: false,
        saveUninitialized: true,
    })
)

// 涉及两个status——0成功,1失败
// 1 向session中存数据
app.post('/api/login',(req,res)=>{
    const username=req.body.username
    const password=req.body.password

    // 这里会交给数据库检查,判断有没有这个用户
    // 如果没有该用户,返回登陆失败;如果有,往下走
    // 这边就省略该过程了,默认都通过

    // 将用户信息保存在session中
    req.session.user=req.body
    req.session.islogin = true
    res.send({status:0,msg:'登陆成功!'})
})

// 2 从session中获取数据
app.get('/api/getusername',(req,res)=>{
    if(!req.session.islogin){
        return res.send({status:1,msg:'请重新登陆!'})
    }
    res.send({status:0,msg:'获取成功',username:req.session.user.username})
})

// 3 清空session
app.post('/api/logout',(req,res)=>{
    req.session.destroy()
    res.send({
        status:0,
        msg:'已退出!'
    })
})

app.listen(8080,()=>{
    console.log('你的服务器运行在:http://localhost:8080')
})

讲个小插曲:
上次跟着官网的推荐无脑加了个cookie: { secure: true },发现无法获取session。
原因在于,它要求使用https,而我使用的是http,所以为了安全考量它不会给我保存cookie
于是发送完请求后我去查看了下cookie,确实没有保存!
所以如果发送了没有带cookie的请求,服务端是无法从session中获取数据的

login.html

<h1>首页</h1>
<p id="ptxt"></p>
<button id="btn">退出登录</button>
<script>
  // 获取用户信息
  let p=document.getElementById('ptxt')
  // 一进来就调用下,判断有没有登陆过
  window.onload=function(){
    //因为是同源,所以可以省略前面的域名
    fetch('/api/getusername').then(res=>res.json()).then(res=>{
      if (res.status !== 0) {
          alert(res.msg)
          location.href = './login.html'
        } else {
          p.innerHTML='欢迎您:' + res.username
        }
    })
  }
	
  // 退出
  let btn=document.getElementById('btn')
  btn.addEventListener('click',()=>{
    fetch('/api/logout',{
      method:'POST'
    }).then(res=>res.json()).then(res=>{
      if (res.status === 0) {
          location.href = './login.html'
        }
    })
  })
</script>

login.html

<div>账号:<input id="username" /></div>
<div>密码:<input id="password" /></div>
<button id="submit">登陆</button>

<script>
  let submit=document.getElementById('submit')
  submit.addEventListener('click',(e)=>{
    let username=document.getElementById('username').value
    let password=document.getElementById('password').value

    fetch('/api/login',{
      method:'POST',
      //指明请求体的数据类型
      headers: {
        'Content-Type': 'application/json'
      },
      body:JSON.stringify({
        username,
        password
      })
    }).then(res=>res.json()).then(res=>{
      if (res.status === 0) {
        location.href = './index.html'
      } else {
        alert(res.msg)
      }
    })
  })

02 token认证

过程:客户端发送用户信息——服务端验证通过后返回token——客户端将返回的token保存在localstorage中——在此访问需要用户认证的网页时,通过头部的Authorization字段携带token发送请求——服务端验证通过后返回目标页面

要点

  • 使用vscode的扩展程序live serve快速给页面创建服务器(其他方式也可以,反正就是要让页面和接口不同源)
  • 第三方中间件jsonwebtoken(生成token)、express-jwt(解析token)、cors(跨域)
  • 注册拦截错误的中间件
  • 共三个文件:app.js(服务器)、index.html(后台页)、login.html(登录页)

app.js

const express=require('express')
const app=express()
const cors=require('cors')
app.use(cors())
app.use(express.json())

//1.定义密钥
const secretKey="mimayo~" 
//2.生成jwt
const enjwt=require('jsonwebtoken')
//3.还原jwt:会自动把解析出来的数据挂载到req.auth的属性上
const {expressjwt: jwt}=require('express-jwt')
app.use(
    jwt({
        secret:secretKey,
        algorithms: ["HS256"],
    }).unless({path:[/^\/api\//]})  //// 设置以/api/开头的不需要访问权限
)

// 1.认证加密
app.post('/api/login',(req,res)=>{
    const user=req.body
    let {username,password}=user

    // 01 当用户调用了登陆接口,需要对其用户信息进行验证,这里假设数据库只有下面这个账户
    if(username!=='admin'||password!=='123456'){
        return res.send({status:0,msg:'账号或密码错误'})
    }
    
    // 02 验证成功后对用户信息进行加密  (不建议携带密码)
    const tokenStr=enjwt.sign(
        {username},
        secretKey,
        {expiresIn:'10h'} //有效期,也可以把单位换成s(秒),以便进行token期限测试
    )
    
    // 03 返回加密后的token
    res.send({
        status:200,
        message:'登陆成功',
        token:tokenStr
    })
})

//2 验证用户
app.get('/admin/getInfo',(req,res)=>{
    // 通过req.auth可以获取到token解析后的信息
    if(req.auth){
        res.send({
            status:200,
            msg:'获取数据成功',
            data:req.auth
        })
    }else{
        res.send({
            status:200,
            msg:'没有权限!',
            data:req.auth
        })
    }
    
})

// 3 处理错误:解析token错误或过期(错误中间件写后面)
app.use((err,req,res,next)=>{
    if(err.name==='UnauthorizedError'){
        res.send({
            status:401,
            msg:'token已过期'
        })
    }
    res.send({
        status:500,
        msg:'请求错误'
    })
})

app.listen(8080,()=>{
    console.log('你的服务器运行在:http://localhost:8080')
})

login.html

<div>账号:<input id="username" /></div>
<div>密码:<input id="password" /></div>
<button id="submit">登陆</button>

<script>
  document.getElementById('submit').addEventListener('click',(e)=>{
    let username=document.getElementById('username').value
    let password=document.getElementById('password').value

    fetch('http://localhost:8080/api/login',{
      method:'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body:JSON.stringify({
        username,
        password
      })
    }).then(res=>res.json()).then(res=>{
      if (res.status === 200) {
        // 验证通过了,将token保存在localstorage中
        localStorage.setItem('token',res.token)
        location.href = `./index.html`
      } else {
        alert(res.msg)
      }
    })
  })
</script>

index.html

<h1>首页</h1>
<p id="ptxt"></p>
<button id="btn">退出登录</button>
<script>
  window.onload=function(){
    //1 拿到传过来的token
    let token=localStorage.getItem('token')
    if(!token){
      //2 当没有token时
      alert('您尚未登录,请登录后再执行此操作!')
      location.href = './login.html'
    }else{
      //3 当有token时,发送请求。由于fetch跨域起来很麻烦,所以我采用的是xhr
      let xhr=new XMLHttpRequest()
      xhr.open('GET','http://localhost:8080/admin/getInfo')
      xhr.setRequestHeader("Authorization", "bearer "+ token)
      xhr.send()
      xhr.onload=function(){
        let res=JSON.parse(this.response)
        if(res.status=='200'){
          document.getElementById('ptxt').innerHTML="欢迎您,"+res.data.username
        }else{
          alert(res.msg)
        }
      }
    }
  }

  let btn=document.getElementById('btn')
  btn.addEventListener('click',()=>{
    localStorage.clear()	//清空tokon
    location.href = './login.html'
  })
</script>

标签:交互方式,express,res,app,req,token,session
来源: https://www.cnblogs.com/sanhuamao/p/16227089.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有