前后端的身份认证 | 青训营笔记

67 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第20天

Web开发模式

目前主流的Web开发模式有两种:

  • 基于服务端渲染的传统Web开发模式

优点:

  1. 前端耗时少。因为服务器端负责动态生成 HTML内容,浏览器只需要直接渲染页面即可。尤其是移动端,更省电。
  2. 有利于SEO。因为服务器端响应的是完整的 HTML页面内容,所以爬虫更容易爬取获得信息,更有利于 SEO。

缺点:

  1. 占用服务器端资源。即服务器端完成 HTML 页面内容的拼接,如果请求较多,会对服务器造成一定的访问压力。
  2. 不利于前后端分离,开发效率低。使用服务器端渲染,则无法进行分工合作,尤其对于前端复杂度高的项目,不利于项目高效开发。
  • 基于前后端分离的新型Web开发模式

优点:

  1. 开发体验好。前端专注于UI页面的开发,后端专注于api的开发,且前端有更多的选择性。
  2. 用户体验好。Aiax 技术的广泛应用,极大的提高了用户的体验,可以轻松实现页面的局部刷新。
  3. 减轻了服务器端的渲染压力。因为页面最终是在每个用户的浏览器中生成的。

缺点:

  1. 不利于 SEO。因为完整的 HTML页面需要在客户端动态拼接完成,所以爬虫对无法爬取页面的有效信息。(解决方案:利用VueReact等前端框架的 SSR (server side render) 技术能够很好的解决 SEO 问题!)

如何选择开发模式?

不谈业务场景而盲目选择使用何种开发模式都是耍流氓

  • 企业级网站,主要功能是展示而没有复杂的交互,并且需要良好的 SEO,则这时我们就需要使用服务器端渲染。
  • 后台管理项目,交互性比较强,不需要考虑SEO,那么就可以使用前后端分离的开发模式。
  • 另外,具体使用何种开发模式并不是绝对的,为了同时兼顾了首页的渲染速度和前后端分离的开发效率,一些网站采用了首屏服务器端渲染+其他页面前后端分离的开发模式

鉴权

身份认证(Authentication),是指通过一定手段,完成对用户身份的确认

对于服务端渲染和前后端分离这两种开发模式来说,分别有着不同的身份认证方案:

  • 服务端染推荐使用 Session 认证机制
  • 前后端分离推荐使用JWT 认证机制

Session认证机制

HTTP协议的无状态性:客户端的每次HTTP请求都是独立的

如何突破无状态的限制?Cookie

Cookie

Cookie是存储在用户浏览器中的一段不超过4KB的字符串。由一个名称、一个值和其他几个用于控制Cookie有效期、安全性、适用范围的可选属性组成。

自动发送、域名独立、过期时限、4KB限制Cookie不具有安全性,可以被伪造

image.png

image.png

在Express中使用Session认证

npm i express-session
const express = require('express')
const app = express()
const session =require('express-session')
//配置session中间件
append.use(session({
    secret:'keyboard cat',
    resave:false,
    saveUninitialized:true
}))

//向session存数据
app.get('/api/login', (req, res) => {
    if(req.body.username!=='admin' || req.body.password!=='123456'){
        return res.send({
            status: 0,
            msg: '登陆失败',
        })
    }
    res.session.user=req.body //将用户的信息存储到session
    res.session.user=true //将用户的登录状态存储到session
    res.send({
        status: 0,
        msg: '登陆成功',
    })
})

//向session取数据
app.get('/api/getName', (req, res) => {
    if (req.session.isLogin) {
        return res.send({
            status: 1,
            msg: 'Failed',
        })
    }
    res.send({
        status: 0,
        msg: 'success',
        username: req.session.user.username
    })
})

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

JWT认证机制

Session 认证机制需要配合 Cookie 才能实现。由于 Cookie 默认不支持跨域访问,所以,当涉及到前端跨域请求后端按口的时候,需要做很多额外的配置,才能实现跨域 Session 认证。

  • 当前端请求后端接口不存在跨域问题的时候,推荐使用 Session 身份认证机制
  • 当前端需要跨域请求后端接口的时候,不推荐使用 Session 身份认证机制,推荐使用JWT 认证机制。

JWT(JSON Web Token)是目前最流行的跨域认证解决方案

Header.Payload.Signature
//Payload才是真正的用户信息,其余是安全性相关的部分,只是为了保证Token安全性。

image.png

客户端收到服务器返回的JWT 之后,通常会将它储存在 localStoragesessionStorage 中此后,客户端每次与服务器通信,都要带上这个JWT 的字符串,从而进行身份认证。推荐的做法是把JWT 放在 HTTP请求头的 Authorization 字段中。

在Express中使用JWT认证

npm install jsonwebtoken express-jwt
//jsonwebtoken 用于生成JWT字符串
//express-jwt 用于将JWT字符串解析还原为JSON对象
const express = require('express')
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')

const app = express()
//定义secret密钥
const secretKey = 'ahisnaklqqiox=0--091>/,/'

//注册将JWT字符串解析为JSON对象的中间件
app.use(expressJWT({ secret: secretKey }).unless({ path: [/^\/api\//] }))//可以通过中间件,把解析出来的user挂载到app
app.use(express.urlencoded({ extended: false }))
//定义中间件捕获错误
app.use((err, req, res, next) => {
    if (err.name === 'UnauthorizedError') {
        return res.send({
            status: 401,
            msg: '无效的Token',

        })
    }
    res.send({
        status: 500,
        msg: '未知错误',
    })
})

app.post('/api/login', (req, res) => {
    const userInfo = req.body
    if (req.body.username !== 'admin' || req.body.password !== '123456') {
        return res.send({
            status: 0,
            msg: '登陆失败',
        })
    }
    const tokenStr = jwt.sign({ username: userInfo.username }, secretKey, { expiresIn: '60s' })
    res.send({
        status: 200,
        msg: '登陆成功',
        token: tokenStr
    })
})

app.get('/admin/getInfo', (req, res) => {
    const userInfo = req.body
    console.log(req.user)
    const tokenStr = jwt.sign({ username: userInfo.username }, secretKey, { expiresIn: '60s' })
    res.send({
        status: 200,
        msg: '登陆成功',
        data: req.user
    })
})

app.listen(82, () => {
    console.log('express server running at http://127.0.0.1:82')
})