首页 > 解决方案 > 为什么我在我的 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 不再登录。

我做错了什么?

标签: javascriptreactjstypescript

解决方案


那是因为您将登录凭据存储在会话的内存中。当您自行更改 URL 或刷新页面时,应用程序的路由器(例如 React 路由器)并不是网站的路由。加载它的是浏览器的本机导航系统。结果,会话中先前存在的所有内存都将被重置。它正在加载一个新页面,而不是在同一页面中导航(不要让 URL 更改来欺骗您它实际上是一个新页面——这就是 HTML5 浏览器历史记录的工作方式)。

您需要在会话之间保持状态。您可以使用 cookie sessionStorage、 或localStorage. 不要忘记,如果您使用会话存储,如果您关闭浏览器,您的用户信息将被删除。


推荐阅读