使用令牌进行身份验证来标识后端和前端上的用户

时间:2019-03-10 09:08:16

标签: javascript node.js express authentication jwt

我想使用Json Web令牌创建一个简单的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发送给客户端吗?您是否将其与令牌一起存储到本地浏览器存储中,并在删除令牌时将其删除?

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

2 个答案:

答案 0 :(得分:1)

您有两个选择:

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

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

2)在JWT中存储用户或会话ID,然后在数据库/内存Map中查找该用户或会话ID以获取用户。当数据库与此同时使用时,您可以节省带宽并跟踪更改。

// 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}`);

答案 1 :(得分:0)

我尝试回答您的问题:

  

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

这取决于您是否需要用户才能发送。我通常通过创建中间件(例如authMiddleware)将用户对象存储在req.user中,

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中

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