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

问题描述

我正在使用 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

像这样创建一个 express 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"
  },

现在进入身份验证部分:

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

在 .env 文件中:

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

制作一个passport.js文件:

这会将用户的令牌添加到所有请求标头中,因为我们在 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,};

这个文件附加到你的路由的头部,像这样:

router.post("/example/get",authUser,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",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,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",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
          },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} />

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