在我的 React 应用程序中,设置间隔在浏览器刷新时变得清晰

Set interval gets clear on browser refresh in my react application

我正在使用 setInterval 函数执行 API 调用以在一段时间后获取刷新令牌。每次我刷新浏览器 setInterval 计时器都会清零并从零开始计数。我的访问令牌过期并且刷新令牌永远不会收到呼叫并且用户已注销。有什么办法可以解决这个问题吗?

useEffect(() => {
  const interval = setInterval(() => { 
    setTokenByReSendingAuth(); //dispatch action
  }, 300000);

  return () => clearInterval(interval);
}, [setTokenByReSendingAuth]);

使用 MERN:

这是您的依赖项:

jwt-decode: https://www.npmjs.com/package/jwt-decode

passport-jwt: http://www.passportjs.org/packages/passport-jwt/

护照: https://www.npmjs.com/package/passport

jsonwebtoken: https://www.npmjs.com/package/jsonwebtoken

bcryptjs: https://www.npmjs.com/package/bcryptjs

像这样创建一个快递server.js:

const express = require("express");
const cors = require("cors");
const mongoose = require("mongoose");
const path = require("path");
const passport = require("passport");
const db = require("./config/.env").mongoURI; //uri in .env file

const app = express();
const port = process.env.PORT || 5000;

app.use(cors());
app.use(express.json());

mongoose.connect(db, {
  useNewUrlParser: true,
  useCreateIndex: true,
  useUnifiedTopology: true,
});
const connection = mongoose.connection;
connection.once("open", () => {
  console.log("MongoDB database connection established successfully");
});

const myRouter = require("./routes/example.js");

app.use(passport.initialize()); // used to attatch token to request headers
require("./config/passport")(passport); 

app.use("/example", myRouter);

if (process.env.NODE_ENV === "production") {
  app.use(express.static("client/build"));
  app.get("*", (req, res) => {
    res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
  });
}

app.listen(port, () => {
  console.log(`Server is running on port: ${port}`);
});

安装依赖项:

  "dependencies": {
    "axios": "^0.21.1",
    "bcryptjs": "^2.4.3",
    "concurrently": "^5.3.0",
    "cors": "^2.8.5",
    "dotenv": "^8.2.0",
    "express": "^4.17.1",
    "jsonwebtoken": "^8.5.1",
    "mongoose": "^5.11.8",
    "passport": "^0.4.1",
    "passport-jwt": "^4.0.0",
    "validator": "^13.5.2",
    "nodemon": "^2.0.7"
  },

将此添加到您的 package.json 服务器所在的任何目录中:

  "devDependencies": {
    "nodemon": "^2.0.7"
  },
  "scripts": {
    "start": "node server.js",
    "server": "nodemon server.js"
  },

现在进入 Auth 部分:

为您的护照和 URI 创建一个配置文件夹:

在 .env 文件中:

module.exports = {
  mongoURI: "mongodb+srv://",
  secretOrKey: "abunchofrandomcharacterscreatedwithbcrypt",
};

创建一个passport.js文件:

这会将用户的令牌添加到所有请求 header 中,它会自动 运行ning 因为我们在 server.js 文件中使用了它。

const JwtStrategy = require("passport-jwt").Strategy;
const ExtractJwt = require("passport-jwt").ExtractJwt;
const mongoose = require("mongoose");
const User = mongoose.model("users");
const keys = require("./.env");

const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = keys.secretOrKey;

module.exports = (passport) => {
  passport.use(
    new JwtStrategy(opts, (jwt_payload, done) => {
      User.findById(jwt_payload.id)
        .then((user) => {
          if (user) {
            return done(null, user);
          }
          return done(null, false);
        })
        .catch((err) => console.log(err));
    })
  );
};

为您的后端创建一个中间件文件夹:

添加一个auth.js文件:

const jwt = require("jsonwebtoken");
const config = require("../config/.env").secretOrKey;

function authUser(req, res, next) {
  const authHeader = req.header("Authorization");
  const token = authHeader && authHeader.split(" ")[1];
  // Check for token
  if (!token)
    return res.status(401).json({ msg: "No token, authorization denied" });

  try {
    // Verify token
    const decoded = jwt.verify(token, config);
    // Add user from payload
    req.user = decoded;
    next();
  } catch (e) {
    res.status(400).json({ msg: "Token is not valid" });
  }
}

module.exports = {
  authUser,
};

此文件附加到您在 header 中的路线,如下所示:

router.post("/example/get", authUser, (req, res) => { 
  const { reqData } = req.body; //dont ever put user ids in here
    .catch((err) => {
      res.status(400).json({ msg: err });
    });
});

登录和注册的路径应如下所示:

const router = require("express").Router();
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
const { authUser } = require("../middleware/auth"); //used in the header of auth needed requests
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const keys = require("../config/.env");
const validateRegisterInput = require("../validation/register"); //checks and validates  user register inputs
const validateLoginInput = require("../validation/login"); //checks and validates user register inputs
const User = require("../models/user");
const { Permissions } = require("../models/permissions");

//uses a middleware to validate register inputs, checks if user data exists in db, salts and hashes the password.

