node实战——后端koa结合jwt连接mysql实现权限登录(node后端就业储备知识)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
云数据库 RDS PostgreSQL,高可用系列 2核4GB
简介: node实战——后端koa结合jwt连接mysql实现权限登录(node后端就业储备知识)

⭐前言

大家好,我是yma16,本文分享关于node实战——后端koa项目配置jwt实现登录注册(node后端就业储备知识)。

本文适用对象:前端初学者转node方向,在线大学生,应届毕业生,计算机爱好者。

node系列往期文章

node_windows环境变量配置

node_npm发布包

linux_配置node

node_nvm安装配置

node笔记_http服务搭建(渲染html、json)

node笔记_读文件

node笔记_写文件

node笔记_连接mysql实现crud

node笔记_formidable实现前后端联调的文件上传

node笔记_koa框架介绍

node_koa路由

node_生成目录

node_读写excel

node笔记_读取目录的文件

node笔记——调用免费qq的smtp发送html格式邮箱

node实战——搭建带swagger接口文档的后端koa项目(node后端就业储备知识)

jwt的发展

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在不同应用程序或服务之间安全地传输信息和声明。它最初由JWT工作组在2015年发布,现在已成为Web应用程序和API的常见身份验证和授权方案之一。JWT标准建立在JSON数据格式上,由三个部分组成:头部 header、载荷 payload、签名 Authorization。它们共同构成了一个包含声明信息的安全令牌。

JWT的发展可以追溯到OAuth 2.0协议,它是OAuth 2.0协议的一种扩展。JWT的目的是为了解决OAuth 2.0协议中的一些缺点,例如在使用OAuth 2.0协议时,每次请求都需要向OAuth服务器进行验证。使用JWT可以避免这种验证,因为JWT包含了所有必要的信息,服务器可以直接验证JWT的签名以确认用户的身份。

随着云计算和移动设备应用的普及,JWT已经成为了Web应用程序和API的一种标准身份验证和授权方案。同时,围绕JWT的生态系统也在不断壮大。例如,OAuth 2.0协议和OpenID Connect协议都已经支持JWT作为身份验证和令牌颁发方案。

总之,JWT是一种开放的、安全的、跨平台的身份验证和授权方案,它为不同应用程序和服务之间的安全通信提供了便利。随着时间的推移,JWT的发展已经越来越成熟,它将继续在Web应用程序和API的身份验证和授权领域发挥重要作用。

注册

登录

⭐ 环境准备

安装koa 需要的依赖

$ npm install koa koa-jwt koa-bodyparser koa2-cors koa-router koa2-swagger-ui mysql

官方文档用法

var Koa = require('koa');
var jwt = require('koa-jwt');
var app = new Koa();
// Custom 401 handling if you don't want to expose koa-jwt errors to users
app.use(function(ctx, next){
  return next().catch((err) => {
    if (401 == err.status) {
      ctx.status = 401;
      ctx.body = 'Protected resource, use Authorization header to get access\n';
    } else {
      throw err;
    }
  });
});
// Unprotected middleware
app.use(function(ctx, next){
  if (ctx.url.match(/^\/public/)) {
    ctx.body = 'unprotected\n';
  } else {
    return next();
  }
});
// Middleware below this line is only reached if JWT token is valid
app.use(jwt({ secret: 'shared-secret' }));
// Protected middleware
app.use(function(ctx){
  if (ctx.url.match(/^\/api/)) {
    ctx.body = 'protected\n';
  }
});
app.listen(3000);

初始化项目搭建请查看我写的这篇博客:node实战——搭建带swagger接口文档的后端koa项目(node后端就业储备知识)

⭐ 实现过程

注册过程分解:

  1. 查询用户名是否在user表存在
  2. 注册用户名已存在则登录
  3. 注册用户名不存在则插入用户数据

登录过程分解:

  1. 查询用户密码是否对应user表的数据
  2. 存在则根据用户名密码生成token
  3. 不存在抛出错误

token登录过程分解:

  1. 解析web端传递的token
  2. 拿出其中的用户名密码在数据库查询存在则登录成功

⭐ mysql 配置

封装mysql连接传递一个sql语句来运行即可

const mysql = require('mysql');
const config ={
    host     : 'ip',
    user     : '账号',
    password : '密码',
    database : '数据库模式'
}
// 把这个方法抛出去
const execMysql=(sql)=>{
    return new Promise((resolve,reject)=>{
        // 连接mysql
        const connection = mysql.createConnection(config);
        try{
            connection.query(sql, function (error, results) {
                if (error){
                    reject(error)
                };
                resolve(results)
            });
        }
        catch (e) {
            reject(e)
        }
        finally {
            connection.end()
        }
    })
};
module.exports={
    execMysql
}

