koa2+mysql+vue實現用戶注冊、登錄、token驗證


說明:

  node.js提供接口,vue展現頁面,前后端分離,出於編輯器功能和編輯習慣,vue用HbuilderX,node.js用VScode。(PS:僅作為學習筆記,如有不當之處歡迎指出,在此先謝為敬~~~)

環境:

  首先需要有node.js環境,安裝教程 在這里,最好下載較新的版本,對es6、es7有更好的支持,再裝個 淘寶鏡像,完畢!

后台:

1、安裝mysql

  1.1、mysql下載地址

  解壓到安裝位置,修改環境變量,win10編輯環境變量很方便了,win7的話記得以 ; 分割開

  

  1.2、添加配置文件

  在mysql的bin目錄下,新建my.ini文件(如果沒有),打開my.ini文件,寫入以下配置內容

[mysqld]
# 設置3306端口
port=3306
# 設置mysql的安裝目錄
basedir=D:\\myInstalls\\mysql-8.0.11
# 設置mysql數據庫的數據的存放目錄
datadir=D:\\myInstalls\\mysql-8.0.11\\Data
# 允許最大連接數
max_connections=200
# 允許連接失敗的次數。這是為了防止有人從該主機試圖攻擊數據庫系統
max_connect_errors=10
# 服務端使用的字符集默認為UTF8
character-set-server=utf8
# 創建新表時將使用的默認存儲引擎
default-storage-engine=INNODB
# 默認使用“mysql_native_password”插件認證
default_authentication_plugin=mysql_native_password
[mysql]
# 設置mysql客戶端默認字符集
default-character-set=utf8
[client]
# 設置mysql客戶端連接服務端時默認使用的端口
port=3306
default-character-set=utf8

  1.3、安裝

  以管理員身份運行cmd,進入mysql的bin目錄下,不進入也行,因為我們已經配置了環境變量

  初始化數據庫,運行 mysqld --initialize --console,記住紅色框內的初始密碼

     

  安裝mysql服務,運行 mysqld --install [服務名] ,服務名可以不寫,安裝完畢 net start mysql 啟動mysql

  

  啟動成果,mysql停止指令 net stop mysql

  默認密碼太復雜,改個簡單的,首先運行 mysql -u root -p 進入mysql,密碼是剛才記住的初始密碼

  

  修改密碼指令:ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '新密碼'; 

  

  OK,mysql我們已經有了,接下來搭建koa2!

2、搭建koa2項目

  (你可以使用系統自帶的cmd窗口,也可以用編輯器自帶的。我這里用VScode的命令行終端,看起來特別虛浮~~~)

  2.1、我們不一步步搭建,采用koa2框架,並使用koa-generator生成項目,類似vue-cli

  安裝指令:cnpm install koa-generator -g

  

  2.2、在你的項目目錄下,運行 koa2 項目名,生成項目,如:koa2 paopao(泡泡是我的貓的名字~~~)

  

  成功,根據上面提示走~~~

  cd paopao 進入項目目錄

  cnpm install 安裝項目依賴

  cnpm start paopao 運行項目(cnpm是淘寶鏡像)

  有個報錯大概意思是這個包不再維護了,cnpm uninstall koa-onerror 卸載,重新裝最新的版本 cnpm install koa-onerror --save

  

  在瀏覽器輸入:localhost:3000,瀏覽器運行結果(左),項目結構(右)

      

3、實現API

  3.1、用sequelize來操作數據庫,同時安裝mysql、mysql2

  cnpm install sequelize mysql mysql2 --save

  

  所有安裝的依賴可以在package.json里查看:

  

  注意:我在使用時發現koa-static(處理靜態文件的中間件),默認3.0.0版本會報錯,於是更新成了最新版本

  使用cnpm install koa-static@5.0.0 --save更新,再查看package.json,版本變成了5.0.0即可

  3.2、連接數據庫

  在項目根目錄下建一個config文件夾,在該文件夾建一個js文件,取名db.js,用來配置數據庫連接

  config-->db.js

var Sequelize = require("sequelize")
var sequelize = new Sequelize('paopao','root','happy',{
    host:'localhost',
    dialect:'mysql',
    operatorsAliases:false,
    dialectOptions:{
        //字符集
        charset:'utf8mb4',
        collate:'utf8mb4_unicode_ci',
        supportBigNumbers: true,
        bigNumberStrings: true
    },
    pool:{
        max: 5,
        min: 0,
        acquire: 30000,
        idle: 10000
    },
    timezone: '+08:00'  //東八時區
});

module.exports = {
    sequelize
};

  paopao是我的數據庫表名,root數據庫用戶名,happy數據庫用戶密碼

  3.3、定義數據庫模型

  在根目錄建一個module文件夾,在module文件下面建一個user.js,用來定義數據模型,告訴sequelize怎么跟數據庫的數據一一對應

  module-->user.js

