# JWT鉴权详解:从原理到React项目实战

67 阅读3分钟

一、什么是JWT?

JWT全称JSON Web Token,是一种用于身份验证的轻量级令牌。简单来说,它就是一个加密的JSON字符串,包含了用户的身份信息和一些其他元数据。

想象一下,你去电影院看电影,需要出示电影票。JWT就相当于这张电影票,证明你有权利进入特定的"区域"(访问特定的API接口)。

二、JWT的结构

JWT由三部分组成,用点(.)分隔:

  1. Header(头部) :包含令牌类型和加密算法
  2. Payload(载荷) :包含用户信息和其他声明(如过期时间)
  3. Signature(签名) :用于验证令牌的真实性

三、JWT的工作流程

JWT的工作流程非常简单,主要包括以下几个步骤:

  1. 用户使用用户名和密码登录
  2. 服务器验证成功后,生成JWT令牌并返回给客户端
  3. 客户端存储JWT令牌(通常在localStorage或Cookie中)
  4. 后续请求时,客户端将JWT令牌放入请求头中
  5. 服务器验证令牌的有效性,有效则处理请求,无效则返回错误

四、在React项目中实现JWT鉴权

结合下面的 api/config.js 文件,我们来看看如何在React项目中实现JWT鉴权。

1. Axios拦截器配置

import axios from 'axios';

// 创建axios实例
const service = axios.create({
  baseURL: process.env.VITE_APP_API_URL, // 基础URL
  timeout: 5000 // 请求超时时间
});

// 请求拦截器:添加JWT令牌
axios.interceptors.request.use((config)=>{
  // 从localStorage中获取JWT令牌
  const token = localStorage.getItem('token');
  // 如果令牌存在,则添加到请求头
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
}, (error)=>{
  // 处理请求错误
  return Promise.reject(error);
});

// 响应拦截器:处理令牌过期等情况
axios.interceptors.response.use((response)=>{
  // 正常响应处理
  return response.data;
}, (error)=>{
  // 处理响应错误
  if (error.response && error.response.status === 401) {
    // 令牌过期,跳转到登录页
    localStorage.removeItem('token');
    window.location.href = '/login';
  }
  return Promise.reject(error);
});

export default service;

2. 登录接口实现

import service from './config';

// 登录接口
export const login = (username, password) => {
  return service.post('/api/login', {
    username,
    password
  });
};

// 登出接口
export const logout = () => {
  localStorage.removeItem('token');
  window.location.href = '/login';
};

3. 登录组件实现

import React, { useState } from 'react';
import { login } from '../api/login';

const Login = () => {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');

  const handleLogin = async () => {
    try {
      const res = await login(username, password);
      // 存储JWT令牌
      localStorage.setItem('token', res.data.token);
      // 跳转到首页
      window.location.href = '/';
    } catch (err) {
      setError('用户名或密码错误');
    }
  };

  return (
    <div className="login-container">
      <h2>登录</h2>
      {error && <div className="error-message">{error}</div>}
      <div className="form-group">
        <label>用户名</label>
        <input
          type="text"
          value={username}
          onChange={(e) => setUsername(e.target.value)}
        />
      </div>
      <div className="form-group">
        <label>密码</label>
        <input
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
        />
      </div>
      <button onClick={handleLogin}>登录</button>
    </div>
  );
};

export default Login;

4. 路由守卫实现

import React from 'react';
import { Route, Redirect } from 'react-router-dom';

// 路由守卫组件
const PrivateRoute = ({ component: Component, ...rest }) => {
  const isAuthenticated = !!localStorage.getItem('token');

  return (
    <Route
      {...rest}
      render={(props) =>
        isAuthenticated ? (
          <Component {...props} />
        ) : (
          <Redirect to={{ pathname: '/login', state: { from: props.location } }} />
        )
      }
    />
  );
};

export default PrivateRoute;

五、JWT的优缺点

优点

  • 无状态 :服务器不需要存储会话信息,减轻服务器负担

  • 跨域支持 :JWT可以在不同域之间传递

  • 自包含 :令牌包含了所有必要的用户信息,减少数据库查询

  • 易于扩展 :可以添加自定义声明

缺点

  • 无法作废已发布的令牌 :一旦令牌发布,在过期前始终有效(除非服务器维护黑名单)

  • 令牌较大 :相比Session ID,JWT令牌通常更大,会增加网络传输负担

  • 安全隐患 :如果令牌被窃取,攻击者可以冒充用户访问系统

  • 刷新机制复杂 :需要实现令牌刷新机制以避免用户频繁登录

六、结语

JWT是一种简单、高效的身份验证机制,特别适合前后端分离的应用。通过在React项目中使用Axios拦截器,我们可以轻松实现JWT的添加和验证。

然而,JWT也有其局限性,我们需要注意安全问题,设置合理的过期时间,并实现令牌刷新机制。