router.post("/register", (req, res) => {
  const { errors, isValid } = validateRegisterInput(req.body);

  if (!isValid) {
    return res.status(400).json(errors);
  }

  User.findOne({ email: req.body.email }).then((user) => {
    if (user) {
      return res.status(400).json({ email: "Email already exists" });
    } else {
      const newUser = new User({
        firstName: req.body.firstName,
        lastName: req.body.lastName,
        email: req.body.email,
        password: req.body.password,
      });

      bcrypt.genSalt(10, (err, salt) => {
        bcrypt.hash(newUser.password, salt, (err, hash) => {
          if (err) throw err;
          newUser.password = hash;
          newUser
            .save()
            .then((user) => res.json(user))
            .catch((err) => console.log(err));
        });
      });
    }
  });
});

//login creds are req through this route, the details are compared to the db user collection, and the user data that matches the decoded password and username will be responed back through the token.

router.post("/login", (req, res) => {
  const { errors, isValid } = validateLoginInput(req.body);
  if (!isValid) {
    return res.status(400).json(errors);
  }

  const email = req.body.email;
  const password = req.body.password;

  User.findOne({ email }).then((user) => {
    if (!user) {
      return res.status(404).json({ email: "Email not found" });
    }

    bcrypt.compare(password, user.password).then((isMatch) => {
      if (isMatch) {
        const payload = {
          id: user.id,
          firstName: user.firstName,
        };

        jwt.sign(
          payload,
          keys.secretOrKey,
          {
            expiresIn: 31556926, //expires in a year
          },
          (err, token) => {
            res.json({
              success: true,
              token: "Bearer " + token,
            });
          }
        );
      } else {
        return res
          .status(400)
          .json({ passwordincorrect: "Password incorrect" });
      }
    });
  });
});

module.exports = router;

这基本上就是后端身份验证路由方面的内容,但是为了让客户端在浏览器中获得令牌,您需要将这些内容添加到客户端:

在你的 index.js 中,无论怎样,在每个渲染器的 运行 组件之外添加这个:

这会检查浏览器中是否有 jwttoken,它对其进行解码并将用户数据设置为全局使用的状态。它还会重定向用户。

import setAuthToken from "./utils/setAuthToken";
import jwt_decode from "jwt-decode";

if (localStorage.jwtToken) {
  // Set auth token header auth
  const token = localStorage.jwtToken;
  setAuthToken(token);
  // Decode token and get user info and exp
  const decoded = jwt_decode(token);
  // Set user and isAuthenticated
  store.dispatch(setCurrentUser(decoded)); // using redux, can easily also just use contextApi or something else
  // Check for expired token
  const currentTime = Date.now() / 1000; // to get in milliseconds
  if (decoded.exp < currentTime) {
    // Logout user
    store.dispatch(logoutUser());

    // Redirect to login
    window.location.href = "./";
  }
}

创建登录、注册和注销功能:

import axios from "axios";
import setAuthToken from "../../utils/setAuthToken";
import jwt_decode from "jwt-decode";

import { SET_CURRENT_USER } from "./authTypes"; //puts user data into state
import { showSnackbar } from "../inventory/inventoryActions";

export const registerUser = (userData) => (dispatch) => {
  axios
    .post("/users/register", userData)
    .then(() => {
    console.log("logged in")
    })
    .catch(() => {
     console.log("something wrong")
    });
};

export const loginUser = (userData) => (dispatch) => {
  axios
    .post("/users/login", userData)
    .then((res) => {
      const { token } = res.data;
      localStorage.setItem("jwtToken", token);
      setAuthToken(token);
      const decoded = jwt_decode(token);
      dispatch(setCurrentUser(decoded));
      dispatch(showSnackbar(`Successfully signed in!`, "success", 3000));
    })
    .catch(() => {
      console.log("somethings wrong")
    });
};

export const setCurrentUser = (decoded) => { // used in loginUser
  return {
    type: SET_CURRENT_USER,
    payload: decoded,
  };
};

//removes token from localstorage
export const logoutUser = () => {
  return (dispatch) => {
    localStorage.removeItem("jwtToken");
    setAuthToken(false);
    dispatch(setCurrentUser({}));
  };
};

如果您有任何只希望登录用户访问的私有组件,请使用此 PrivateRoute 组件包装器:

这会将所有未登录主页的用户重定向

import React from "react";
import { Route, Redirect } from "react-router-dom";
import { connect } from "react-redux";
import PropTypes from "prop-types";

const PrivateRoute = ({ component: Component, auth, ...rest }) => (
  <Route
    {...rest}
    render={(props) =>
      auth.isAuthenticated === true ? (
        <Component {...props} />
      ) : (
        <Redirect to="/" />
      )
    }
  />
);

PrivateRoute.propTypes = {
  auth: PropTypes.object.isRequired,
};

const mapStateToProps = (state) => ({
  auth: state.auth,
});

export default connect(mapStateToProps)(PrivateRoute);

将其用作 react-router-dom 元素:

<PrivateRoute exact path="/example" component={privateComponentExample} />

如果您有任何问题,请告诉我。 :)