module.exports = function(sequelize,DataTypes){
    return sequelize.define(
        'user',
        {
            userId:{
                type: DataTypes.INTEGER,
                primaryKey: true,
                allowNull: true,
                autoIncrement: true
            },
            mobileNo:{
                type: DataTypes.STRING,
                allowNull: false,
                field: 'mobileNo'
            },
            password:{
                type: DataTypes.STRING,
                allowNull: false,
                field: 'password'
            }
        },
        {
            timestamps: false
        }
    );
}

  3.4、數據庫操作和功能處理

  controller-->user.js 添加以下代碼 

//引入db配置
const db = require('../config/db')

//引入sequelize對象
const Sequelize = db.sequelize

//引入數據表模型
const user = Sequelize.import('../module/user')
//自動創建表
user.sync({ force: false }); 

//數據庫操作類
class userModule {
    static async userRegist(data) {
        return await user.create({
            password: data.password,
            mobileNo: data.mobileNo
        })
    }

    static async getUserInfo(mobileNo) {
        return await user.findOne({
            where: {
                mobileNo
            }
        })
    }
}

  數據庫操作有了,接下來進行功能處理,還是在該文件添加

  controller-->user.js 里添加該userController 類,並將之exports出去

//功能處理
class userController {
    
}

module.exports = userController;

  用戶注冊:

  在 userController 類里添加用戶注冊邏輯

//注冊用戶
    static async create(ctx) {
        const req = ctx.request.body;
        if (req.mobileNo && req.password) {
            try {
                const query = await userModule.getUserInfo(req.mobileNo);
                if (query) {
                    ctx.response.status = 200;
                    ctx.body = {
                        code: -1,
                        desc: '用戶已存在'
                    }
                } else {
                    const param = {
                        password: req.password,
                        mobileNo: req.mobileNo,
                        userName: req.mobileNo
                    }
                    const data = await userModule.userRegist(param);

                    ctx.response.status = 200;
                    ctx.body = {
                        code: 0,
                        desc: '用戶注冊成功',
                        userInfo: {
                            mobileNo: req.mobileNo
                        }
                    }
                }

            } catch (error) {
                ctx.response.status = 416;
                ctx.body = {
                    code: -1,
                    desc: '參數不齊全'
                }
            }
        }
    }

  因為還要做登錄超時token驗證,用戶登錄成功還要返回token,為了生成token,我們需要安裝幾個中間件

  cnpm install jsonwebtoken --save  導入jwt模塊

  cnpm install koa-jwt --save  koa提供的jwt中間件

  在app.js里添加如下代碼:

  unless()表示里面的regist、login不做token驗證

const koajwt = require('koa-jwt')

// logger
app.use(async (ctx, next) => {
  return next().catch((err) => {
    if(err.status === 401){
      ctx.status = 401;
      ctx.body = {
        code: '-2000',
        desc: '登陸過期,請重新登陸'
      };
    }else{
      throw err;
    }
  })
})

app.use(koajwt({
  secret: '123456'
}).unless({
  path: [/^\/user\/regist/,/^\/user\/login/]
}))

  為了解析token,在public目錄下新建tool.js,加入解析token的代碼

const getToken = require('jsonwebtoken')

exports.verToken = function(token){
    return new Promise((resolve,rejece) => {
        const info = getToken.verify(token.split(' ')[1],"123456");
        resolve(info);
    })
}

  返回controller-->user.js,添加

//引入jwt做token驗證
const jwt = require('jsonwebtoken')

//解析token
const tools = require('../public/tool')

//統一設置token有效時間  為了方便觀察,設為10s
const expireTime = '10s'

  用戶登錄:

  之后就可以寫用戶登錄邏輯了

  controller-->user.js-->userController 類里添加

  通過 jwt.asign() 方法生成token,這里的123456跟app.js里的123456相同,就理解為一個秘鑰吧~~

//密碼登陸
    static async login(ctx) {
        const req = ctx.request.body;
        if (!req.mobileNo || !req.password) {
            return ctx.body = {
                code: '-1',
                msg: '用戶名或密碼不能為空'
            }
        } else {
            const data = await userModule.getUserInfo(req.mobileNo);
            if (data) {
                if (data.password === req.passWord) {
                    //生成token,驗證登錄有效期
                    const token = jwt.sign({
                        user: req.mobileNo,
                        passWord: req.password
                    }, '123456', { expiresIn: expireTime });
                    const info = {
                        createdAt: data.createdAt,
                        updatedAt: data.updatedAt,
                        mobileNo: data.mobileNo,
                        userId: data.userId
                    }
                    return ctx.body = {
                        code: '0',
                        token: token,
                        userInfo: JSON.stringify(info),
                        desc: '登陸成功'
                    }
                } else {
                    return ctx.body = {
                        code: '-1',
                        desc: '用戶密碼錯誤'
                    }
                }
            } else {
                return ctx.body = {
                    code: '-1',
                    desc: '該用戶尚未注冊'
                }
            }
        };
    }

  為了驗證token是否過期,我們再定義一個獲取用戶信息的邏輯,登陸10s后獲取用戶信息,驗證token是否過期

  獲取用戶信息:

  controller-->user.js-->userController 類里添加