⭐路由前的准备

初始化router

const Router = require('koa-router');
const router = new Router();
const {execMysql}=require('../../utils/mysql/index')
const jwtToken = require("jsonwebtoken");
// 唯一字符串
function uuid() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = Math.random() * 16 | 0,
            v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
}
// 当前时间
const  getCurrentTime=() =>{
    const now = new Date()
    const year = now.getFullYear()
    const month = now.getMonth()
    const date = now.getDate()
    const hour = now.getHours()
    const minutes = now.getMinutes()
    const second = now.getSeconds()
    const formatNum = (n) => {
        return n > 9 ? n.toString() : '0' + n
    }
    return `${year}-${formatNum(month + 1)}-${formatNum(date)} ${formatNum(hour)}:${formatNum(minutes)}:${formatNum(second)}`
}

⭐账号注册生成token

封装注册写入sql 生成token

// 注册
router.post('/register', async (ctx) => {
   try{
       // 解析参数
       const bodyParams =  ctx.request.body
       const {username,password} = bodyParams;
       if(!username||!password){
           return ctx.body = {
               code: 0 ,
               msg:'username or password is null'
           };
       }
       // 查询重复
       const search=await execMysql(`select count(1) as total from user where username='${username}';`)
       console.log('search',search)
       if(search[0].total>0){
           return ctx.body = {
               code: 0 ,
               msg:'user is exist'
           };
       }
       // id 唯一字符
       const id= uuid()
       const create_time=getCurrentTime()
       // 插入 数据
       const createRes=await execMysql(`INSERT INTO user (id,username,password,create_time) VALUES ('${id}', '${username}','${password}','${create_time}');`)
       // 更新token update_time
       const token=jwtToken.sign(
           {
               username,
               password
           },
           "yma16-app", // secret
           { expiresIn: 24 * 60 * 60 } // 60 * 60 s
       )
       const update_time=getCurrentTime()
       const tokenRes=await execMysql(`update user set token='${token}', update_time='${update_time}' where username='${username}';`)
       ctx.body = {
           code:200,
           data:{
               createSqlData:createRes,
               tokenSqlData:tokenRes
           },
           msg:' insert success',
           token:token
       };
   }
   catch (e) {
       ctx.body = {
           code:0,
           msg:e
       };
   }
});

⭐账号登录生成token

账号密码生成token

// 获取token
router.post('/token/gen', async (ctx) => {
    try{
        // 解析参数
        const bodyParams =  ctx.request.body
        const {username,password} = bodyParams;
        // 查询 用户
        const search=await execMysql(`select count(1) as total from user where username='${username}' and password='${password}';`)
        if(search[0].total>0){
            // 更新token update_time
            const token=jwtToken.sign(
                {
                    username,
                    password
                },
                "yma16-app", // secret
                { expiresIn: 24 * 60 * 60 } // 60 * 60 s
            )
            const update_time=getCurrentTime()
            // 更新token
            const tokenRes=await execMysql(`update user set token='${token}', update_time='${update_time}' where username='${username}';`)
            // 配置token
            const AUTHORIZATION='Authorization'
            ctx.set(AUTHORIZATION,token)
            return ctx.body = {
                code:200,
                msg:'login success',
                token:token
            };
        }
        ctx.body = {
            code:0,
            msg:' login fail',
        };
    }
    catch (e) {
        ctx.body = {
            code:0,
            msg:e
        };
    }
});

⭐token登录

用户使用token登录

// token 登录
router.post('/token/login',async (ctx) => {
    try{
        // 解析参数
        const bodyParams =  ctx.request.body
        const {token} = bodyParams;
        const payload = jwtToken.verify(token, 'yma16-app');
        console.log('token',token)
        const {username,password} =payload
        // 查询重复
        // 查询 用户
        const search=await execMysql(`select count(1) as total from user where username='${username}' and password='${password}';`)
        if(search[0].total>0){
            const last_login_time=getCurrentTime()
            // last_login_time  登录时间
            const tokenRes=await execMysql(`update user set last_login_time='${last_login_time}' where username='${username}' and password='${password}';`)
            return ctx.body = {
                code:200,
                msg:'login success',
                data:{
                    username
                }
            };
        }
        ctx.body = {
            code:0,
            msg:' login fail',
        };
    }
    catch (e) {
        console.log('e',e)
        ctx.body = {
            code:0,
            msg:JSON.stringify(e)
        };
    }
})

⭐ 自测过程截图

生成token

选择 authoration 为 beare token传递token 在 body的传递自定义token请求登录成功

数据库表写入 用户名、密码、token、创建时间、更新时间、最近登录时间 成功!

⭐总结

