• nodejs--开发自己的项目——4.1——用户登录函数模块-token-JWT部分(验证机制)——支持跨域


    生成 JWT 的 Token 字符串(服务器端生成,响应给客户端)——可以通过Token实现客户端的身份认证 

            首先将用户信息传递通过浏览器传递给服务器,服务器进行验证,服务器验证之后加密生成token字符串,直接将token字符串响应给浏览器。浏览器中将token字符串存储到localstorag或sessionstorage中,客户端需要再次发起请求的时候,通过请求头的Authorization字段,将Token发给服务器,服务器将token字符串解析,服务器身份认证成功 响应成功

    安装生成token和压缩token的包:jsonwebtoken express-jwt

     //ES6的语法——需要覆盖字符串的内容——token的安全性:在token加密中不推荐保存密码等指之类的敏感信息

            const user={...results[0],password:'',user_pic:''}//ES6的语法——需要覆盖字符串的内容

    首先导入包:

    1. //导入生成token的包
    2. const jwt=require('jsonwebtoken')
    3. //导入全局的配置文件——里面有加密解析token的密钥
    4. const config=require('../config')

    token实现需要密钥:——使用中间件

    1. //全局的配置中间件
    2. //我们需要专门定义一个用于加密和解密的secret密钥——中间件
    3. module.exports = {
    4. jwtSecretKey: 'abdbdodjod-^-^-',
    5. //有效期
    6. expiresIn:'10h',
    7. }

    实现token生成//调用jsonwebtoken包中的sign()方法,将用户的信息加密生成JWT字符串,响应给客户端

    1. //调用jsonwebtoken包中的sign()方法,将用户的信息加密生成JWT字符串,响应给客户端
    2. const tokenStr=jwt.sign(user,config.jwtSecretKey,{expiresIn:config.expiresIn})

    调用res.send()将token响应给客户端

    1. console.log(tokenStr)//测试
    2. res.send(
    3. {
    4. status:0,
    5. message:'登录成功',
    6. token:'Bearer '+tokenStr,//字符串的拼接方便将token直接夹存在浏览器中之后发起请求头
    7. }
    8. )//响应给客户端的话


    解析token)——在路由之前

    1. //导入解析jwt的包
    2. var { expressjwt: expressJWT } =require('express-jwt')
    3. //导入全局的配置文件——里面有加密解析token的密钥
    4. const config=require('./config')
    5. //之后需要将客户端中的token,还原成字符串全局生效的中间件
    6. app.use(expressJWT({secret:config.jwtSecretKey,algorithms: ["HS256"]}).unless({path:[/^\/api/]}))//除去/api开头的不需要转义

    还有一个toke出错需要捕获:放在最后

    1. //定义错误级别中间件
    2. app.use(function(err, req, res, next) {
    3. //验证失败导致的错误
    4. if (err instanceof joi.ValidationError) {//验证的方式
    5. return res.cc(err.message)
    6. }
    7. //身份认证失败后的错误
    8. if(err.name === "UnauthorizedError"){
    9. return res.cc('身份认证失败')
    10. }
    11. return res.cc('未知的错误')
    12. })

    测试: 

    /c开头的认证失败,需要身份认证——需要进行expressJWT,token判断请求头中是否含有请v求头 Authorization : Bearer空格

    发送了请求头,token成功了,但是由于服务器没有这个给接口,所以也是失败的 

    全部函数:路由处理函数模块:

    1. //对应的路由处理函数
    2. //导入数据库操作模块
    3. const db=require('../db/index')
    4. //检测数据库是否导入成功
    5. // db.query('select 1',(err,results)=>{
    6. // if(err) return console.log(err.message)
    7. // console.log(results)//此结果可以正常打印输出就表示数据库连接正常
    8. // })
    9. //导入bcryptjs包对明文密码进行加密
    10. const bcrypt=require('bcryptjs')
    11. //导入生成token的包
    12. const jwt=require('jsonwebtoken')
    13. //导入全局的配置文件——里面有加密解析token的密钥
    14. const config=require('../config')
    15. //这是注册新用户的处理函数
    16. const regUser=function(req,res){//postreq.body
    17. //获取客户端提交给服务器的用户信息
    18. const userInfo=req.body
    19. //console.log(userInfo)——测试是否接收到表单数据
    20. //*********对表单中的数据进行合法性的校验_____放到了表单数据传入的部分
    21. //定义SQL语句,查询用户名是否被占用
    22. const sqlStr='select * from ev_users where username=?'//sql语句
    23. db.query(sqlStr,[userInfo.username],(err,results)=>{
    24. //执行SQL语句失败_
    25. if(err) return res.cc(err)
    26. //select查询的结果是数组,用户名被占用——查询不会影响行数
    27. if(results.length>0) return res.cc('用户名被占用,请更换用户名')
    28. //用户名可用使用
    29. //调用bcrypt.hashSync进行加密--归还给userInfo
    30. userInfo.password=bcrypt.hashSync(userInfo.password,10)//加密的内容
    31. //console.log(userInfo)
    32. //定义插入新用户的SQL语句——插入的少使用表(字段)values(值) 插入多使用set
    33. const sqlStr1='insert into ev_users set ?'
    34. db.query(sqlStr1,{username:userInfo.username,password:userInfo.password},(err,results)=>{
    35. //执行SQL语句失败_
    36. if(err) return res.cc(err)
    37. //判断插入失败
    38. if(results.affectedRows!==1)return res.cc('注册用户失败,请稍后再试!')
    39. //c成功传参
    40. res.cc('注册用户成功!',0)
    41. })
    42. })
    43. }
    44. //这是登录的处理函数
    45. const login=function(req,res){//post
    46. userInfo=req.body//接收表单数据
    47. //定义查询用户的SQL语句
    48. const sqlStr='select * from ev_users where username= ?'
    49. db.query(sqlStr,userInfo.username,(err,results)=>{
    50. //执行SQL语句失败_
    51. if(err) return res.cc(err)
    52. //判断查询失败--result里面存的是数组
    53. if(results.length!==1) return res.cc('用户名不存在,请重新登录')
    54. //密码的验证——直接使用等号是不行的,
    55. //我们在用户注册的时候调用bcrypt.hashSync对密码进行加密————存储
    56. //results[0]查出来的第一项
    57. const compareResults= bcrypt.compareSync(userInfo.password, results[0].password)
    58. if(!compareResults) return res.cc('密码错误,请重新登录!')
    59. //用户密码用户名成功之后——token操作
    60. //在服务器端生成Token的字符串
    61. // console.log({...results[0]})//查询数据的数组内容
    62. const user={...results[0],password:'',user_pic:''}//ES6的语法——需要覆盖字符串的内容
    63. //console.log(user)//测试
    64. //调用jsonwebtoken包中的sign()方法,将用户的信息加密生成JWT字符串,响应给客户端
    65. const tokenStr=jwt.sign(user,config.jwtSecretKey,{expiresIn:config.expiresIn})
    66. //console.log(tokenStr)//测试
    67. res.send(
    68. {
    69. status:0,
    70. message:'登录成功',
    71. token:'Bearer '+tokenStr,//字符串的拼接方便将token直接夹存在浏览器中之后发起请求头
    72. }
    73. )//响应给客户端的话
    74. })
    75. }
    76. //向外暴露
    77. module.exports={
    78. regUser,
    79. login
    80. }

    token密钥模块:

    1. //全局的配置中间件
    2. module.exports = {
    3. //
    4. //我们需要专门定义一个用于加密和解密的secret密钥——中间件
    5. jwtSecretKey: 'abdbdodjod-^-^-',
    6. //有效期
    7. expiresIn:'10h',
    8. }

    服务器模块:

    1. //导入express模块
    2. const express=require('express')
    3. //创建web服务器
    4. const app=express()
    5. // 导入 cors 中间件——支持跨域访问
    6. const cors = require('cors')
    7. // 将 cors 注册为全局中间件
    8. app.use(cors())
    9. //配置解析application/x-www-form-urlencoded格式数据的内置中间件
    10. app.use(express.urlencoded({ extended: false }))//extended默认值
    11. //导入表单数据中间件
    12. const joi=require('joi')
    13. //在路由之前,封装函数
    14. // 响应数据的中间件
    15. app.use(function (req, res, next) {
    16. // status = 0 为成功; status = 1 为失败; 默认将 status 的值设置为 1,方便处理失败的情况
    17. res.cc = function (err, status = 1) {//status默认失败——传参
    18. res.send({
    19. // 状态
    20. status,
    21. // 状态描述,判断 err 是 错误对象 还是 描述错误字符串
    22. message: err instanceof Error ? err.message : err,//instanceof是否是Error实例
    23. })
    24. }
    25. next()//流转关系传下去
    26. })
    27. //导入解析jwt的包
    28. var { expressjwt: expressJWT } =require('express-jwt')
    29. //导入全局的配置文件——里面有加密解析token的密钥
    30. const config=require('./config')
    31. //之后需要将客户端中的token,还原成字符串全局生效的中间件
    32. app.use(expressJWT({secret:config.jwtSecretKey,algorithms: ["HS256"]}).unless({path:[/^\/api/]}))//除去/api开头的不需要转义
    33. //导入并使用用户的路由模块
    34. const userRouter=require('./router/user.js')
    35. app.use('/api',userRouter)//加上统一的前缀——将其注册成为全局可以用的路由模块
    36. //定义错误级别中间件
    37. app.use(function(err, req, res, next) {
    38. //验证失败导致的错误
    39. if (err instanceof joi.ValidationError) {//验证的方式
    40. return res.cc(err.message)
    41. }
    42. //身份认证失败后的错误
    43. if(err.name === "UnauthorizedError"){
    44. return res.cc('身份认证失败')
    45. }
    46. return res.cc('未知的错误')
    47. })
    48. //启动服务器、使用3007端口
    49. app.listen(3007,()=>{
    50. console.log('http://127.0.0.1:3007 服务器启动成功')
    51. })

  • 相关阅读:
    用 GPT-4 给开源项目 GoPool 重构测试代码 - 每天5分钟玩转 GPT 编程系列(8)
    数据可视化
    阿里 P8 最新版 SpringCloud 面试题是如何让我“颜面扫地”的?
    Springboot+网上眼镜商场 毕业设计-附源码241659
    在 Net7.0 环境下使用 RestSharp 发送 Http(FromBody和FromForm)请求
    【毕业设计-课程设计】-单片机电子密码锁设计
    JavaScript For-In循环
    【Python基础入门技能树笔记】数据类型-基本数据类型
    第十三届蓝桥杯省赛C/C++ B组
    中秋佳节 月饼来访 ! python采集相关数据,看看哪款月饼最受欢迎
  • 原文地址:https://blog.csdn.net/weixin_47295886/article/details/126887634