我想使用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发送给客户端吗?您是否将其与令牌一起存储到本地浏览器存储中,并在删除令牌时将其删除?
也许我弄错了,但是目前我只知道如何检查某人是否已通过身份验证,而不是哪一个。客户端需要知道当前登录的用户。
答案 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
})