Как авторизовать токен jwt на стороне клиента PrivateRoute? - PullRequest
1 голос
/ 05 июля 2019

Я пытаюсь отправить простой GET-запрос в конечную точку авторизации на внутреннем сервере, чтобы проверить токен пользователя jwt, который хранится в localStorage после успешной аутентификации (входа в систему). Я установил вспомогательную функцию (см. Ниже), которая будет отправлять запрос GET на сервер с токеном jwt в заголовке. Если получено состояние 200, я возвращаю значение true (авторизовано).

Эта вспомогательная функция вызывается в моем PrivateRoute HOC, и если true, то возвращает частный компонент, а если false, будет перенаправлять в Login.

По сути, я могу заставить работать защищенные маршруты, если просто декодирую токен и проверяю, существует ли токен, и, следовательно, пользователь вошел в систему. Однако это не проверка токена на сервере jwt verify и, следовательно, небезопасный. Я не уверен, нужно ли мне где-нибудь реализовывать асинхронный код? Я знаю, что шаг проверки должен существовать во внутреннем интерфейсе, и внешний интерфейс должен просто отправить запрос API с токеном в заголовке, но я не могу получить эту работу с частными маршрутами.

Я также попытался запустить вспомогательную функцию на главном компоненте приложения в качестве ловушки useEffect и обновить переменную состояния «loggedIn» в зависимости от того, возвращает ли вспомогательная функция авторизованный или нет, который затем может быть передан в качестве подпорки другим компонентам. , Я сделал это для того, чтобы компонент Nav Bar отображал «SIGN IN / REGISTER» или «SIGN OUT» в зависимости от того, является ли состояние «loggedIn» истинным или ложным.

Функция проверки токена на стороне сервера:

const jwt = require('jsonwebtoken');

module.exports = function (req, res, next) {
    const token = req.header('auth-token');
    if(!token) return res.status(401).send('Access Denied');

    try {
        const decoded = jwt.verify(token, process.env.TOKEN_SECRET);
        req.user = decoded;
        next();
    } catch (err) {
        res.status(400).send('Invalid Token');
    }
}

Вспомогательная функция:

import React from 'react';
    import { Redirect } from 'react-router-dom';
    import axios from 'axios';

    const checkAuth = {
        isAuthorised: false,
        userID: void (0),

        authorise: function () {

            const token = window.localStorage.getItem("access_token");
            if (!token) {
                console.log('Invalid Token');
                window.localStorage.setItem("access_token", null);
            }

            const response = axios({
                method: 'get',
                url: 'http://localhost:3001/api/verify',
                headers: { 'auth-token': token }
            })
            const data = response.data;
            if (response.status === 200) {
                this.isAuthorised = true;
                this.userID = data._id;
                return this.isAuthorised;
            }

            return this.isAuthorised;
        },
        logout: function () {
            window.localStorage.setItem("access_token", null);
            this.isAuthorised = false;
            return <Redirect to="/" />
        },
    }

    export default checkAuth;

Частный маршрут HOC:

    import React from 'react';
    import { Redirect, Route } from 'react-router-dom';
    import checkAuth from './helper';

    const PrivateRoute = ({ component: Component, path, ...rest }) => {

    return (
        <Route
            path={path}
            {...rest}
            render={props =>
                checkAuth.authorise()
                    ? <Component {...props} />
                    : <Redirect to={{ pathname: "/login" }} />
            }
        />
    );
        }
    export default PrivateRoute;

Компонент приложения (скрытие импорта):

const App = () => {
  const [loggedIn, setLoggedIn] = useState(false);
  const [user, setUser] = useState();

  const verify = () => {
    if (checkAuth.authorise()) {
      setLoggedIn(true);
      setUser(checkAuth.userID);
      console.log('logged in');
      return true;
    } else {
      console.log('not logged in');
      return false;
    }
  }

  useEffect(() => {
    verify()
  }, [loggedIn]);

  return (
    <div>
      <NavBar loggedIn={loggedIn} user={user} />
      <Router>
        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/story" component={Story} />
          <Route path="/prologue" component={Prologue} />
          <PrivateRoute path="/chapters" component={Chapters} />
          <PrivateRoute path="/forum" component={Forum} />
          <PrivateRoute path="/characters" component={Characters} />
          <PrivateRoute path="/characters/:id" component={CharacterSheet} />
          <Route path="/login" component={Login} />
          <Route path="/register" component={Register} />
          <Route component={NoMatch} />
        </Switch>
      </Router>
    </div>
  );
}

export default App;

Компонент NavBar (только соответствующая часть):

const NavBar = ({ loggedIn, user }) => {
    return (
      ...
          {loggedIn ? (
                    <Nav>
                        <h1 style={{ paddingRight: '30px', color: 'black', fontSize: '32pt'}}>Welcome, {user}</h1>
                        <Nav.Link href="/" onClick={() => checkAuth.logout()}>Sign Out</Nav.Link>
                    </Nav>
                ) : (
                        <Nav>
                            <Nav.Link href="/login">Login</Nav.Link>
                            <Nav.Link href="/register">Register</Nav.Link>
                        </Nav>
                    )}
                ...
    )
}

export default NavBar;

Я ожидаю, что когда пользователь войдет в систему, NavBar будет обновляться, чтобы показать, что пользователь вошел в систему на основании функции помощника авторизации, вызываемой в результате ловушки useEffect.

Я бы также ожидал, что когда пользователь перенаправит на один из защищенных маршрутов (PrivateRoute), вспомогательная функция запустится и вернет true (после входа в систему), а затем отобразит.

Я получаю ожидаемые результаты, когда вместо вызова API в вспомогательной функции используется следующий код (однако, как я понимаю, это НЕ правильная авторизация):

    authorise: function () {

        const token = window.localStorage.getItem("access_token");
        if (!token) {
            console.log('Invalid Token');
            window.localStorage.setItem("access_token", "");
        }

        try {
            const decoded = decode(token);
            console.log(decoded)
            if (decoded) {
                this.isAuthorised = true;
                this.userID = decoded._id;
                return this.isAuthorised;
            }
        }
        catch {
            console.log('invalid token')
        }

        return this.isAuthorised;
    },

На данный момент вход в систему, похоже, ничего не делает - хотя маркер пользователя получен и сохранен в LocalStorage.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...