前端的同学看过来,今天讲讲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;

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

相关推荐
Json____16 分钟前
从零构建家政服务平台:一套全栈架构如何打通管理端与移动端-java-springboot
java·spring boot·后端·架构·毕设·wwwoop.com
Q一件事19 分钟前
ArcGIS中TypeError: can‘t multiply sequence by non-int of type ‘str‘错误
前端·javascript·arcgis
leeyi34 分钟前
命令行里的平台:df_cli 都会什么(第108篇)
后端·aigc·agent
云雀衔光42 分钟前
Java Spring AI MCP:在 Spring 里把 AI 接上你的业务系统
java·开发语言·人工智能·后端·测试工具·spring
美狐美颜SDK开放平台44 分钟前
从视频处理到实时渲染:直播APP中视频美颜SDK的关键技术解析
前端·人工智能·架构·实时互动·音视频·视频美颜sdk
苏渡苇1 小时前
Spring Insight 里如何对 Span 进行清洗
java·spring boot·后端·spring·系统监控
yume_sibai1 小时前
ES6 核心知识点完全总结(变量 + 箭头函数 + 解构 + Promise + 模块化)
前端·ecmascript·es6
Moment1 小时前
为什么市面上的 coding agent 大多数都基于Nodejs?
前端·后端·面试
吃瓜的猹1 小时前
Gemini 3.8 Flash 是真的站起来了,一起来看看我到底做了什么吧~
前端
光影少年1 小时前
React18 对RN 的影响
android·前端·react.js·ios·前端框架