注意事项:

  1. jwt 前后的 Authorization 保持一致
  2. 给注册页面开启白名单,不需要jwt校验
  3. 注意过去的expire时间,调试的时候调大一点

前后端加上aes加密更安全

后端node配置

const aesCrypto = require('crypto-js/aes');
const utf8Encode = require("crypto-js/enc-utf8")
const secretKey = "yma16-app"
// 加密
const encrypt = text => {
    let encryptedText = aesCrypto.encrypt(utf8Encode.parse(text), secretKey).toString();
    return encryptedText
}
// 解密
const decrypt = text => {
    let decryptText = aesCrypto.decrypt(text, secretKey).toString(utf8Encode)
    console.log(decryptText)
    return decryptText.toString(utf8Encode);
}
exports.aes = { encrypt, decrypt }

前端vue3配置

安装 crypto-js

定义加密解密方法

// @ts-ignore
import CryptoJS from 'crypto-js';
export const globalEnv=()=>{
    return import.meta.env
};
const key='yma16-app'
export const aes:any={
    //加密
    encrypt(word:string, keyStr?:any) {
        keyStr = keyStr ? key : 'yma16-app';
        var key = CryptoJS.enc.Utf8.parse(keyStr);
        var srcs = CryptoJS.enc.Utf8.parse(word);
        var encrypted = CryptoJS.AES.encrypt(srcs, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 });
        return encrypted.toString();
    },
    //解密
    decrypt(word:string, keyStr?:any) {
        keyStr = keyStr ? key : 'abcdsxyzhkj12345';
        var key = CryptoJS.enc.Utf8.parse(keyStr);
        var decrypt = CryptoJS.AES.decrypt(word, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7});
        return CryptoJS.enc.Utf8.stringify(decrypt).toString();
    }
};

⭐结束

本文分享到这结束,如有错误或者不足之处欢迎指出!


相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
22天前
|
负载均衡 算法 关系型数据库
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
|
6月前
|
前端开发 关系型数据库 MySQL
PHP与MySQL动态网站开发实战指南####
【10月更文挑战第21天】 本文将深入浅出地探讨如何使用PHP与MySQL构建一个动态网站,从环境搭建到项目部署,全程实战演示。无论你是编程新手还是希望巩固Web开发技能的老手,都能在这篇文章中找到实用的技巧和启发。我们将一起探索如何通过PHP处理用户请求,利用MySQL存储数据,并最终呈现动态内容给用户,打造属于自己的在线平台。 ####
236 0
|
4月前
|
SQL 存储 关系型数据库
MySQL秘籍之索引与查询优化实战指南
最左前缀原则。不冗余原则。最大选择性原则。所谓前缀索引,说白了就是对文本的前几个字符建立索引(具体是几个字符在建立索引时去指定),比如以产品名称的前 10 位来建索引,这样建立起来的索引更小,查询效率更快!
193 22
 MySQL秘籍之索引与查询优化实战指南
|
4月前
|
JavaScript 算法 安全
深度剖析:共享文件怎么设置密码和权限的 Node.js 进阶算法
在数字化时代,共享文件的安全性至关重要。本文聚焦Node.js环境,介绍如何通过JavaScript对象字面量构建数据结构管理文件安全信息,包括使用`bcryptjs`库加密密码和权限校验算法,确保高效且安全的文件共享。通过实例代码展示加密与权限验证过程,帮助各行业实现严格的信息资产管理与协作。
|
6月前
|
安全 关系型数据库 MySQL
PHP与MySQL动态网站开发实战指南####
——深入探索LAMP栈下的高效数据交互与处理技巧 ####
|
6月前
|
关系型数据库 MySQL PHP
PHP与MySQL动态网站开发实战指南####
深入探索PHP与MySQL的协同工作机制,本文旨在通过一系列实战案例,揭示构建高效、稳定且用户友好的动态网站的秘诀。从环境搭建到数据交互,再到最佳实践分享,本文为开发者提供了一条清晰的学习路径,助力其在LAMP(Linux, Apache, MySQL, PHP/Perl/Python)栈上实现技术飞跃。 ####
|
5月前
|
JSON 安全 Java
什么是JWT?如何使用Spring Boot Security实现它?
什么是JWT?如何使用Spring Boot Security实现它?
1102 5
|
9月前
|
SQL Java 测试技术
在Spring boot中 使用JWT和过滤器实现登录认证
在Spring boot中 使用JWT和过滤器实现登录认证
463 0
|
7月前
|
JSON 安全 算法
|
4月前
|
XML JavaScript Java
SpringBoot集成Shiro权限+Jwt认证
本文主要描述如何快速基于SpringBoot 2.5.X版本集成Shiro+JWT框架,让大家快速实现无状态登陆和接口权限认证主体框架,具体业务细节未实现,大家按照实际项目补充。
280 11