You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Spring Boot:JWT授权头始终为Undefined的令牌处理问题

React + Spring Boot全栈项目Authorization头"Bearer undefined"问题排查

问题概述

开发基于React前端和Spring Boot后端的全栈项目时,前端调用受保护API接口时,请求的Authorization头始终为"Bearer undefined",后端抛出JWT格式异常。

问题详情

使用Axios发起HTTP请求,通过React Context(AuthProvider)管理认证并存储JWT令牌。后端已确认登录成功后能生成并返回JWT令牌,但前端未正确获取或设置令牌,导致Authorization头出现"Bearer undefined"。后端日志报错:JWT strings must contain exactly 2 period characters. Found: 0.

环境配置

  • 前端:React + Axios,用useContext管理认证,通过useEffect全局设置Axios请求头
  • 后端:Spring Boot,包含JwtController和JwtAuthenticationFilter用于认证

相关代码

前端AuthProvider(JWT令牌管理上下文)

import axios from 'axios';
import { createContext, useContext, useMemo, useState, useEffect } from 'react';

const AuthContext = createContext();

const AuthProvider = ({ children }) => {
    const [token, setToken_] = useState(() => {
        return localStorage.getItem("token") || null;
    });

    const setToken = (newToken) => {
        if (newToken) {
            localStorage.setItem('token', newToken);
            setToken_(newToken);
        } else {
            localStorage.removeItem('token');
            setToken_(null);
        }
    };

    useEffect(() => {
        if (token) {
            console.log("Setting default Authorization header:", token);
            axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
        } else {
            delete axios.defaults.headers.common["Authorization"];
        }
    }, [token]);

    const contextValue = useMemo(() => ({ token, setToken }), [token]);

    return <AuthContext.Provider value={contextValue}>{children}</AuthContext.Provider>;
};

export const useAuth = () => useContext(AuthContext);
export default AuthProvider;

前端LoginForm(用户登录组件)

import React, { useState } from 'react';
import axios from 'axios';
import { useAuth } from "../provider/authProvider";
import { useNavigate } from 'react-router-dom';

function LoginForm() {
    const [username, setUsername] = useState('');
    const [password, setPassword] = useState('');
    const navigate = useNavigate();
    const { setToken } = useAuth();

    const handleSubmit = async (e) => {
        e.preventDefault();
        try {
            const response = await axios.post('http://localhost:8080/auth/login', { username, password }, {
                headers: { 'Content-Type': 'application/json' }
            });
            console.log("Full response:", response);
            console.log("Response data:", response.data);

            const { jwt } = response.data;
            if (jwt) {
                setToken(jwt);
                console.log("Token set:", jwt);
                navigate('/profile');
            } else {
                console.error("JWT token is undefined or empty. Check backend response.");
            }
        } catch (error) {
            console.error('Error logging in!', error);
            if (error.response) {
                console.error('Error details:', error.response.data);
            }
        }
    };

    return (
        <form onSubmit={handleSubmit}>
            <h2>Login</h2>
            <label>
                Username: <input type="text" value={username} onChange={(e) => setUsername(e.target.value)} />
            </label>
            <label>
                Password: <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
            </label>
            <button type="submit">Login</button>
        </form>
    );
}

export default LoginForm;

后端Spring Boot控制器

@RestController
@RequestMapping("/auth")
public class JwtController {
    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private UserDetailsService userDetailsService;

    @PostMapping("/login")
    public ResponseEntity<?> authenticate(@RequestBody AuthRequest authRequest) {
        try {
            Authentication authentication = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())
            );
        } catch (BadCredentialsException e) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials");
        }

        final UserDetails userDetails = userDetailsService.loadUserByUsername(authRequest.getUsername());
        final String jwt = jwtUtil.generateToken(userDetails);
        System.out.println("Generated JWT token: " + jwt); // Debug

        return ResponseEntity.ok(new AuthenticationResponse(jwt));
    }
}

后端日志

Authorization header: Bearer undefined
Extracted JWT token from header: undefined
JWT strings must contain exactly 2 period characters. Found: 0

求助问题

  1. 为何后端已生成JWT令牌,但前端获取到的令牌仍为undefined?
  2. localStorage、useEffect或Axios默认请求头是否存在可能导致竞态条件的已知问题?
  3. 前端存储或设置令牌的方式存在哪些问题?

已尝试的解决方法

  • 调试后端确认令牌生成正常
  • 在前端打印后端响应以验证是否接收到令牌
  • 将令牌存储到localStorage并设置Axios全局请求头
  • 检查Spring Boot的CORS配置,已允许所有来源和请求头

预期与实际结果

预期结果

  • 登录成功后,JWT令牌应被设置到后续请求的Authorization头中
  • 后端应接收到有效的JWT令牌而非undefined

实际结果

  • 登录后所有请求的Authorization头均为"Bearer undefined"
  • 后端抛出JWT令牌格式错误或缺失的异常

排查与解决思路

  1. 字段名不匹配:检查后端AuthenticationResponse类的字段名是否为jwt,前端通过response.data.jwt获取令牌,若后端返回字段为token或其他名称,会直接导致jwt为undefined。
  2. 响应结构验证:查看前端控制台打印的response.data具体内容,确认后端返回的JSON是否直接包含jwt字段,而非嵌套在其他对象中。
  3. 竞态条件处理:登录后调用navigate('/profile')可能在setToken状态更新完成前跳转,导致/profile页面发起请求时令牌未完成设置。可通过useEffect监听token变化,待令牌存在后再执行跳转。
  4. 全局请求头设置校验:检查前端控制台是否打印Setting default Authorization header:及对应令牌值,若未打印,说明token状态未正确更新。
  5. localStorage读取检查:页面刷新时,验证localStorage.getItem("token")是否能正确获取到令牌,排查存储时的编码或格式问题。

内容的提问来源于stack exchange,提问作者Shafi Chowdhury

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 20:14:53