//獲取用戶信息(除密碼外)
    static async getUserInfo(ctx){
        const req = ctx.request.body;
        const token = ctx.headers.authorization;
        if(token){
            try {
                const result = await tools.verToken(token);
                if (!req.mobileNo) {
                    return ctx.body = {
                        code: '-1',
                        desc: '參數錯誤'
                    }
                } else {
                    let data = await userModule.getUserInfo(req.mobileNo);
                    if (req.mobileNo == data.mobileNo) {
                        const info = {
                            createdAt: data.createdAt,
                            updatedAt: data.updatedAt,
                            mobileNo: data.mobileNo,
                            userId: data.userId
                        };
                        return ctx.body = {
                            code: '0',
                            userInfo: JSON.stringify(info),
                            desc: '獲取用戶信息成功'
                        }
                    }
                }
            } catch (error) {
                ctx.status = 401;
                return ctx.body = {
                    code: '-1',
                    desc: '登陸過期,請重新登陸'
                }
            }
        }else{
            ctx.status = 401;
            return ctx.body = {
                code: '-1',
                desc: '登陸過期,請重新登陸'
            }
        }
    }

  3.5、路由,即處理請求的url,使用koa-router

  不用重新導入,koa-generator已經幫我們導入了,直接使用

  在routes目錄下新建文件 user.js

  寫入以下代碼:

  routes-->user.js

const Router = require('koa-router');
const userController = require('../controller/user')

const router = new Router({
    prefix: '/user'
});

//用戶注冊
router.post('/regist',userController.create)

//密碼登陸
router.post('/login',userController.login)

//獲取用戶信息
router.post('/getUserInfo',userController.getUserInfo)

module.exports = router;

  然后在入口文件app.js引入

  

  使用

  

  完成這些以后,cnpm run dev 啟動項目(依賴nodemon,package.json里面有,這樣每次更改代碼以后不用手動重新啟動)

  啟動正常如下:

  

  如果有報錯,提示缺少這包那包的,不用着急!

  把根目錄下的node_modules目錄刪除

  檢查一遍package.json

  確認無誤后重新cnpm install

  再次啟動 cnpm run dev ~~~

  補充一點,如果想在其他端口啟動,在app.js里添加 app.listen(3333),修改為3333端口,自動熱刷新~~~蛋是此時接口仍然不可調試,因為存在跨域問題

  

  3.6、解決跨域,koa-cors

  koa同樣提供了解決跨域的依賴包

  cnpm install koa-cors --save

  在app.js添加:

  

  現在可以測試接口了,隨便寫個ajax或者使用postman,postman測試結果:

  注冊:

  

  登錄:

  

  查看數據庫結果(使用的是破解版Navicat圖形化數據庫管理工具):

  

  到此為止,API就完成了,最后一步,驗證token過期有沒有效果

4、結合VUE驗證token

  寫到太晚了,想起來今天還沒給泡泡鏟屎,VUE就不寫那么詳細了,有空再補上   ~.~

  我就貼一下代碼和驗證結果

  vue項目里,在接口文件里:

import axios from 'axios';
import qs from 'qs';
import route from '../router';
import {
    message
} from 'ant-design-vue'

axios.interceptors.request.use(function(config) {
    // 處理請求參數
    config.data = qs.stringify(config.data)

    //將token寫入請求頭
    if (window.localStorage.getItem('token')) {
        config.headers.Authorization = `Bearer ${window.localStorage.getItem('token')}`;
    }

    return config;
}, function(error) {
    // 對請求錯誤做些什么
    return Promise.reject(error);
});

axios.interceptors.response.use(
    response => {
        return response
    },
    error => {
        if (error.response) {
            switch (error.response.status) {
                case 401:
                    message.error("登錄過期,請重新登錄!", ()=>{
                        window.localStorage.removeItem("token"); //可能是token過期,清除它
                        route.replace({ //跳轉到登錄頁面
                            path: '/login',
                            query: {
                                // 將跳轉的路由path作為參數,登錄成功后跳轉到該路由
                                redirect: route.currentRoute.fullPath
                            } 
                        });
                    })
            }
        }
        return Promise.reject(error) // 返回接口返回的錯誤信息
    }
);

//注冊
export const regist = params => {
    return axios.post('http://localhost:3333/user/regist', params, {}).then(res => res.data)
}

//登錄
export const login = params => {
    return axios.post('http://localhost:3333/user/login', params, {}).then(res => res.data)
}

//獲取用戶信息
export const getUserInfo = params => {
    return axios.post('http://localhost:3333/user/getUserInfo', params, {}).then(res => res.data)
}

  axios.interceptors.request.use攔截請求,給請求頭加上token

  axios.interceptors.response.use攔截響應,如果返回401,token過期,跳回login路由

  登錄后10s再請求用戶數據,返回登錄過期:

  

總結:完結撒花,如有不當指出,歡迎各位大神指出,我該鏟屎去了 ~.~

 

 

  

 

 

 

 

  

  

  

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM