使用 React、Nodejs 和 Mariadb 数据库在会话过期时重新登录时进行身份验证回退

问题描述 投票:0回答:1

这是我在这里发布的第一期,所以请耐心等待:D 我正在创建一个社交应用程序,目前我正在数据库关闭时实施回退。在我的

Login.jsx
组件中,我正在获取数据,后端如下所示:

fetch(getApiUrl('/auth/login'), {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(values),
  credentials: 'include',
  })
  .then(res => {
    const jsn = res.json();
    if (res.ok) {
      handleAuthLogin(navigate, revalidate)(jsn);
    } else if (jsn.message) {
     toast.error(<div className='text-lg'>{jsn.message}</div>);
    }
  })
  .catch((err) => {
    console.error(err);
    if (err.message === 'Failed to fetch') {
      navigate('/service-unavailable');
    } else if (err.message === 'Invalid token' || err.message === 'Unauthorized') {
      navigate('/login');
    } else {
      toast.error(<div className='text-lg'>Failed to connect to the server. Please check your connection.</div>);
    }
  });

然后在我的后端,我使用 JWT 来哈希密码。这只是我的

index.js
文件中的代码片段:

/**
 * Middleware for handling authentication routes.
 * All routes starting with '/auth' will be handled by the authRouter.
 */
app.use(checkToken);
app.use('/auth', authRouter);
app.use(userRouter);

// Set up the /users endpoint
app.get('/users', async (req, res) => {
    const { name } = req.query.name;

    // Query the database using Prisma
    const users = await prisma.user.findMany({
        where: {
            name: {
                contains: name,
            },
        },
    });

    // Send the result back to the client
    res.json(users);
});

和 checkToken 组件:

const checkToken = (req, res, next) => {
    const token = req.headers['authorization'];
    console.log('token: ', token);

    if (!token) {
        return res.status(403).send({ message: 'No token provided.' });
    }

    try {
        const decoded = decodeJwt(token);
        req.userId = decoded.id;
        next();
    } catch (error) {
        return res.status(401).send({ message: 'Unauthorized! Session expired.' });
    }
};

以及表单验证:

const formVerification = (req, res, action) => {
  // Extract the data from the request body
  const data = req.body;
  console.log(data);

  // Validate the data against the schema
  schema
    .validate(data)
    .catch(err => {
      // If validation fails, send a 422 status code and the validation error messages
      res.status(422).json({ status: 422, message: err.errors });
    })
    .then(valid => {
      if (valid) {
        const cred = {
          usr: data.username,
          pwd: data.password
        };

        if (action === "login") {
          // Login handle
          handleLogin(cred, req, res);
        } else if (action === "signup") {
          // Signup handle
          handleSignup(cred, req, res);
        } else {
          throw new Error("Invalid action");
        }
      } else {
        res.status(400).json({ status: 400, message: "Invalid request" });
      }
    });
}

使用 jwt 编码的handleLogin组件:

const hashPwd = (pwd) => {
    return createHash("md5").update(pwd).digest("hex");
};

const encodeJwt = ({ id, name }) => {
    return sign({ id, name }, secret, { expiresIn: "1h" });
};

const decodeJwt = (jwt) => {
    try {
        return verify(jwt, secret);
    } catch (e) {
        throw new Error('Failed to decode JWT: ' + e.message);
    }
};
const handleLogin = ({ usr, pwd }, req, res) => {
    console.log('usr: ', usr, 'pwd: ', pwd);
    getUser(usr, (err, user) => {
        if (err) {
            console.error(err);
            return res.status(500).json({ status: 500, message: "Database is offline" });
        }
        console.log('user: ', user);
        if (!user) {
            return res.status(404).json({ status: 404, message: "User not found" });
        } else if (user.credentials.hash !== hashPwd(pwd)) {
            return res.status(401).json({ status: 401, message: "Invalid password" });
        } else {
            return res.status(200).json({ status: 200, jwt: encodeJwt(user), user: { id: user.id, name: user.name } });
        }
    }, true);
};

您现在可能会意识到,很难确定错误到底在哪里,而且代码有点大,所以我将链接整个代码的存储库:

https://github.com/Patri22k/social-app

最后,错误是什么?在安装上我遇到错误

user.js:29 GET http://localhost:5000/user/@me 401 (Unauthorized)
。哦,顺便说一句,这是 user.js 文件:

// No token stored in local store
const NO_TOKEN_ERR = "no_token";

const UserContext = createContext(null);
const UserProvider = ({ children }) => {
    const [user, setUser] = useState(null);
    const [fetching, setFetching] = useState(false);
    const [error, setError] = useState(null);
    
    // Obtain new data
    const revalidate = () => {
        // Obtain local JWT val
        const jwt = localStorage.getItem("jwt");
        if (!jwt) {
            setError(NO_TOKEN_ERR);
            return;
        }
        const init = {
            method: "GET",
            headers: {
                'Authorization': `Bearer ${jwt}`
            }
        }
        // Fetch that thing
        fetch(getApiUrl("/user/@me"), init) // line 29
            .then(res => res.json())
            .then(({ user }) => setUser(user))
            .catch((e) => setError(e.message))
            .finally(() => setFetching(false))
    }
    // rest of the code
}

在我将任何值传递给表单后,令牌未定义,并且出现错误

POST http://localhost:5000/auth/login 403 (Forbidden)
。但在挂载点上有一个哈希令牌。 我想实现什么目标? 我想在数据库关闭时进行后备,它将重定向到
/service-unavailable
url。就是这样。当它打开时,它应该像任何具有表单验证的社交应用程序一样工作。

javascript reactjs node.js database mariadb
1个回答
0
投票

如果您需要使用

const token = req.headers['authorization'];
访问令牌,您需要在 POST 请求标头中手动传递令牌,如下所示:

fetch(getApiUrl('/auth/login'), {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${your_token}`
    },
    body: JSON.stringify(values),
    credentials: 'include',
})

并且在你的后端你需要删除'Bearer',不要忘记它!

const token = req.headers['authorization'].split(' ')[1]

仅设置:

credentials: 'include'
不会将令牌放在正确的位置,这就是为什么你的令牌是
undefined
并且http状态是401,就像你编程的那样。

不要担心错误如此简单;我花了很多天的时间才在我的代码中意识到这种事情......答案总是在我们从未想过会出错的事情中。

希望有帮助;)

© www.soinside.com 2019 - 2024. All rights reserved.