使用Node.js、AngularJS和JWT进行用户名和密码验证

3

我要创建一个关于IT技术的生产应用程序,需要翻译的内容涉及AngularJS和RESTFul API。

现在我已经创建了部分RESTFul API。

这个API将在登录时生成一个用户对象,但是我不想通过HTTP头明文传输密码。

让您了解我的API:

当登录时:

    var jwt = require('jwt-simple');

// Route: /login
module.exports = function (express, sequelize, router) {

    var DataTypes = require("sequelize");
    var crypto = require('crypto');

    var User = sequelize.define('user', {
            id: DataTypes.INTEGER,
            username: DataTypes.STRING,
            name: DataTypes.STRING,
            organization_id: DataTypes.INTEGER,
            type_id: DataTypes.INTEGER,
            division_id: DataTypes.INTEGER
        }, {
            freezeTableName: true,
            instanceMethods: {
                retrieveByNamePassword: function (username, onSuccess, onError) {
                    User.find({where: {username: username}}, {raw: true})
                        .then(onSuccess).catch(onError);
                }
            }
        }
    );

    router.route('/login')

        .post(function (req, res) {
            var user = User.build();

            var username = req.body.username || '';
            var password = req.body.password || '';

            if (username == '' || password == '') {
                res.status(401);
                res.json({
                    "status": 401,
                    "message": "Invalid credentials"
                });
                return;
            }

            user.retrieveByNamePassword(username, function (users) {
                if (users) {
                    // If authentication is success, we will generate a token
                    // and dispatch it to the client
                    res.json(genToken(users));
                    return;
                } else {
                    // If authentication fails, we send a 401 back
                    res.status(401);
                    res.json({
                        "status": 401,
                        "message": "Invalid credentials"
                    });
                    return;
                }
            }, function (error) {
                res.status(401);
                res.json({
                    "status": 401,
                    "message": "Invalid credentials"
                });
                return;
            });
        });

    var genToken = function(user) {
        var expires = expiresIn(7); // 7 days
        var token = jwt.encode({
            exp: expires,
            user_id: user.id,
            organization_id: user.organization_id,
            type_id: user.type_id,
            division_id: user.division_id

        }, require('./config/secret')());

        return {
            token: token,
            expires: expires,
            user: user
        };
    };

    var expiresIn = function(numDays) {
        var dateObj = new Date();
        return dateObj.setDate(dateObj.getDate() + numDays);
    };

目前看来(因为我还不确定如何处理)只需要查找用户名并检查该用户名是否存在。

我的问题很简单,如何对密码进行加密,并在到达API后验证它?

2个回答

2
JWT可以与加密和非加密数据一起使用。但是,无论您是否加密要传输的信息,都需要进行签名过程,以便在阅读信息之前验证接收到的信息未被篡改。
对于通过URL在域之间传输登录状态的用例,我认为最好的选择是完全不包括密码在您的JWT中。
以以下过程为例:
1.将登录详细信息通过HTTP发布到domain1。
2.验证用户。如果成功,则创建包含以下内容的JWT:
2.1. exp:到期时间(假设当前时间+最多10至15秒)
2.2. iat:令牌创建时间
2.3. 电子邮件:用户的电子邮件地址
2.4. 不包括用户密码(或任何其他敏感数据)。
3.将用户重定向到带有JWT令牌的domain2中的URL。
4.您的其他服务器/域可以检查JWT签名、iat和exp时间,以验证JWT未被篡改并且有效。
5.如果所有内容都有效,请将用户登录到另一个域中。
这是有效的,因为只要JWT数据验证,第二个域实际上不需要知道您的用户密码,因为domain1服务器已经验证了登录并且只是告诉domain2接受它。只要保持JWT秘密密钥完全保密,这应该是相当安全的。我还鼓励尽可能在两个服务器上定期自动更改您的秘密密钥。
请注意:使用此方法,任何获得JWT的人都可以查看其中包含的数据-因此不要在那里传输任何机密或敏感信息。
请注意2:这种方法会对您在JWT的到期字段中设置的时间段内进行重放攻击。如果第三方在该时间段内获得令牌,则他们也将能够使用令牌登录为该用户。因此,您必须尽可能保持到期字段低,以减少风险。

那我无法避免通过HTTP发布用户名和密码吗?如果我使用套接字连接,然后将元素推送到套接字,会怎样呢? - Marc Rasmussen
@MarcRasmussen 我的回答主要是关于使用JWT,只需对数据进行签名即可创建令牌。您可以选择在将其编码为JWT之前加密要发送的数据(使用公钥/私钥),然后在稍后读取令牌时再解密它。有关此完整信息,请参见此处:https://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-40,而您使用的任何JWT库都很可能具有简单的方法来允许您执行此操作。请注意 - 我以上述方式回答是因为最好根本不发送敏感数据,而不是加密它。 - Joel Cox
@Joel:你知道在更改密码/注销时使jwt失效的最佳实践是什么吗?-https://dev59.com/gl4b5IYBdhLWcg3wtjzY - Gopinath Shiva

1
这已经很晚了,但是你能不能在用户模型之后添加一个使用加密的钩子呢?(像这样:https://www.youtube.com/watch?v=pquxHIBx8ks
// User model...
},   {
hooks: {
    beforeValidate: function() {

    },
    afterValidate: function(User) {
        User.hashword = bcrypt.hashSync(User.hashword, 10);



    },
    beforeCreate: function() {

    },
    afterCreate: function() {

    }
}
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接