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

Laravel Sanctum+Next.js页面刷新后Cookie丢失问题排查

Laravel + Next.js 刷新页面后Cookie被删除问题排查

我的应用后端基于Laravel(运行在127.0.0.1),前端基于Next.js(运行在localhost:3000)。用户完成授权后可以进入个人资料页,但刷新页面时Cookie会立即被删除(在localhost:3000创建的token Cookie显示黄色背景)。用Postman测试后端一切正常,相关配置代码如下:

.env配置

APP_URL=http://localhost:3000
SANCTUM_STATEFUL_DOMAINS=localhost
SESSION_DRIVER=cookie

cors.php配置

'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => ['http://localhost:3000'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,

AuthController.php代码

class AuthController extends Controller
{
    public function register(RegisterRequest $request) {
        $data = $request->validated();

        $user = User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
        ]);

        $token = $user->createToken('auth_token')->plainTextToken;
        $cookie = cookie('token', $token, 60 * 24); // 1 day

        return response()->json([
            'user' => new UserResource($user),
        ])->withCookie($cookie);
    }

    public function login(LoginRequest $request) {
        $data = $request->validated();
        $user = User::where('email', $data['email'])->first();

        if (!$user || !Hash::check($data['password'], $user->password)) {
            return response()->json([
                'message' => 'Email or password is incorrect!'
            ], 401);
        }

        $token = $user->createToken('auth_token')->plainTextToken;
        $cookie = cookie('token', $token, 60 * 24); // 1 day

        return response()->json([
            'user' => new UserResource($user),
        ])->withCookie($cookie);
    }

    public function logout(Request $request) {
        $request->user()->currentAccessToken()->delete();
        $cookie = cookie()->forget('token');

        return response()->json([
            'message' => 'Logged out successfully!'
        ])->withCookie($cookie);
    }

    public function user(Request $request) {
        return new UserResource($request->user());
    }
}

前端代码

AuthApi.js

import http from "./http";

export async function login(data) {
    return await http.post(`/login`, data)
}

export async function register(data) {
    return await http.post('register', data)
}

export async function logout() {
    return await http.post('/logout')
}

export async function user() {
    return await http.get('/user')
}

http.js

import axios from 'axios'

const axiosInstance = axios.create({
    baseURL: `${process.env.NEXT_PUBLIC_BECK_APP_URL}`,
    withCredentials: true,
    headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
    },
})

export default axiosInstance

LoginPage.js

'use client';

import Button from '@/components/UI/Button';
import AlertError from '@/components/UI/AlertError';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faUser } from '@fortawesome/free-regular-svg-icons';
import Link from 'next/link';
import { useState } from 'react';
import { useAuth } from '@/context/AuthContext';
import { login } from '@/lib/AuthApi';
import { useRouter } from 'next/navigation';

const LoginPage = () => {
    const { setUser, csrfToken } = useAuth();

    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    const [error, setError] = useState(null);

    const router = useRouter();

    const handleSubmit = async e => {
        e.preventDefault();
        setError(null);

        await csrfToken()

        try {
            const resp = await login({email, password});
            if (resp.status === 200) {
                setUser(resp.data.user);

                router.push('/profile');
            }
        } catch (error) {
            if (error.response.status === 422) {
                setError(error.response.data.message);
            }
            if (error.response.status === 401) {
                setError(error.response.data.message);
            }

            console.log(error)
        }

    }

    return (
            <div className='xs:w-[95%] md:w-1/2 lg:w-1/3 my-10 text-center mx-auto py-10 border-t-4 border-myYellow bg-gray-900'>
                <form
                    onSubmit={handleSubmit}
                    className='max-w-md mx-auto text-myWhite px-5'
                >
                    <h6 className='text-xl font-extrabold font-montserrat'>
                        Авторизація
                    </h6>
                    <div className='py-3'>
                        <FontAwesomeIcon
                            icon={faUser}
                            className='text-3xl bg-white p-5 rounded-full leading-none'
                        />
                    </div>
                    <div className='mb-4'>
                        <input
                            type='email'
                            value={email}
                            onChange={e => setEmail(e.target.value)}
                            placeholder='Ваш логін'
                            className={`block w-full bg-myDark border rounded-md border-myWhite text-myWhite text-[16px] p-2`}
                        />
                        
                        <input
                            type='password'
                            value={password}
                            onChange={e => setPassword(e.target.value)}
                            placeholder='Ваш пароль'
                            className={`block w-full bg-myDark border rounded-md border-myWhite text-myWhite text-[16px] p-2 mt-4`}
                        />
                        <div className='mt-2'>
                            <AlertError error={error} />
                        </div>
                    </div>

                    <div type='submit'>
                        <Button title='Увійти на сайт' className='w-full py-2' />
                    </div>

                    <div className='flex justify-between mt-4 text-gray-300 text-[14px] decoration-dotted'>
                        <div className='block'>
                            <Link href='/signup'>Реєстрація</Link>
                        </div>
                        <div className='block'>
                            <Link href='/forgot-password'>Забули пароль?</Link>
                        </div>
                    </div>
                </form>
            </div>
        )
};

export default LoginPage;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 16:14:52