Reactjs JWT在React中的安全路由

Reactjs JWT在React中的安全路由,reactjs,jwt,jwt-auth,Reactjs,Jwt,Jwt Auth,我正在将JWT身份验证添加到我正在开发的博客应用程序中。在服务器端(使用Nodejs构建),我正在创建令牌,并在成功登录后将其发送回去。在客户端,我将令牌保存在LocalStorage中。当我登录并检查dev tools中的application选项卡时,我可以看到令牌。在博客发布到的服务器路由上,我检查身份验证。如果令牌已通过身份验证,则日志将发送到数据库,但如果我删除令牌或更改令牌,然后发出post请求,则请求将失败,如预期的那样 到目前为止还不错 我所困惑的是如何限制对博客编辑器驻留在客户

我正在将JWT身份验证添加到我正在开发的博客应用程序中。在服务器端(使用Nodejs构建),我正在创建令牌,并在成功登录后将其发送回去。在客户端,我将令牌保存在LocalStorage中。当我登录并检查dev tools中的application选项卡时,我可以看到令牌。在博客发布到的服务器路由上,我检查身份验证。如果令牌已通过身份验证,则日志将发送到数据库,但如果我删除令牌或更改令牌,然后发出post请求,则请求将失败,如预期的那样

到目前为止还不错

我所困惑的是如何限制对博客编辑器驻留在客户端的页面的访问。如果人们没有经过身份验证,他们应该根本无法访问此页面,即使没有经过身份验证,他们也无法发布

服务器上的登录路由:

router.post('/login', async (req, res, next) => {
    const cursor = User.collection.find({username: req.body.username}, {username: 1, _id: 1, password: 1});
    if(!(await cursor.hasNext())) {
        return res.status(401).json({ message: 'Cannot find user with that username' });
    }
    const user = await cursor.next();
    try {
    if(await bcrypt.compare(req.body.password, user.password)) {
        const token = jwt.sign({
            email: user.email,
            userId: user._id
        }, process.env.JWT_SECRET, { expiresIn: "1h" })
        return res.status(201).json({
            message: 'User Authenticated',
            token: token
        });
    } else {
        return res.status(400).json({ 
            authenticated: false,
            username: req.body.username,
            password: req.body.password
        })
    }
    } catch (err) {
        return res.status(500).json({ message: err })
    }
});
如何在服务器上检查令牌身份验证:

const jwt = require('jsonwebtoken');

module.exports = (req, res, next) => {
    try {
        const token = req.headers.authorization;
        console.log(token);
        const decoded = jwt.verify(token, process.env.JWT_SECRET);
        req.userData = decoded;
        next();
    } catch (error) {
        return res.status(401).json({ message: 'Auth Failed' })
    }

}
我的客户端登录路径获取:

handleSubmit(event) {
        event.preventDefault();
        const formData = {
            username: event.target.username.value,
            password: event.target.password.value
        }
        fetch('http://localhost:4000/user/login', {
            method: "POST",
            mode: "cors",
            body: JSON.stringify(formData),
            headers: {
                "Content-Type": "application/json"
            }
        })
        .then(res => res.json())
        .then(res => {
            localStorage.setItem('authorization', res.token);
            console.log(res);
        })
        .catch(err => console.error(err)) 
    }
下面是我在编辑器所在的博客发布路径上从客户端发出的获取调用:

handleSubmit = (event) => {
      event.preventDefault();
      const data = new FormData(event.target);
      const body = event.target.postBody.value;
      const postTitle = event.target.title.value;

      console.log(event.target);
      console.log(data);
      console.log(event.target.postBody.value);

      fetch('http://localhost:4000/blog', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          "Authorization": localStorage.getItem('authorization')
        },
        mode: 'cors',
        body: JSON.stringify({
          title: postTitle,
          postBody: body
        })
      })
      .then(res => res.json())
      .then(err => console.error(err))
    }
所以,正如我所说,一切都按预期进行,但我不希望人们在未经身份验证的情况下能够访问编辑器页面。我想我应该检查一下本地存储中是否存在令牌,然后重定向?但是,我是否也需要检查客户机上的令牌是否也可以在服务器上进行身份验证?因此,无论何时有人导航到该页面或我想限制访问的任何其他页面,我都需要发布到服务器来进行检查吗?想想看,如果用户已经通过身份验证,我也不希望他们能够访问登录页面

我听说人们使用Redux来管理组件之间的状态,但我真的不想走这条路,至少现在还不想,因为这个项目是为了学习,我真的不想从Redux或其他类似的东西开始,直到我更好地掌握React本身。我不知道我是否需要Redux,根据我的理解,这足以让我知道我可能不需要它

这是一个与我以前从PHP会话中使用的流程完全不同的流程,我很难理解它


我意识到你们可能并不真的需要看到所有这些代码,但我也希望一些更有经验的人能够看到这些代码,并指出我可能犯的错误或我可以改进的地方。

所以这就是我现在提出的,如果有人知道更好的方法,我肯定会接受建议

我创建了一个名为CheckAuth的类,它本质上只是向服务器发出一个GET请求,并将jwt与之一起发送

checkAuth.js:

class CheckAuth {
    constructor() {
        this.auth = false;
    }

    async checkLogin() {
        console.log(localStorage.getItem("authorization"));
        let data = await fetch('http://localhost:4000/auth', {
            method: "GET",
            mode: "cors",
            headers: {
                "Content-Type": "application/json",
                "authorization": localStorage.getItem("authorization")
            }
        })
        return data.json();

    }

    logout(cb) {
        localStorage.removeItem('authenticated')
        this.auth = false;
        cb();
    }

    async isAuthenticated() {
        const data = await this.checkLogin()
        return data;
    }

}

export default new CheckAuth();
然后,在只有登录用户才能看到的页面上,我正在做一个简单的检查,看看他们是否拥有令牌,以及它在
componentDidMount()
中是否有效


我不确定,因为我来自Vue,但React是否有导航卫士?如果是这样,在登录时,您可以在选择的状态管理中声明set变量,该变量显示经过身份验证的用户的类型。在导航卫士中,您可以放置一个元数据,说明此路由是针对普通用户还是访问编辑器页面的用户。如果您计划在应用程序树中分布多个受保护的路由,最简单的路径是Redux(或Context)。否则,一旦加载编辑器页面,它应该有条件地呈现微调器(
加载…
),并将令牌发送到后端进行身份验证。然后,后端可以用一个简单的
布尔值
true
=用户已验证或
false
=未授权用户)响应客户端。然后,客户端可以使用此布尔响应,加载页面或将用户重定向到登录页面。请参见此处的示例:(本地状态和redux)或此处的示例:(上下文)
componentDidMount() {
        const check = checkAuth.isAuthenticated();
        console.log(check);
        check.then(res => {
            console.log(res);
            if(res.authenticated !== true) {
                this.props.history.push("/login");
            }
        })
        .catch(err => { console.error(err) })
    }