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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 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();
    }
};

⭐结束

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


相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
9天前
|
前端开发 JavaScript 中间件
基于最新koa的Node.js后端API架构与MVC模式
基于最新koa的Node.js后端API架构与MVC模式
22 1
|
10天前
|
SQL 关系型数据库 数据库
阿里云数据库 RDS SQL Server版实战【性能优化实践、优点探析】
本文探讨了Amazon RDS SQL Server版在云数据库中的优势,包括高可用性、可扩展性、管理便捷、安全性和成本效益。通过多可用区部署和自动备份,RDS确保数据安全和持久性,并支持自动扩展以适应流量波动。可视化管理界面简化了监控和操作,而数据加密和访问控制等功能保障了安全性。此外,弹性计费模式降低了运维成本。实战应用显示,RDS SQL Server版能有效助力企业在促销高峰期稳定系统并保障数据安全。阿里云的RDS SQL Server版还提供了弹性伸缩、自动备份恢复、安全性和高可用性功能,进一步优化性能和成本控制,并与AWS生态系统无缝集成,支持多种开发语言和框架。
53 2
|
18天前
|
关系型数据库 MySQL 数据库
MySQL 启动 登录报错Job for mysqld.service failed because the control process exited with error code. See
MySQL 启动 登录报错Job for mysqld.service failed because the control process exited with error code. See
|
18天前
|
缓存 关系型数据库 MySQL
MySQL数据库性能优化实战
【4月更文挑战第30天】本文探讨了MySQL性能优化实战技巧,包括硬件与配置优化(如使用SSD、增加内存和调整配置参数)、索引优化(创建合适索引、使用复合索引及定期维护)、查询优化(避免全表扫描、减少JOIN和使用LIMIT)、分区与分片(表分区和数据库分片),以及使用缓存、定期清理数据库和监控诊断。通过这些方法,可以提升数据库性能和响应速度。
|
18天前
|
NoSQL 关系型数据库 Redis
数据管理DMS产品使用合集之要通过 DMS 登录到 RDS、DRDS 或 Redis,我该怎么操作
阿里云数据管理DMS提供了全面的数据管理、数据库运维、数据安全、数据迁移与同步等功能,助力企业高效、安全地进行数据库管理和运维工作。以下是DMS产品使用合集的详细介绍。
|
18天前
|
存储 关系型数据库 MySQL
MySQL数据库实战:从入门到精通
本文介绍了MySQL的使用和优化,适合Web开发者阅读。首先,确保安装并配置好MySQL,熟悉SQL基础。接着,通过命令行客户端连接数据库,执行创建、查询、添加、修改和删除数据等操作。学习数据类型并创建表存储数据。最后,探讨了数据库优化,包括查询优化和索引使用,以提升性能。
43 2
|
18天前
|
编解码 前端开发 JavaScript
node实战——koa实现文件下载和图片/pdf/视频预览(node后端储备知识)
node实战——koa实现文件下载和图片/pdf/视频预览(node后端储备知识)
40 1
|
18天前
|
存储 缓存 NoSQL
node实战——koa给邮件发送验证码并缓存到redis服务(node后端储备知识)
node实战——koa给邮件发送验证码并缓存到redis服务(node后端储备知识)
28 0
|
16天前
|
关系型数据库 MySQL API
实时计算 Flink版产品使用合集之可以通过mysql-cdc动态监听MySQL数据库的数据变动吗
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
124 0
|
11天前
|
存储 SQL 关系型数据库
【MySQL】数据库基础 -- 详解
【MySQL】数据库基础 -- 详解