React+Golang登录后Cookie页面刷新消失问题求助
问题背景
我使用React作为前端、Golang作为后端,两者部署在同一VPS上:前端通过虚拟主机绑定域名,后端部署在VPS的8081端口Docker容器中。后端登录接口会设置存储JWT的Cookie,登录成功会弹出成功提示,但页面刷新后Cookie消失,无法维持登录状态,用户无法访问个人资料等功能。
相关代码
后端Login接口处理代码
func (app *application) Login(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { w.Header().Set("Allow", "POST") http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } var User UserLoginBody decoder := json.NewDecoder(r.Body) if err := decoder.Decode(&User); err != nil { app.server_error(w, err) return } userID, err := app.users.Authenticate(User.Email, User.Password) if err != nil { app.server_error(w, err) return } fmt.Println("User ID:", userID) tokenString, err := app.user_helpers.GenerateToken(userID) if err != nil { println(userID, tokenString) app.server_error(w, err) return } http.SetCookie(w, &http.Cookie{ Name: "token", Value: tokenString, MaxAge: 3600, HttpOnly: false, Secure: true, SameSite: http.SameSiteNoneMode, }) w.WriteHeader(http.StatusOK) }
前端登录请求代码
const LoginForm = () => { const form = useForm<SignInSchemaType>({ resolver: zodResolver(SignInSchema), defaultValues: { userEmail: "", userPassword: "", } }) const navigate = useNavigate(); const { toast } = useToast() const [isPassword, setIsPassword] = useState(true); const handleLogin = async (values: SignInSchemaType) => { try { const response = await fetch("https://cihanbatasul.com:8081/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(values), credentials: "include", }); if (!response.ok) { toast({ variant: "destructive", title: "Failure", description: "There was a problem with the login. PLease make sure you have the right credentials." }) } if (response.ok) { toast({ variant: "default", title: "Success", description: "Welcome back!" }) navigate(-1); } } catch (error) { console.log(error) toast({ variant: "destructive", title: "Failure", description: "There was a problem with the login. PLease make sure you have the right credentials." }) } } return ( <motion.div key="login-form" initial={{ opacity: 0, scale: 0 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0 }} transition={{ duration: 0.6, ease: easeInOut }} className="w-full h-full flex flex-col gap-3 text-inherit p-3 md:p-6 lg:p-12" > <Form {...form}> <form onSubmit={form.handleSubmit(handleLogin)} className="space-y-8"> <FormField control={form.control} name="userEmail" render={({ field }) => ( <FormItem> <FormControl> <Input LabelText="Email" type="email" {...field} /> </FormControl> <FormMessage /> </FormItem> )} /> <FormField control={form.control} name="userPassword" render={({ field }) => ( <FormItem> <FormControl> <PasswordInput {...field} fullWidth={true} PlacerholderText="Password" isPassword={isPassword} onVisible={setIsPassword} /> </FormControl> <FormMessage /> </FormItem> )} /> <Button className="w-full bg-blue-600 rounded-lg hover:bg-blue-700 py-2 text-white cursor-pointer" type="submit">Submit</Button> </form> </Form> </motion.div> ); }; export default LoginForm;
前端获取Cookie代码
const getJwtFromCookie = () => { const cookieValue = document.cookie .split('; ') .find(row => row.startsWith('token=')) ?.split('=')[1]; return cookieValue; }; const token = getJwtFromCookie() if (token) { dispatch(setToken(token)) } const isUserLoggedIn = useSelector((state: RootState) => state.user.isLoggedIn)
Redux的userSlice代码
import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import {ShelvesResponse } from "@/views/UserProfile"; interface User { userID?: string | null isLoggedIn: boolean token: string | null userName: string shelves: ShelvesResponse recommendations: string[] authors: string[] } const initialState: User = { userID: null, isLoggedIn: false, token: null, userName: "", shelves: { userName: "User", AllShelves: [ { shelfType: "curr", shelf: [ { bookID: "1", title: "Current Book", author: "Author 1", description: "Description of the current book", picture: "url-to-book-image", added_at: Date() }, ], }, { shelfType: "fav", shelf: [ { bookID: "2", title: "Favorite Book", author: "Author 2", description: "Description of the favorite book", picture: "url-to-book-image", added_at: Date() }, ], }, { shelfType: "wtr", shelf: [ { bookID: "3", title: "Want to Read Book", author: "Author 3", description: "Description of the want to read book", picture: "url-to-book-image", added_at: Date() }, ], }, { shelfType: "read", shelf: [ { bookID: "4", title: "Read Book", author: "Author 4", description: "Description of the read book", picture: "url-to-book-image", added_at: Date() }, ], }, ], }, recommendations: [], authors: [] } const userSlice = createSlice({ name: "user", initialState, reducers: { setAuthors: (state, action: PayloadAction<string[]>) => { state.authors = action.payload }, setRecommendations: (state, action:PayloadAction<string[]>) => { state.recommendations = action.payload }, setShelves: (state, action: PayloadAction<ShelvesResponse>) => { state.shelves = action.payload }, setToken: (state, action: PayloadAction<string>) =>{ state.token = action.payload state.isLoggedIn = !!action.payload console.log(state.token) }, logoutUser: (state) => { document.cookie = "token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;" state.token = null state.isLoggedIn = false }, setID: (state, action: PayloadAction<string>) => { state.userID = action.payload } } }) export const {setToken, logoutUser, setID, setShelves, setRecommendations, setAuthors} = userSlice.actions export default userSlice.reducer
登录后浏览器截图


排查原因及修复方案
Cookie未绑定正确域名
当前后端设置Cookie时未指定Domain属性,浏览器默认将Cookie绑定到带端口的域名cihanbatasul.com:8081,而前端域名是不带端口的cihanbatasul.com,跨端口场景下前端无法读取该Cookie。
修复:在后端Cookie配置中添加Domain: "cihanbatasul.com",让Cookie绑定到根域名,确保前端页面能读取。Cookie路径限制
默认Path为请求路径/login,导致只有/login路径下能访问Cookie,其他页面无法读取。
修复:添加Path: "/",使Cookie在整个域名下生效。前端获取Cookie时机优化
当前获取Cookie的代码仅在组件初始化时执行,若页面刷新时Cookie读取逻辑未触发,会导致Redux状态未更新。
修复:将获取Cookie的逻辑放入useEffect钩子,确保页面刷新时重新读取:useEffect(() => { const token = getJwtFromCookie(); if (token) { dispatch(setToken(token)); } }, []);确认Secure属性兼容性
虽然当前是HTTPS环境,但若存在非HTTPS请求场景,Secure: true会阻止Cookie设置。需确保所有请求均为HTTPS。检查Docker端口映射与反向代理
确认后端Docker容器端口映射正确,若前端使用Nginx等反向代理,需确保代理配置正确传递Set-Cookie响应头,未修改或丢失Cookie属性。
内容的提问来源于stack exchange,提问作者intero

