对应内容:#10 Adding User Authentication | Build a Complete App with GraphQL, Node.js, MongoDB and React.js
这一部分的主要内容是进行用户的身份验证,并且发放一个持久化 token
。
- 实现登录验证的
resolver
- 创建middleware
Auth.js
- 在其他
resolver
中添加对登录身份的验证,替换固定的用户id
1 实现登录验证
先安装jsonwebtoken
cnpm install --save jsonwebtoken
在./graphql/resolver/auth.js
中创建函数 login
。
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const User = require("../../models/user");
const rootValue = {
createUser: async args => {
...
},
login: async ({ email, password }) => {
try {
const user = await User.findOne({email: email});
if (!user) {
throw new Error("User does not exist!");
}
const isEqual = await bcrypt.compare(password, user.password);
if (!isEqual) {
throw new Error("password is incorrect!");
}
const ONE_WEEK = 7 * 24 * 60 * 60;
const token = jwt.sign({
userId: user._id,
email: user.email
},
"secret", {
expiresIn: ONE_WEEK
});
return {
_id: user.id,
token: token,
tokenExpiration: ONE_WEEK
};
} catch (error) {
throw error;
}
}
};
module.exports = rootValue;
2 创建middleware is-auth
在根目录下新建文件夹和文件./middleware/isauth.js
。
const jwt = require("jsonwebtoken");
module.exports = (req, res, next) => {
const authHeader = req.get("Authorization");
if (!authHeader) {
req.isAuth = false;
return next();
}
const token = authHeader.split(' ')[1];
if (!token || token === "") {
req.isAuth = false;
return next();
}
let decodedToken;
try {
decodedToken = jwt.verify(token, "secret");
} catch (error) {
req.isAuth = false;
return next();
}
if (!decodedToken) {
req.isAuth = false;
return next();
}
req.isAuth = true;
req.userId = decodedToken.userId;
next();
}
3 在其他resolver中添加身份验证,替换固定的用户id
先在app.js中添加刚才创建的中间件。
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const { graphqlHTTP } = require("express-graphql");
const isAuth = require("./middleware/is-auth");
const app = express();
app.use( bodyParser.json() );
// 要注意放的顺序,一定要在use("/graphql")之上
app.use(isAuth);
const graphQlSchema = require("./graphql/schema/index");
const graphQlRootValue = require("./graphql/resolvers/index");
...
在 ./graphql/resolver/booking.js
中举例,其实每个resolver的参数不只有args,后面可以由req,res等,只不过之前没用上。
我们在中间件中,在req对象中强行添加了isAuth属性和userId属性。
const Event = require("../../models/events");
const Booking = require("../../models/booking");
const { transformBooking, transformEvent } = require("./merge");
const rootValue = {
bookings: async (_, req) => {
if (!req.isAuth) {
throw new Error("un authenticated!");
}
try {
const bookings = await Booking.find();
return bookings.map(booking => {
return transformBooking(booking);
})
} catch (error) {
console.log(error);
throw error;
}
},
bookEvent: async (args, req) => {
if (!req.isAuth) {
throw new Error("un authenticated!");
}
try {
const fetchedEvent = await Event.findById(args.eventId);
const booking = new Booking({
user: req.userId,
event: fetchedEvent
});
const result = await booking.save();
return transformBooking(result);
} catch (error) {
console.log(error);
throw error;
}
},
cancelBooking: async (args, req) => {
if (!req.isAuth) {
throw new Error("un authenticated!");
}
try {
const booking = await Booking.findById(args.bookingId).populate("event");
const event = transformEvent(booking.event);
await Booking.deleteOne({ _id: args.bookingId });
return event;
} catch (error) {
console.log(error);
throw error;
}
}
};
module.exports = rootValue;
后续可以用userId属性,替换之前在创建booking和event时使用的固定用户id。例如:
bookEvent: async (args, req) => {
if (!req.isAuth) {
throw new Error("un authenticated!");
}
try {
const fetchedEvent = await Event.findById(args.eventId);
const booking = new Booking({
+> user: req.userId,
event: fetchedEvent
});
const result = await booking.save();
return transformBooking(result);
} catch (error) {
console.log(error);
throw error;
}
},