node+vue,实战demo

该项目功能

页面内容:
1.注册
2.登录
3.退登、修改密码/修改昵称
4.列表页(可新增,删除,修改,查询)
5.子账号页(可增,删,改,查)==》仅对超级管理员开放
用到技术:
1.反向代理
2.数据库建表,请求mysql数据库
3.进行node模块化开发,分模块处理请求()
具体功能:
1.生成验证码
2.token:前端用uuid加密生成token实现验证码一一对应
3.用redis(可支持:键/值对,哈希表,链表,集合)缓存token以及用户信息,并设置过期时效
redis使用:
1.先跑起来redis服务:到redis文件目录下:redis-server redis.window.conf
2.在打开可视化工具(redis desktop manager)查看

**前端代码 https://github.com/miaSlady/modular_html.git **
**node后台代码 https://github.com/miaSlady/modular.git **

具体实现过程

数据库部分

数据库搭建

1.建立读书计划表





2.建立用户列表


后台node部分

1.安装以下依赖

 "koa": "^2.12.0",
  "koa-bodyparser": "^4.3.0",
  "koa-router": "^8.0.8",
  "koa-session": "^6.0.0",
  "koa2-cors": "^2.0.6",
  "mysql": "^2.18.1",
  "redis": "^3.0.2",
  "svg-captcha": "^1.4.0"
  "crypto": "^1.0.1"

koa 写后台;mysql连接服务器;svg-captcha生成二维码;redis写token校验;crypto配合redis使用
2.配置跨域
1.在公共方法utils里头新建中间件koa-cors.js
注:1.allowDomain允许访问的域名
    2.对options进行处理:method = OPTIONS 时, 属于预检(复杂请求), 当为预检时, 可以直接返回空响应体   
module.exports = async (ctx, next) => {
  const allowDomain=[
    "http://172.16.0.128:1818/",
    "http://172.16.0.25:1818/",
    "http://localhost:1818/",
  ]
  if(allowDomain.includes(ctx.header.referer) || allowDomain.includes(ctx.header.origin + '/')){
    // ctx.set('Access-Control-Allow-Origin', '*'); //允许来自所有域名请求(不携带cookie请求可以用*,如果有携带cookie请求必须指定域名)
    ctx.set("Access-Control-Allow-Origin", ctx.header.origin); // 只允许指定域名http://localhost:8080的请求

    ctx.set('Access-Control-Allow-Methods', 'OPTIONS, GET, PUT, POST, DELETE'); // 设置所允许的HTTP请求方法

    ctx.set('Access-Control-Allow-Headers', 'x-requested-with, accept, origin, content-type,token'); // 字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段.
    // 服务器收到请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。

    ctx.set('Content-Type', 'application/json;charset=utf-8'); // Content-Type表示具体请求中的媒体类型信息

    ctx.set('Access-Control-Allow-Credentials', true); // 该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。
    // 当设置成允许请求携带cookie时,需要保证"Access-Control-Allow-Origin"是服务器有的域名,而不能是"*";

    ctx.set('Access-Control-Max-Age', 300); // 该字段可选,用来指定本次预检请求的有效期,单位为秒。
    // 当请求方法是PUT或DELETE等特殊方法或者Content-Type字段的类型是application/json时,服务器会提前发送一次请求进行验证
    // 下面的的设置只本次验证的有效时间,即在该时间段内服务端可以不用进行验证

    ctx.set('Access-Control-Expose-Headers', 'myData'); // 需要获取其他字段时,使用Access-Control-Expose-Headers,
    // getResponseHeader('myData')可以返回我们所需的值
    /*
    CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:
        Cache-Control、
        Content-Language、
        Content-Type、
        Expires、
        Last-Modified、
        Pragma。
    */

    /* 解决OPTIONS请求 */
    if (ctx.request.method == 'OPTIONS') {
        ctx.status = 200;
    } else {
        await next();
    }
  }else{
    ctx.throw(500);
  }

};
3.连接服务器
1.在middlewares文件下创建databases.js。
const mysql=require("mysql");

const db=mysql.createConnection({
  host:"localhost",
  port:3306,
  user:'root',
  password:'123456',
  database:'modular',//数据库名
  multipleStatements: true,//可执行多条sql语句
});

const connectDb=function(){
  db.connect(err=>{
    if(err) throw err;
    console.log("连接成功");
  })
}

module.exports={
  db,
  connectDb
}

> 暴露出db用于sql查询,connectDb在app.js中进行全局引入

2.在app.js添加

