美文网首页
Node.js 开发web server博客项目[6]

Node.js 开发web server博客项目[6]

作者: Mark同学 | 来源:发表于2019-12-05 21:09 被阅读0次

    Mac 终端 oh-my-zsh 配置
    https://www.jianshu.com/p/64344229778a

    sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
    

    6-3 cookie用于登录验证

    1.

    blog-node
    ——app.js

      // 解析 query
      // ...
      // 解析 cookie
      req.cookie = {}
      const cookieStr = req.headers.cookie || ''
      cookieStr.split(';').forEach(item => {
        if(!item) {
          return
        }
        const arr = item.split('=');
        // 拼接 Cookie 会自动增加空格,用 trim 方法去掉
        const key = arr[0].trim();
        const val = arr[1].trim();
        req.cookie[key] = val;
      })
      // console.log('req.cookies is...', req.cookie);
    

    2.登录验证的测试

    如果 cookie 里面有 username,已登录

    blog-node
    ——src
    ————router
    ——————user.js

      // 登录
      // ...
      
      // 登录验证测试
      if (method === 'GET' && req.path === '/api/user/login-test') {
        if (req.cookie.username) {
          return Promise.resolve(
            new SuccessModel({
              username: req.cookie.username 
            })
          );
        } else {
          return Promise.resolve(new ErrorModel('尚未登录'));
        }
      }
    

    3.登录临时改为 GET,参数从 query 里面取

    演示在后端修改 Cookie

    blog-node
    ——src
    ————router
    ——————user.js

      // 登录
      if(method === 'GET' && 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) {
            
            // 操作 Cookie
            res.setHeader('Set-Cookie', `username=${data.username}; path=/`);
    
            return new SuccessModel();
          } else {
            return new ErrorModel('登录失败');
          }
        });
      }
    

    6-4&6-5 cookie做限制

    登录成功后,用户可以通过设置 cookie, 任意伪造其他用户

    1.限制前端修改 Cookie-httpOnly

    // 操作 Cookie
    res.setHeader('Set-Cookie', `username=${data.username}; path=/; httpOnly`);
    

    2.设置Cookie 过期时间

    blog-node
    ——src
    ————router
    ——————user.js

    // 获取 cookie 的过期时间
    const getCookieExpires = () => {
      const d = new Date();
      d.setTime(d.getTime() + (24 * 60 * 60 * 1000));
      console.log('d.toGMTString() is ', d.toGMTString());
      return d.toGMTString();
    };
    const handleUserRouter = (req, res) => {
      // ...
    
      // 操作 Cookie
      res.setHeader('Set-Cookie', `username=${data.username}; path=/; httpOnly; expires=${getCookieExpires()}`);
    
      // ...
    };
    

    6-6 session介绍

    cookie => userid

    const SESSTION_DATA = {}
    const getPostData = (req) => {...}
    // ...
      // 解析 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]
      getPostData(req).then(postData => {...}
    
        const blogResult = handleBlogRouter(req, res)
        if (blogResult) {
    
          if (needSetCookie) {
            res.setHeader('Set-Cookie', `userid=${userId}; path=/; httpOnly; expires=${getCookieExpires()}}`)
          }
    
          blogResult.then(blogData => {
            res.end(JSON.stringify(blogData))
          })
          return
        }
    
        const userResult = handleUserRouter(req, res)
        if (userResult) {
    
          if (needSetCookie) {
            res.setHeader('Set-Cookie', `userid=${userId}; path=/; httpOnly; expires=${getCookieExpires()}}`)
          }
    
          userResult.then(userData => {
            res.end(JSON.stringify(userData))
          })
          return
        }
    
      if (req.method === 'GET' && req.path === '/api/user/login') {
        // ...
        return result.then(data => {
          if (data.username) {
    
            req.session.username = data.username
            req.session.realname = data.realname
            console.log('req.session is...', req.session)
    
            //...
          } else {
            //...
          }
        })
      }
    
      if (req.method === 'GET' && req.path === '/api/user/login-test') {
        if (req.session.username) {
          return Promise.resolve(
            new SuccessModel({
              username: req.session
            })
          )
        } else {
          //...
        }
      }
    

    \color{red}{理解引用的赋值是理解 session 的关键}

    6-8 从 session 到 redis

    理论

    6-9 redis 介绍

    https://www.runoob.com/redis/redis-install.html

    brew install redis

    启动两个 terminal,运行:

    • redis-server
    • redis-cli
    - keys *
    - get 1574645202751_0.571581082859681
    - del 1574645202751_0.571581082859681
    

    6-11 nodejs 连接 redis

    npm i redis --save

    let REDIS_CONF
    //...
      REDIS_CONF = {
        port: 6379,
        host: '127.0.0.1'
      }
    
    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
    }
    

    app.js

    • 注释掉之前处理 session 逻辑
    // const SESSION_DATA = {}
      // let needSetCookie = false
      // let userId = req.cookie.userid
      // if (!userId) {
      //   needSetCookie = true
      //   userId = `${Date.now()}_${Math.random()}`
      //   SESSION_DATA[userId] = {}
      // } else {
      //   if (!SESSION_DATA[userId]) {
      //     SESSION_DATA[userId] = {}
      //   }
      // }
      // req.session = SESSION_DATA[userId]
    
    const { get, set } = require('./src/db/redis')
    //...
      // 解析 session (使用 redis)
      let needSetCookie = false
      let userId = req.cookie.userid
      if (!userId) {
        needSetCookie = true
        userId = `${Date.now()}_${Math.random()}`
        // 初始化 redis 中的 session 值
        set(userId, {})
      } 
      // 获取 session
      req.sessionId = userId
      get(req.sessionId).then(sessionData => {
        if (sessionData == null) {
          // 初始化 redis 中的 session 值
          set(req.sessionId, {})
          // 设置 session
          req.session = {}
        } else {
          req.session = sessionData
        }
        console.log('req.session ', req.session)
        return getPostData(req)
      })
    

    router
    ——user.js

    const { set } = require('../db/redis')
    //...
    
    set(req.sessionId, req.session)
    
      if (req.method === 'GET' && req.path === '/api/user/login-test') {
        if (req.session.username) {
          return Promise.resolve(
            new SuccessModel({
              session: req.session
            })
          )
        } else {
          return Promise.resolve(
            new ErrorModel('尚未登录')
          )
        }
      }
    

    blog.js

    // 统一的登录验证函数
    const loginCheck = (req) => {
      if (!req.session.username) {
        return Promise.resolve(
          new ErrorModel('尚未登录')
        )
      } 
    }
    //...
    const loginCheckResult = loginCheck(req)
    if (loginCheckResult) {
      return loginCheckResult
    }
    
    // 哪里需要登录信息
    req.session.username
    

    6-15 nginx 反向代理

    brew install nginx

    nano /usr/local/etc/nginx/nginx.conf

    3>worker_processes  4;
    36>
    server {
            listen       8080;
    #        location / {
    #            root   html;
    #            index  index.html index.htm;
    #        }
    
            location / {
                proxy_pass http://localhost:3000;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "Upgrade";
            }
    
            location /api/ {
                proxy_pass http://localhost:8000;
                proxy_set_header Host $host;
            }
    }
    

    nginx -t 【测试】

    nginx 【启动】

    nginx -s stop 【停止】

      if (req.method === 'GET' && req.path === '/api/blog/list') {
        let author = req.query.author
        const keyword = req.query.keyword
        // 管理员界面限制
        if (req.query.isadmin) {
          const loginCheckResult = loginCheck(req)
          if (loginCheckResult) {
            return loginCheckResult
          }
          author = req.session.username
        }
    
        const result = getList(author, keyword)
        return result.then(data => {
          return new SuccessModel(data)
        })
      }
    

    注意:对象的转换

    function set(key, val) {
        if (typeof val === 'object') {
            val = JSON.stringify(val)
        }
        redisClient.set(key, val, redis.print)
    }
    

    注意:userId 是保存在 request 里的

    req.sessionId = userId
    

    相关文章

      网友评论

          本文标题:Node.js 开发web server博客项目[6]

          本文链接:https://www.haomeiwen.com/subject/aepjgctx.html