前端的同学看过来,今天讲讲jwt登录

第一次听说jwt鉴权登录的时候,觉得是一个非常牛逼的技术,非常高深,直到我最近开发一个网站使用jwt后我才发现,其实jwt非常简单,只要你看过一遍就能明白了。下面和我一起来学习下吧👇🏻

什么是jwt鉴权登录

jwt全称:json web token

它的功能可以简单概述为两种:其一是签发token,其二是验证token。

我简单用生活中的事情举个例子:

  1. 签发token就好比公安局给你的身份证,身份证就是你的token,它代表着你,记录着你的信息。
  2. 验证token就好比你坐火车进站刷身份证,火车站的系统能根据你的身份证确认你的信息

jwt就这两个功能

下面介绍下jwt的签发token是如何做的

token签发

首先看下token的组成,token是由三部分组成的

  1. header(这部分一般都是默认的,不需要我们管)
js 复制代码
{
    "alg": "HS256", // 加密算法
    "typ": "JWT" // 令牌类型
}

还有很多其它的参数,这些参数都是可以使用默认的

  1. 隐私信息,这些信息可以使用用户信息组成
js 复制代码
{
    userId: '11111',
    name: '张三'
}

这些信息由用户自己定义

  1. secretKey,这个说白了就是一个字符串,随便什么都可以,一般为了保密,都会设定一个字符串,然后使用加密算法加密后使用
js 复制代码
    abcdef

以上这三部分就是token的组成了,有了这三部分就可以构建最终的token,构建方法就将这三部分分别加密,然后使用 . 将这三部分连接起来

js 复制代码
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsImlhdCI6MTc0Mzc1OTY2NywiZXhwIjoxNzQzNzYzMjY3fQ.Gha7H8S12RXhWSd4BXdtQVAYETivKDgyX_VtqpNKTAo

这个token例子就是我生成的

生成方法

js 复制代码
const token = jwt.sign(
    {
      userId: user.id
    },
    process.env.SECRET,
    { expiresIn: '1h' }
);

第一个参数:用户信息

第二个参数:secretKey

第三个参数:过期时间

我们登录过程中将生成的token返回给前端,返回的方式可以加在cookie中

前端传递token给服务端

js 复制代码
  // 前端获取到token在请求时需要传递给后端,一般是在header离传递过去
  // 以axios为例
  // 传递方式一般会使用 'Bearer token'这种方式
  axios.post('https://api.com/login', {
      headers: {
          authorization: 'Bearer token'
      }
  })
  

后端接收方式

js 复制代码
// 服务端会在中间件中校验登录信息,便于所有接口都能够使用
module.exports = async function(req, res, next) {
  try{
    //判断Token是否存在
    const authHeader = req.headers.authorization || req.query.token;

    const token = authHeader && authHeader.split(' ')[1];
    if(!token){
      throw new Error('当前接口需要认证才能访问。')
    }

    const decoded = jwt.verify(token, process.env.SECRET);
    const { userId } = decoded;

    const user = await User.findByPk(userId);
    if(!user) {
      throw new NotFoundError('用户不存在');
    }

    req.userInfo = user;

    next();
  } catch (error){
    fail(res,error);
  }
}

好了,以上差不多就是jwt的整体能力,它的流程还是挺简单的,通过这篇文章你就能实现一个非常简单的jwt鉴权登录了,快去试一试

当然这只是登录的其中一种方式,还有就是大企业都选择的SSO单点登录方式,这个好像是挺牛的,我还没搞懂,等我搞懂,下一节我们一起来探讨下,不会让大家等太长时间的

下面是jwt鉴权登录的实例,大家可以体验下

express服务端

js 复制代码
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const cors = require('cors');

const app = express();
app.use(express.json());
app.use(cors());

const PORT = 8008;
const JWT_SECRET = 'jwt_secret_code_custom';

// 模拟用户
let users = [
    { id: 'abc', name: '张三', email: '11@qq.com' }
];

// 登录接口
app.post('/api/login', async (req, res) => {
  const { email, password } = req.body;
  
  const user = users.find(u => u.email === email);
  if (!user) return res.status(401).json({ message: '用户不存在' });

  const validPass = await bcrypt.compare(password, user.password);
  if (!validPass) return res.status(401).json({ message: '密码错误' });

  // 生成JWT
  const token = jwt.sign(
    { userId: user.id, email: user.email },
    JWT_SECRET,
    { expiresIn: '1h' }
  );

  res.json({ token });
});

