zoukankan      html  css  js  c++  java
  • Node.js 从零开发 web server博客项目[登录]

    web server博客项目

    1. Node.js 从零开发 web server博客项目[项目介绍]
    2. Node.js 从零开发 web server博客项目[接口]
    3. Node.js 从零开发 web server博客项目[数据存储]
    4. Node.js 从零开发 web server博客项目[登录]
    5. Node.js 从零开发 web server博客项目[日志]
    6. Node.js 从零开发 web server博客项目[安全]
    7. Node.js 从零开发 web server博客项目[express重构博客项目]
    8. Node.js 从零开发 web server博客项目[koa2重构博客项目]
    9. Node.js 从零开发 web server博客项目[上线与配置]

    cookie

    • 什么是 cookie

    1. 存储在浏览器的一段字符串 ( 最大5kb )
    2. 跨域不共享
    3. 格式如 k1=v1; k2=v2; k3=v3; 因此可以存储结构化数据
    4. 每次发送 http 请求 , 会将请求域的 cookie 一起发送给 server
    5. server 可以修改 cookie 并返回给浏览器
    6. 浏览器中也可以通过 JavaScript 修改 cookie ( 有限制 )
    • JavaScript 操作 cookie , 浏览器中查看 cookie

    • 客户端查看 cookie , 三种方式

      • Network ->Reques Headers ->Cookie
      • Application -> Cookie
      • document.cookie
    • JavaScript 查看修改 cookie ( 有限制 )

      • document.cookie = 'foo=bar;' 只能增加 cookie, 不能删除
    • server 端操作 cookie , 实现登录验证

    app.js

      // 解析 cookie
      req.cookie = {}
      const cookieStr = req.headers.cookie || '' // k1=v1; k2=v2; k3=v3
      cookieStr.split(';').forEach(item => {
        if (!item) {
          return
        }
        const arr = item.split('=')
        const key = arr[0]
        const val = arr[1]
        req.cookie[key] = val
      })
      
      console.log('req.cookie is: ', req.cookie) 
      // 在客户端中 document.cookie = 'username: zhangsan;'
      // req.cookie is:  { username: 'zhangsan' }
    

    router/user.js

     // 登录
      if (method === 'GET' && path === '/api/user/login') {
        // const {
        //   username,
        //   password
        // } = req.body
    
        const { username, password } = req.query
        const result = login(username, password)
    
        // if (result) {
        //   return new SuccessModel(result)
        // } else {
        //   return new ErrorModel('登录失败')
        // }
    
        return result.then(data => {
          if (data.username) {
    
            // 操作 cookie
            res.setHeader('Set-Cookie', `username=${data.username}; path=/`)
    
            return new SuccessModel()
          }
          return new ErrorModel('登录失败')
        })
      }
    
      // 登录验证测试
      if (method === 'GET' && req.path === '/api/user/login-test') {
        if (req.cookie.username) {
          return Promise.resolve(
            new SuccessModel({
              username: req.cookie.username
            })
          )
        }
        return Promise.reject(
          new ErrorModel('登录失败')
        )
      }
    

    cookie做限制

    在客户端当你登录成功后 ( username=lisi ) -> 删除 cookie ( username=lisi ) -> 添加 cookie ( username=zhangsan ) 会发现 用张三的账号操作的李四账号
    router/user.js

    const {
      login
    } = require('../controller/user')
    const { SuccessModel, ErrorModel } = require('../model/resModel')
    
    // 获取 cookie 的过期时间
    const getCookieExpires = () => {
      const d = new Date()
      d.setTime(d.getTime() + (24 * 60 * 60 * 1000))
      return d.toGMTString() // Mon, 08 Jul 2019 05:27:33 GMT
    }
    
    handleUserRouter = (req, res) => {
      const {
        method,
        path
      } = req
    
      // 登录
      if (method === 'GET' && path === '/api/user/login') {
        // const {
        //   username,
        //   password
        // } = req.body
    
        const { username, password } = req.query
        const result = login(username, password)
    
        // if (result) {
        //   return new SuccessModel(result)
        // } else {
        //   return new ErrorModel('登录失败')
        // }
    
        return result.then(data => {
          if (data.username) {
    
            // 操作 cookie
            res.setHeader('Set-Cookie', `username=${data.username}; path=/; httpOnly; expires=${getCookieExpires()}`)
            // path=/ 所有网站都生效
            // httpOnly 只允许前端更改 
            // expires 过期时间
    
            return new SuccessModel()
          }
          return new ErrorModel('登录失败')
        })
      }
    
      // 登录验证测试
      if (method === 'GET' && req.path === '/api/user/login-test') {
        if (req.cookie.username) {
          return Promise.resolve(
            new SuccessModel({
              username: req.cookie.username
            })
          )
        }
        return Promise.reject(
          new ErrorModel('登录失败')
        )
      }
    }
    
    module.exports = handleUserRouter
    

    session

    • 上一节的问题 : 会暴露 username , 很危险
    • 如何解决 : cookie 中存储 userid , server 端对应 username
    • 解决方案 : session , 即 sever 端存储用户信息
      app.js
    // session 数据
    const SESSION_DATA = {}
    
    // 获取 cookie 的过期时间
    const getCookieExpires = () => {
      const d = new Date()
      d.setTime(d.getTime() + (24 * 60 * 60 * 1000))
      return d.toGMTString() // Mon, 08 Jul 2019 05:27:33 GMT
    }
    ...
      // 解析 session
      let needSetCookie = false
      let userId = req.cookie.userid
      if (userId) {
        if(!SESSION_DATA[userId]) {
          SESSION_DATA[userId] = {}
        }
      } else {
        needSetCookie = true
        userId = `${Date.now()}_${Math.random()}`
        SESSION_DATA[userId] = {}
      }
    
      req.session = SESSION_DATA[userId]
      ...
        // 处理 user 路由
        const userResult = handleUserRouter(req, res)
        if (userResult) {
          userResult.then(userData => {
    
            if (needSetCookie) {
              res.setHeader('Set-Cookie', `userid=${userId}; path=/; httpOnly; expires=${getCookieExpires()}`)
            }
    
            res.end(
              JSON.stringify(userData)
            )
          })
          return
        }
    

    router/user.js

        return result.then(data => {
          if (data.username) {
    
            // 操作 cookie
            res.setHeader('Set-Cookie', `userid=${data.userId}; path=/; httpOnly; expires=${getCookieExpires()}`)
            // path=/ 所有网站都生效
            // httpOnly 只允许前端更改 
            // expires 过期时间
            
            // 设置 session
            req.session.username = data.username
            req.session.realname = data.realname
            console.log('req.session is : ', req.session)        
    
            return new SuccessModel(req.session)
          }
          return new ErrorModel('登录失败')
        })
      }
    
      // 登录验证测试
      if (method === 'GET' && req.path === '/api/user/login-test') {
        if (req.session.username) {
          return Promise.resolve(
            new SuccessModel({
              username: req.session
            })
          )
        }
        return Promise.reject(
          new ErrorModel('尚未登陆')
        )
      }
    

    session 存入 redis

    • cnpm i redis -S
    • conf/db.js
    const env = process.env.NODE_ENV // 环境参数
    
    // 配置
    let MYSQL_CONF
    let REDIS_CONF
    
    if (env === 'dev') {
      // MySQL
      MYSQL_CONF = {
        host: 'localhost',
        user: 'root',
        password: 'root',
        port: '3306',
        database: 'myblog'
      }
    
      // redis
      REDIS_CONF = {
        port: 6379,
        host: '127.0.0.1'
      }
    }
    
    if (env === 'production') {
      MYSQL_CONF = {
        host: 'localhost',
        user: 'root',
        password: 'root',
        port: '3306',
        database: 'myblog'
      }
    
      // redis
      REDIS_CONF = {
        port: 6379,
        host: '127.0.0.1'
      }
    }
    
    module.exports = { MYSQL_CONF, REDIS_CONF }
    
    • 创建db/redis.js
    const redis = require('redis')
    const { REDIS_CONF } = require('../conf/db.js')
    
    // 创建客户端
    const redisClient = redis.createClient(REDIS_CONF.port, REDIS_CONF.host)
    redisClient.on('error', err => {
        console.error(err)
    })
    
    function set(key, val) {
        if (typeof val === 'object') {
            val = JSON.stringify(val)
        }
        redisClient.set(key, val, redis.print)
    }
    
    function get(key) {
        const promise = new Promise((resolve, reject) => {
            redisClient.get(key, (err, val) => {
                if (err) {
                    reject(err)
                    return
                }
                if (val == null) {
                    resolve(null)
                    return
                }
    
                try {
                    resolve(
                        JSON.parse(val)
                    )
                } catch (ex) {
                    resolve(val)
                }
            })
        })
        return promise
    }
    
    module.exports = {
        set,
        get
    }
    
    • router/blogs.js
    const {
      getList,
      getDetail,
      newBlog,
      updateBlog,
      delBlog
    } = require('../controller/blog')
    const { SuccessModel, ErrorModel } = require('../model/resModel')
    
    // 统一的登录验证函数
    const loginCheck = (req) => {
      if (!req.session.username) {
          return Promise.resolve(
              new ErrorModel('尚未登录')
          )
      }
    }
    
    const handleBlogRouter = (req, res) => {
      const method = req.method // GET POST
      const id = req.query.id
    
      // 获取博客列表
      if (method === 'GET' && req.path === '/api/blog/list') {
          let author = req.query.author || ''
          const keyword = req.query.keyword || ''
          // const listData = getList(author, keyword)
          // return new SuccessModel(listData)
    
          if (req.query.isadmin) {
              // 管理员界面
              const loginCheckResult = loginCheck(req)
              if (loginCheckResult) {
                  // 未登录
                  return loginCheckResult
              }
              // 强制查询自己的博客
              author = req.session.username
          }
    
          const result = getList(author, keyword)
          return result.then(listData => {
              return new SuccessModel(listData)
          })
      }
    
      // 获取博客详情
      if (method === 'GET' && req.path === '/api/blog/detail') {
          // const data = getDetail(id)
          // return new SuccessModel(data)
          const result = getDetail(id)
          return result.then(data => {
              return new SuccessModel(data)
          })
      }
    
      // 新建一篇博客
      if (method === 'POST' && req.path === '/api/blog/new') {
          // const data = newBlog(req.body)
          // return new SuccessModel(data)
    
          const loginCheckResult = loginCheck(req)
          if (loginCheckResult) {
              // 未登录
              return loginCheckResult
          }
    
          req.body.author = req.session.username
          const result = newBlog(req.body)
          return result.then(data => {
              return new SuccessModel(data)
          })
      }
    
      // 更新一篇博客
      if (method === 'POST' && req.path === '/api/blog/update') {
          const loginCheckResult = loginCheck(req)
          if (loginCheckResult) {
              // 未登录
              return loginCheckResult
          }
    
          const result = updateBlog(id, req.body)
          return result.then(val => {
              if (val) {
                  return new SuccessModel()
              } else {
                  return new ErrorModel('更新博客失败')
              }
          })
      }
    
      // 删除一篇博客
      if (method === 'POST' && req.path === '/api/blog/del') {
          const loginCheckResult = loginCheck(req)
          if (loginCheckResult) {
              // 未登录
              return loginCheckResult
          }
    
          const author = req.session.username
          const result = delBlog(id, author)
          return result.then(val => {
              if (val) {
                  return new SuccessModel()
              } else {
                  return new ErrorModel('删除博客失败')
              }
          })
      }
    }
    
    module.exports = handleBlogRouter
    
    • router/user.js
    const { login } = require('../controller/user')
    const { SuccessModel, ErrorModel } = require('../model/resModel')
    const { set } = require('../db/redis')
    
    const handleUserRouter = (req, res) => {
        const method = req.method // GET POST
    
        // 登录
        if (method === 'POST' && req.path === '/api/user/login') {
            const { username, password } = req.body
            // const { username, password } = req.query
            const result = login(username, password)
            return result.then(data => {
                if (data.username) {
                    // 设置 session
                    req.session.username = data.username
                    req.session.realname = data.realname
                    // 同步到 redis
                    // console.log(req.session)
                    set(req.sessionId, req.session)
    
                    return new SuccessModel()
                }
                return new ErrorModel('登录失败')
            })
        }
    }
    
    module.exports = handleUserRouter
    
    
    • 和前端联调

    • 登录功能依赖 cookie , 必须用浏览器来联调
    • cookie 跨域不共享 , 前端和 server 端必须同域
    • 需要用到 nginx 做代理 , 让前后端端同域
  • 相关阅读:
    testNG 注解使用说明
    jenkins 生成HTML报表,邮件推送
    Jenkins 邮件发送
    Jenkins+SVN+Maven+testNG管理项目
    SVN 安装教程
    大数据笔记(十一)——倒排索引
    大数据笔记(十)——Shuffle与MapReduce编程案例(A)
    大数据笔记(九)——Mapreduce的高级特性(B)
    大数据笔记(八)——Mapreduce的高级特性(A)
    大数据笔记(七)——Mapreduce程序的开发
  • 原文地址:https://www.cnblogs.com/izhaong/p/12154268.html
Copyright © 2011-2022 走看看