Graphql 初体验 第八章 | #10 Adding User Authentication

对应内容:#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;
        }
    },
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值