【问题标题】:JWT Secure Routes in ReactReact 中的 JWT 安全路由
【发布时间】:2020-04-30 17:57:41
【问题描述】:

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

到目前为止一切顺利。

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

服务器上的登录路径:

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)) 
    }

这是我在编辑器所在的博客发布路径上来自客户端的 fetch 调用:

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 会话中习惯的流程完全不同,而且我在理解它时遇到了一些麻烦。

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

【问题讨论】:

  • 我不确定,因为我来自 Vue,但 React 有导航守卫吗?如果是这样,在登录时,您可以在您选择的状态管理中声明 set 变量,该变量显示经过身份验证的用户是什么类型的用户。在导航守卫中,您可以设置此路线是针对普通用户还是针对有权访问编辑器页面的用户的元数据
  • 如果您计划在应用程序树中分布多个受保护的路由,最简单的路径是 Redux(或 Context)。否则,一旦加载了编辑器页面,它应该有条件地渲染一个微调器 (loading...) 并将令牌发送到后端进行身份验证。然后,后端可以使用简单的Booleantrue = 已验证用户或false= 未授权用户)响应客户端。然后,客户端可以使用此布尔响应并加载页面或将用户重定向到登录页面。
  • 在此处查看示例:stackoverflow.com/questions/53197248/…(本地状态和 redux)或此处:codesandbox.io/s/protected-route-root-context-ho0uj(上下文)

标签: reactjs jwt jwt-auth


【解决方案1】:

所以这就是我现在想出的,如果有人知道更好的方法,我绝对愿意接受建议。

我创建了一个名为 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() 内是否有效。

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) })
    }

【讨论】:

    猜你喜欢
    • 2020-10-20
    • 2020-06-26
    • 1970-01-01
    • 1970-01-01
    • 2017-08-21
    • 2016-11-13
    • 1970-01-01
    • 2021-03-17
    • 2013-10-05
    相关资源
    最近更新 更多