// 资源接口,接口会优先执行中间件
app.get('/api/protected', authenticateToken, (req, res) => {
  res.json({ 
    message: '这是受保护的内容',
    user: req.user 
  });
});

// JWT验证中间件
function authenticateToken(req, res, next) {
  try{
    //判断Token是否存在
    const authHeader = req.headers.authorization || req.query.token;

    const token = authHeader && authHeader.split(' ')[1];
    if(!token){
      throw new Error('当前接口需要认证才能访问。')
    }

    const decoded = jwt.verify(token, process.env.SECRET);
    const { userId } = decoded;

    const user = await User.findByPk(userId);
    if(!user) {
      throw new NotFoundError('用户不存在');
    }

    req.userInfo = user;

    next();
  } catch (error){
    console.log(error)
  }
}

app.listen(process.env.PORT, () => {
  console.log(`服务运行在 http://localhost:${PORT}`);
});

前端react

js 复制代码
// App.js
import React, { useState } from 'react';
import axios from 'axios';
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';

// 配置axios
axios.defaults.baseURL = 'http://localhost:8008/api';

// 登录组件
const Login = ({ setToken }) => {
  const [formData, setFormData] = useState({ email: '', password: '' });

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const res = await axios.post('/login', formData);
      setToken(res.data.token);
    } catch (err) {
      alert(err.response?.data?.message || '登录失败');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        placeholder="邮箱"
        value={formData.email}
        onChange={(e) => setFormData({ ...formData, email: e.target.value })}
        required
      />
      <input
        type="password"
        placeholder="密码"
        value={formData.password}
        onChange={(e) => setFormData({ ...formData, password: e.target.value })}
        required
      />
      <button type="submit">登录</button>
    </form>
  );
};

// 受保护页面
const ProtectedPage = () => {
  const [data, setData] = useState(null);

  const fetchData = async () => {
    try {
      const token = localStorage.getItem('token');
      const res = await axios.get('/protected', {
        headers: { authorization: `Bearer ${token}` }
      });
      setData(res.data);
    } catch (err) {
      console.error('获取数据失败:', err);
    }
  };

  return (
    <div>
      <h2>受保护页面</h2>
      <button onClick={fetchData}>获取数据</button>
      {data && <pre>{JSON.stringify(data, null, 2)}</pre>}
    </div>
  );
};

// 路由配置
const App = () => {
  const [token, setToken] = useState(localStorage.getItem('token'));

  const handleSetToken = (newToken) => {
    localStorage.setItem('token', newToken);
    setToken(newToken);
  };

  return (
    <Router>
      <Routes>
        <Route path="/" element={
          token ? <Navigate to="/protected" /> : <Login setToken={handleSetToken} />
        } />
        <Route path="/protected" element={
          token ? <ProtectedPage /> : <Navigate to="/" />
        } />
      </Routes>
    </Router>
  );
};

export default App;

⭐️关注我,一起掌握新技术

相关推荐
亮子AI1 分钟前
【npm】如何创建自己的npm私有仓库?
前端·npm·node.js
JS_GGbond3 分钟前
前端Token无感刷新:让用户像在游乐园畅玩一样流畅
前端
用户8168694747253 分钟前
Context API 的订阅机制与性能优化
前端·react.js
用户49394095229354 分钟前
Function.prototype.bind实现
前端
AAA阿giao5 分钟前
Vue3 调用 Coze 工作流:从上传宠物照到生成冰球明星的完整技术解析
前端·vue.js·coze
异界蜉蝣6 分钟前
React Fiber架构:Diff算法的演进
前端·react.js·前端框架
xiaok6 分钟前
oa流程到达某个节点之后触发自定义动作(外部接口)
后端
追梦_life8 分钟前
localStorage使用不止于getItem、setItem、removeItem
前端·javascript
全栈陈序员8 分钟前
请描述下你对 Vue 生命周期的理解?在 `created` 和 `mounted` 中请求数据有什么区别?
前端·javascript·vue.js·学习·前端框架
无限大69 分钟前
用三行代码实现圣诞树?别逗了!让我们来真的
前端·javascript