【问题标题】:Identify a user on backend and frontend using tokens for authentication使用令牌在后端和前端识别用户进行身份验证
【发布时间】:2019-07-31 20:05:48
【问题描述】:

我想使用 Json Web Tokens 创建一个简单的 Express API。当用户尝试登录时,我会执行此操作

exports.signIn = async (req, res, next) => {
    const { username, password } = req.body;

    // get user from database
    const userQueryResult = await userQueries.getUserByName([username]);

    // return if database errors occured
    if (userQueryResult.err) {
        res.status(500).json({
            message: userQueryResult.err.message
        });
    }

    const users = userQueryResult.result;
    const user = users[0];

    // no user found
    if (!user) {
        res.status(401).json({
            message: 'Auth failed'
        });
    }

    try {
        // validate the password
        const passwordMatch = bcrypt.compareSync(password, user.passwordHash);

        // wrong credentials
        if (!passwordMatch) {
            res.status(401).json({
                message: 'Auth failed'
            });
        }

        const token = jwt.sign({
            user.id
        }, tokenSecret, {
                tokenExpiration
            });

        res.status(200).json({
            message: 'Auth succeeded',
            token
        });
    } catch (err) {
        res.status(401).json({
            message: 'Auth failed'
        });
    }
};

生成一个新令牌并发送给客户端。我是否也必须将用户对象发送给客户端?因为目前我只检查用户是否经过身份验证,而不是哪个。

对于受保护的路由,我检查用户是否使用中间件功能进行了身份验证

module.exports = (req, res, next) => {
    try {
        const rawToken = req.headers.authorization; // bearer <token>
        const token = rawToken.split(' ')[1]; // extract the token
        req.userData = jwt.verify(token, tokenSecret); // verify this token
        next();
    } catch (error) {
        res.status(401).json({
            message: 'Auth failed'
        });
    }
}

那么您是否也将用户 ID 发送给客户端?是否将其与令牌一起存储到本地浏览器存储中,并在删除令牌时将其删除?

也许我弄错了,但目前我只知道如何检查某人是否通过了身份验证,但不知道是哪一个。客户端需要知道当前登录的是哪个用户。

【问题讨论】:

    标签: javascript node.js express authentication jwt


    【解决方案1】:

    我试着回答你的问题:

    我是否也必须将用户对象发送给客户端?

    这取决于您是否需要用户,您可以发送它。我通常通过创建一个中间件来将用户对象存储在req.user 中,例如像这样的 authMiddleware:

    const { User } = require('../models/user');
    let authenticate = function (req, res, next) {
        let token = req.cookies.access_token;
        User.findByToken(token).then((user) => {
            req.user = user;
            req.token = token;
            next();
        }).catch(e => {
            res.redirect('/');
        })
    }
    module.exports = {
        authenticate
    }
    

    我假设你使用 mongodb 和 mongoose。 什么是 findByToken() 方法?这是自定义的猫鼬模型方法:

    // user model
    const mongoose = require('mongoose');
    let UserSchema = new mongoose.Schema({
       ...your model schema,
       tokens: [{  //here is tokens array
            access: {
                type: String,
                required: true
            },
            token: {
                type: String,
                required: true
            }
        }],
    }
    UserSchema.statics.findByToken = function (token) {
        let user = this;
        let decoded;
        try {
            decoded = jwt.verify(token, 'secret')
        } catch (e) {
            return Promise.reject();
        }
        return user.findOne({
            '_id': decoded._id,
            'tokens.access': 'auth',
            'tokens.token': token
        })
    }
    
    let User = mongoose.model('User', UserSchema);
    module.exports = { User };
    

    那么您是否也将用户 ID 发送给客户端?是否将其与令牌一起存储到本地浏览器存储中,并在删除令牌时将其删除?

    在唱完一个令牌后,您必须将令牌存储在用户模型文档的令牌数组中

    UserSchema.methods.generateAuthToken = function () {
        let user = this;
        let access = 'auth';
        let token = jwt.sign({ '_id': user._id.toHexString(), access }, 'secret').toString();
        user.tokens = user.tokens.concat([{ access, token }]);
    
        return user.save().then(() => {
            return token;
        })
    
    }
    

    建议在cookie中存储token:

    res.cookie('access_token', token, {
        maxAge: 3600000,
        httpOnly: true
    })
    

    【讨论】:

      【解决方案2】:

      你有两个选择:

      1) 您可以将用户存储在 JWT 中。

      当另一个请求进来时,您可以在后端轻松访问它(虽然不是在前端,因为 JWT 是加密的)。这可能比每次都查找用户要快。但是,您必须在每次完成请求时将用户发送到前端并再次返回,这会减慢速度,您还会复制数据,如果数据库记录发生更改,您将不会注意到

      2) 在 JWT 中存储用户或会话 ID,然后在数据库/内存映射中查找以获取用户。当数据库使用 mkre 时,您可以保存带宽并跟踪更改。

      // on login
      const token = jwt.sign({ id: user.id }, tokenSecret, { tokenExpiration });
      // or
      const token = jwt.sign({ user }, tokenSecret, { tokenExpiration  });
      

      为了能够在客户端显示用户的数据,您必须以未加密的方式发送它,例如:

       res.send(`Your name is ${user.name} and you got the id ${user.id}`);
      

      【讨论】:

        【解决方案3】:

        生成一个新令牌并发送给客户端。我是否也必须将用户对象发送给客户端?因为目前我只检查用户是否经过身份验证,而不是检查哪个用户。

        从技术上讲,您还可以发送用户对象(和任何其他信息),但通常您想要发送回的只是 jwt 令牌,因为它包含您在客户端需要的所有信息(有效负载)。

        那么您是否也将用户 ID 发送给客户端?

        与上一个答案相同,如果您需要客户端的用户 ID,请将其包含在 jwt 有效负载中。

        是否将其与令牌一起存储到本地浏览器存储并在删除令牌时将其删除?

        如果您需要它作为永久的东西(页面重新加载)它们是的,请将 jwt 令牌或其他任何东西保存到本地存储中。

        也许我弄错了,但目前我只知道如何检查某人是否通过了身份验证,但不知道是哪一个。客户端需要知道当前登录的是哪个用户。

        您正在寻找的是您在服务器端检查的用户授权(而不是身份验证)。

        【讨论】:

          猜你喜欢
          • 2020-12-03
          • 2014-07-16
          • 1970-01-01
          • 1970-01-01
          • 2021-01-19
          • 1970-01-01
          • 1970-01-01
          • 2019-08-31
          • 1970-01-01
          相关资源
          最近更新 更多