const {connectDb}=require('./middlewares/databases')
connectDb();

3.分模块管理路由

注:
(1)app.js写全局注入
(2)controller.js里头写接口封装
(3)controllers 里头写各个模块调用的接口封装
1.在app.js
// 导入controller middleware:
const controller = require('./controller');//对路由分模块处理
controller(app);
2.在controller.js(具体写法查看github项目:https://github.com/miaSlady/modular.git)
module.exports = function (app,dir) {
    let
        controllers_dir = dir || 'controllers',
        router = require('koa-router')();
    addControllers(router, controllers_dir);
    //token拦截中间件
    app.use(async (ctx,next)=>{
      ...
    })
    app.use(router.routes());
    
    // return router.routes();
};
3.token拦截中间件配置
配置逻辑:
(1)除获取验证码接口,其他接口需携带token
(2)注册、登录接口无需校验,直接去调用接口await next(),会在接口处去校验token、code是否匹配
(3)其他接口需校验token判断code上传是否正确(先判断token是否存在,再获取用户信息),在ctx里头存用户信息,便于其他接口调用ctx.state.user=user;
 //token拦截中间件
    app.use(async (ctx,next)=>{
        var url=ctx.request.URL.pathname;
        if(url!='/login/code'){//非获取验证码(需携带token)
            const {token}=ctx.request.header;
            let response={
                code:401,
                success:false,
                msg:'登录失效,请重新登录'
            };
            if(token){
                ctx.state.token=token;
                return new Promise((res,req)=>{
                    console.log(3)
                    if(url!='/login/signUp' && url!='/login/login'){//不是注册且不是登录进行token校验
                        client.hexists('codeUserInfo',token, (err, data)=>{//判断token是否失效
                            console.log(4,data)
                            if(data){//token未失效获取用户信息  
                                client.hgetall('codeUserInfo', (err, obj)=>{
                                    let user=obj[token];
                                    user=JSON.parse(user);
                                    ctx.state.user=user;
                                    res(true)
                                });
                            }else{//token失效
                                res(false)
                            }
                        }) 
                    }else{//注册或登录无需进行token校验
                        res(true)
                    }
                }).then(async(bool)=>{
                    if(bool){
                        console.log('调接口去');
                        await next();
                    }else{
                        ctx.response.body=response 
                    }
                })
            }else{//没携带token
                ctx.response.body=response
            }
        }else{//获取验证码code
            await next();
        }
    })

4.用基于uuid生成随机数用于token
const uuidv4 = require('uuid/v4');
token=uuidv4()
5.生成验证码
const svgCaptcha = require('svg-captcha');//生成验证码
let captcha = svgCaptcha.create({
    size:4,//验证码个数
    fontSize:50,//验证码字体大小
    width:135,//宽度
    heigth:47,//高度
    background:'#cc9966'//背景大小
  });

  ctx.response.type="image/svg+xml";//设置返回的数据格式
  let token=uuidv4(),obj={};
  obj[token]=captcha.text;
  client.hmset('codeVerify', obj, redis.print);
  client.expire('codeVerify',180);//3分钟自动过期
6.redis存取(我用的hash)
var redis = require('redis')//中间件:缓存哈希表(1.注册/登录判断code是否输入正确;2.登录用token对应用户信息)
var client = redis.createClient(6379, '127.0.0.1')
client.hmset('codeUserInfo', userInfo , redis.print);//存
client.expire('codeUserInfo',24*60*60);//1天自动过期(设置过期时间)
client.hgetall('codeVerify', (err, obj)=>{//获取
      console.log('我获取到了',obj);
      if(err){
        rej(err)
      }else{
        res(obj)
      }
    })
client.hexists('codeUserInfo',token, (err, data)=>{//查存在
      if(data){
        client.hdel('codeUserInfo', token,async (err,data)=>{//删除
          res()
        });
      }else{
        res()
      }
    });
7.登录逻辑
(1)调接口获取验证码,后台在验证码接口用uuid生成随机数作为token返给前端,存在redis里头的codeVerify对象(自己可命名,设置时效3分钟,可自己定),键为token(这串uuid加密的随机数),值为code的值
(2)前端登录/注册请求头带token请求接口,后台去redis里头的codeVerify校验code是否正确,若正确获取用户信息,存在redis里头的codeUserInfo对象(键token,值用户信息,设置时效3天,可自己定),以后每个接口请求头携带token。
posted @ 2020-06-15 17:53  米牙  阅读(482)  评论(0编辑  收藏  举报