javascript - 为什么我在我的 React 应用程序中丢失了登录信息?
问题描述
我有一个 reactjs 应用程序,它使用 django 后端来处理身份验证。
这是我的 React 应用程序的 App.tsx Typescript 代码的相关部分。
import React, {useState, useEffect} from 'react';
import { BrowserRouter as Router, Route, Link, RouteComponentProps, Redirect } from 'react-router-dom';
import {AuthContext, useAuth} from "./context/auth";
import './App.css';
import PrivateRoute from "./PrivateRoute";
import logoImg from "./img/logo.svg";
import { Card, Logo, Form, Input, Button } from "./components/AuthForm";
import axios from 'axios';
import Cookies from 'js-cookie';
// The "Home" Page.
function Index() {
const [appUser, setAppUser] = useState({pk:-1, username: '', email: '', first_name: '', last_name:''});
const {authTokens} = useAuth();
useEffect(()=> {
if (authTokens) {
const url = 'http://localhost:8000/rest-auth/user/';
const withCredentials = true;
const method = 'get';
// the token is what we got from the backend when we logged in.
const headers = {
"Authorization": "Token " + authTokens['key'] + " "
};
axios.request({method, url, withCredentials, headers}).then(response => {
console.log('Login() response is ', response);
setAppUser((appUser) => ({...appUser, ...response.data}));
})
// .catch(error => { setAppUser(null); setLoggedIn(false); })
}
}, [authTokens]);
return authTokens ? (
<h2>Home you are logged in. {appUser.first_name || 'No first name'} email is {appUser.email}</h2>
) : (
<Router>
<div>
<h2>Home you not logged in.</h2>
<nav>
<ul>
<li> <Link to="/login">Login</Link> </li>
<li> <Link to="/signup">Sign Up</Link> </li>
</ul>
</nav>
<Route path="/login" component={Login} />
<Route path="/signup" component={Signup} />
</div>
</Router>
);
}
function Login (props: RouteComponentProps<TParams>) {
const [userName, setUserName] = useState("");
const [password, setPassword] = useState("");
const {authTokens, setAuthTokens} = useAuth();
const [isError, setIsError] = useState(false);
const referer = props.location.state ? props.location.state.referer : '/';
const [appUser, setAppUser] = useState({pk:-1, username: '', email: '', first_name: '', last_name:''});
const [isLoggedIn, setLoggedIn] = useState(false);
useEffect(()=> {
// axios.get('http://localhost:8000/api/v1/rest-auth/user/', {headers: { 'Authorization': `Token ${token}`}})
if (!!authTokens) {
const url = 'http://localhost:8000/rest-auth/user/';
const withCredentials = true;
const method = 'get';
const headers = {
"Authorization": "Token " + authTokens['key'] + " "
};
axios.request({method, url, withCredentials, headers}).then(response => {
console.log('Login() /rest-auth/user response is ', response);
// setAppUser({...appUser, ...response.data});
setAppUser((appUser) => ({...appUser, ...response.data}));
setLoggedIn(true);
})
// .catch(error => { setAppUser(null); setLoggedIn(false); })
}
}, [authTokens]);
function postLogin() {
const url = 'http://localhost:8000/rest-auth/login/';
const withCredentials = true;
const method = 'post';
const data = {"username": userName, "password": password};
// BIG WARNING!!!
// password is being passed unencrypted and in the clear.
axios.request({url, withCredentials, data, method}).then(
result => {
// console.log('Login.postLogin.else result is :', result);
if (result.status === 200) {
setAuthTokens(result.data);
} else {
setIsError(true);
}
});// .catch(e => { console.log('Login.postLogin.catch e is :', e); setIsError(true); });
} // end postLogin
if (authTokens) {
console.log('Login page authTokens is ', authTokens);
console.log('Login page appUser is ', appUser);
// UPDATE
// SET sessionStorage cookie
window.sessionStorage.setItem('key', authTokens['key']);
return <Redirect to={referer}/>;
} else {
console.log('Login page authTokens is ', authTokens);
console.log('Login page appUser is ', appUser);
}
return (
<Card>
<Logo src={logoImg} />
<Form>
<Input
type="username"
placeholder="username"
value={userName}
onChange={(e: { target: { value: React.SetStateAction<string>; }; }) => { setUserName(e.target.value); }}
/>
<Input
type="password"
value={password}
onChange={(e: { target: { value: React.SetStateAction<string>; }; }) => { setPassword(e.target.value); }}
placeholder="password" />
<Button onClick={postLogin}>Sign In</Button>
</Form>
<Link to="/signup">Don't have an account?</Link>
</Card>
);
}
const App: React.FunctionComponent = () => {
const [authTokens, setAuthTokens] = useState(undefined); //type: AuthTokens | undefined (see auth.js)
// <AuthContext.Provider value={false}>
return (
<AuthContext.Provider value={{ authTokens, setAuthTokens }}>
<Router>
<div>
<nav>
<ul>
<li> <Link to="/">Home</Link> </li>
<li> <Link to="/products/1">First Product</Link> </li>
<li> <Link to="/products/2">Second Product</Link> </li>
<li> <Link to="/admin">Admin Page</Link> </li>
</ul>
</nav>
<Route path="/" exact component={Index} />
<Route path="/products/:id" component={Product} />
<Route path="/login" component={Login} />
<Route path="/signup" component={Signup} />
<PrivateRoute path="/admin" component={Admin} />
<Route path="/confirm-email/:id" component={ConfirmEmail} />
</div>
</Router>
</AuthContext.Provider>
);
};
export default App;
现在,如果我启动我的后端服务器并启动我的应用程序,np start
我可以在浏览器中导航到http://localhost:3000,然后我看到:
如果我单击“登录”链接,我可以登录。这是我登录的屏幕截图:
我确实注意到在显示“主页”时浏览器位置仍然报告http://localhost:3000/login 。但是我们可以看到一个 Sheldon Plankton 已经登录。现在,如果我点击我拥有的链接:主页、产品 1、产品 2 和管理员,一切都很好。但是,如果我在浏览器中输入一个 URL,例如http://localhost:3000/,我会丢失我的登录信息,然后我会看到:
这表明 Sheldon Plankton 不再登录。
我做错了什么?
解决方案
那是因为您将登录凭据存储在会话的内存中。当您自行更改 URL 或刷新页面时,应用程序的路由器(例如 React 路由器)并不是网站的路由。加载它的是浏览器的本机导航系统。结果,会话中先前存在的所有内存都将被重置。它正在加载一个新页面,而不是在同一页面中导航(不要让 URL 更改来欺骗您它实际上是一个新页面——这就是 HTML5 浏览器历史记录的工作方式)。
您需要在会话之间保持状态。您可以使用 cookie sessionStorage
、 或localStorage
. 不要忘记,如果您使用会话存储,如果您关闭浏览器,您的用户信息将被删除。
推荐阅读
- google-analytics - Google Analytics Realtime API v3 是否仍处于测试阶段?
- reactjs - Firebase RDB 存在未按预期工作
- javascript - 从父元素传递道具
- java - 如何
使用 Selenium 和 Java 将字符串拆分为列表 - mongodb - 我应该在 mongodb 事务中创建用于重试的递归方法以避免文档级锁定错误吗?
- file - 为摘要使用单独的文件
- java - 如何在 Java 中为 Math.Context 设置比例?
- angular - 使用 controlValueAccessor 的自定义组件 - ts-Lint 错误:在定义之前使用了组件
- android - 使用 useSafeArea Hook 时反应原生 Android TextInput 问题
- android - 如何在flutter android中将文本添加到默认启动画面