AuthとJWT、MongoDB、およびPostgresでエクスプレス


認証は、常に理解し、達成するトリッキーです.実装の概念と高レベルについてより深くする場合は、次の記事を読んでください.
  • Authentication and Authorization in Concept
  • Implmenting Auth at a High Level
  • 基本的には、Authを持つAPIを持つために以下のようにビルドする必要があります.
  • 優先データベースとのインタフェースへのユーザモデル
  • /新しいユーザを作成するためのサインアップルート
  • /ユーザーが存在することを確認するログインルート、パスワードが正しく、すべてのチェックアウト
  • /我々が使うどんなクッキーも破壊するためのログアウト経路
  • どのようなバックエンドのフレームワークや言語を使用すると、上記は本質的に目標です.私たちはこのチュートリアルのために急行を使用して、目的の終わり結果を得る2つの方法を示すためにMongoDBとPostgresを使用します.

    始める
  • つのフォルダを作成します.express_mongo and express_postgres 我々は、各フォルダに別のアプリケーションを作成されます.各フォルダのセットアップは同じです.

  • エクスプレスアプリセットアップ
    両方のフォルダについては、次のようにします.
  • 必要なフォルダを作成するmkdir connection models controllers utils
  • 次のファイルを作成するtouch server.js connection/db.js utils/middleware.js utils/cors.js controllers/HomeController.js

  • 次のライブラリをインストールしますnpm install express dotenv cors morgan cookie-parser

  • バックエンドフレームワーク

  • Dotenvは私たちを使用することができます.env file env変数を定義する

  • Corsは我々のCors頭をセットします(我々のフロントエンドを含む他のウェブサイトはAPIに要求をすることができます)

  • モーガンはトラブルシューティングを助けて、問題を特定します

  • クッキーパーサは、クッキーが入ってくるリクエストに含まれていることを解析し、REQに保存します.クッキー
  • クリエイトア.env and .gitignore ファイル
  • env . env
    PORT = 4000
    
    ギティノー
    /node_modules
    .env
    
  • utils/corsで.私たちがセキュリティを強化したいならば、我々のCorsヘッダーを構成するために以下を加えましょう.
  • // whitelist of URLS that can make a request to your server
    // to allow all urls, whitelist should have "*" as its first element
    const whitelist = ["*"]
    
    // if whitelist starts with "*" then all traffic allowed, otherwise if origin url is not in whitelist, request is blocked.
    const corsOptions = {
      origin: function (origin, callback) {
        if (whitelist[0] === "*") {
          callback(null, true)
        } else {
          if (whitelist.indexOf(origin) !== -1) {
            callback(null, true)
          } else {
            callback(new Error("Not allowed by CORS"))
          }
        }
      },
    }
    
    module.exports = corsOptions
    
  • 最初のルートで我々のhomecontrollerをつくってくださいcontrollers/HomeController
  • // New Express Router
    const router = require("express").Router()
    
    // Router Middleware
    
    // Router Routes
    router.get("/", (req, res) => {
      res.json({ response: "server is working" })
    })
    
    // Export Router
    module.exports = router
    
  • 中でミドルウェアを登録するための機能を作成しましょうutils/middleware.js
  • const express = require("express")
    const cookieParser = require("cookie-parser")
    const morgan = require("morgan")
    const cors = require("cors")
    const corsOptions = require("./cors")
    const HomeController = require("../controllers/HomeController")
    
    // function to create context property in every request with shared data
    const applicationContext = (req, res, next) => {
      // data to share can be added in this object
      req.context = {}
      // move on to next middleware
      next()
    }
    
    const registerMiddleware = app => {
      app.use(cors(corsOptions)) // cors headers
      app.use(cookieParser()) // parse cookies
      app.use(express.json()) // parse json bodies
      app.use(morgan("tiny")) // logging
      app.use(applicationContext) // add context object to request
      app.use("/", HomeController) // register homecontroller routes for  "/" urls
    }
    
    module.exports = registerMiddleware
    
  • サーバで次のようにします.js
  • require("dotenv").config() // load variables from .env
    const express = require("express")
    const registerMiddleware = require("./utils/middleware")
    
    // Grab any ENV variables to be used, set default values in case .env file missing
    const { PORT = 3000 } = process.env
    
    // The Application Object
    const app = express()
    
    // registerMiddleware
    registerMiddleware(app)
    
    // Server listener
    app.listen(PORT, () => console.log(`listening on port ${PORT}`))
    
    また、私たちがこれまでに出したコードを持つためにこのテンプレートを使用することができます.
  • https://github.com/Alex-Merced-Templates/express_starter

  • 蒙古
  • マングースを取り付ける
  • MongoDBで無料Mongoデータベースを取得します.comまたはmongoをインストールし、ローカルのmongoサーバーを実行すると、URLをあなたの.env . また、あなたが好きであるどんなストリングでもありえる秘密と呼ばれている変数を定義してください.
  • PORT=4000
    DATABASE_URL=mongodb+srv://username:[email protected]/databaseName?retryWrites=true&w=majority
    SECRET=thisIsMySecret
    
    *上記のデータベースURLは例ですが、独自のURLを取得する必要があります

    接続
    インサイドconnection/db.js
    require("dotenv").config()
    const mongoose = require("mongoose")
    
    // get env variables
    const { DATABASE_URL } = process.env
    
    // connect to mongoose
    mongoose.connect(DATABASE_URL, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    })
    
    // Connection Messages
    mongoose.connection
      .on("open", () => console.log("Connected to Mongo"))
      .on("close", () => console.log("Disconnected from Mongo"))
      .on("error", error => console.log(error))
    
    // export connection
    module.exports = mongoose
    

    ユーザモデル
    ファイルを作るmodels/User.js
    // import connection, grab schema and model
    const { Schema, model } = require("../connection/db")
    
    // define user schema
    const userSchema = new Schema(
      {
        username: { type: String, required: true, unique: true },
        password: { type: String, required: true },
        role: { type: String, required: true, default: "general" },
      },
      { timestamps: true }
    )
    
    // define user model
    const User = model("User", userSchema)
    
    // export User
    module.exports = User
    

    藤堂モデルmodels/Todo.js
    // import connection, grab schema and model
    const { Schema, model } = require("../connection/db")
    
    // define Todo schema
    const todoSchema = new Schema(
      {
        message: { type: String, required: true },
        completed: { type: Boolean, default: false },
        username: { type: String, required: true },
      },
      { timestamps: true }
    )
    
    // define Todo model
    const Todo = model("Todo", todoSchema)
    
    // export Todo
    module.exports = Todo
    
    すべてのコントローラーが容易にそれらにアクセスできるようになりました.utils/middleware.js
    const express = require("express")
    const cookieParser = require("cookie-parser")
    const morgan = require("morgan")
    const cors = require("cors")
    const corsOptions = require("./cors")
    // import controllers
    const HomeController = require("../controllers/HomeController")
    // import models
    const User = require("../models/User")
    const Todo = require("../models/Todo")
    
    // function to create context property in every request with shared data
    const applicationContext = (req, res, next) => {
      // data to share can be added in this object
      req.context = {
        models: { User, Todo },
      }
      // move on to next middleware
      next()
    }
    
    const registerMiddleware = app => {
      app.use(cors(corsOptions)) // cors headers
      app.use(cookieParser()) // parse cookies
      app.use(express.json()) // parse json bodies
      app.use(morgan("tiny")) // logging
      app.use(applicationContext) // add context object to request
      app.use("/", HomeController) // register homecontroller routes for  "/" urls
    }
    
    module.exports = registerMiddleware
    

    Authコントローラ
  • bcryptとjwtのインストールnpm install bcryptjs jsonwebtoken
  • クリエイトcontrollers/AuthController.js
  • // New Express Router
    const router = require("express").Router();
    const bcrypt = require("bcryptjs");
    const jwt = require("jsonwebtoken");
    require("dotenv").config();
    
    // Router Middleware
    
    //signup route "/auth/signup"
    router.post("/signup", async (req, res) => {
      try {
        // grab model from context
        const User = req.context.models.User;
        // hash the password
        req.body.password = await bcrypt.hash(req.body.password, 10);
        // create new User
        const user = await User.create(req.body);
        // respond, send back user without password
        const response = { username: user.username, role: user.role };
        res.json(response);
      } catch (error) {
        res.status(400).json({ error });
      }
    });
    
    // login route "/auth/login"
    router.post("/login", async (req, res) => {
      try {
          console.count("login")
        // grab model from context
        const User = req.context.models.User;
        console.count("login")
        // grab username and password
        const { username, password } = req.body;
        // see if user exists
        const user = await User.findOne({ username });
        if (user) {
          // check if password matches
          const doesItMatch = await bcrypt.compare(password, user.password);
          if (doesItMatch) {
            // remove password from user data
            const userData = { username: user.username, role: user.role };
            // sign token
            const token = jwt.sign(userData, process.env.SECRET);
            // respond
            res.cookie("token", token, { httpOnly: true }).json(userData);
          } else {
            throw "Passwords do not match";
          }
        } else {
          throw "User Does Not Exist";
        }
      } catch (error) {
        res.status(400).json({ error });
      }
    });
    
    // logout "/auth/logout"
    router.get("/logout", async (req, res) => {
        res.clearCookie("token").json({response: "You are Logged Out"})
    })
    
    // Export Router
    module.exports = router;
    
  • 私たちのミドルウェアでAuthControllerを登録してくださいutils/middleware.js
  • const express = require("express")
    const cookieParser = require("cookie-parser")
    const morgan = require("morgan")
    const cors = require("cors")
    const corsOptions = require("./cors")
    // import controllers
    const HomeController = require("../controllers/HomeController")
    const AuthController = require("../controllers/AuthController")
    // import models
    const User = require("../models/User")
    const Todo = require("../models/Todo")
    
    // function to create context property in every request with shared data
    const applicationContext = (req, res, next) => {
        // data to share can be added in this object
        req.context = {
            models: {User, Todo}
        }
        // move on to next middleware
        next()
    }
    
    
    const registerMiddleware = (app) => {
        app.use(cors(corsOptions)) // cors headers
        app.use(cookieParser()) // parse cookies
        app.use(express.json()) // parse json bodies
        app.use(morgan("tiny")) // logging
        app.use(applicationContext) // add context object to request
        app.use("/", HomeController) // register homecontroller routes for  "/" urls
        app.use("/auth", AuthController) // register homecontroller routes for  "/auth" urls
    }
    
    module.exports = registerMiddleware
    
    これで、ログイン、ログインとログアウト!それでは、ユーザが彼らのためにTODOSを作成できるルートを作成しましょう.

    Authミドルウェア
    正しいユーザーだけがアクセスできるようにし、TODOを修正するには、ユーザがログインしてログインしているかどうかをチェックする必要があります.これは、送信されたトークンクッキーをチェックすることによってすべて行うことができます.我々は、我々が我々が必要とする認可を望むどんなルートででも使うことができるカスタムメイドのミドルウェアをつくります!utils/auth.js
    require("dotenv").config();
    const jwt = require("jsonwebtoken");
    
    const isUserLoggedIn = async (req, res, next) => {
      try {
        // check if the token is in the cookies
        const { token = false } = req.cookies;
        if (token) {
          // verify token
          const payload = await jwt.verify(token, process.env.SECRET);
          // add payload to request
          req.payload = payload;
          // move on
          next();
        } else {
          throw "Not Logged In";
        }
      } catch (error) {
        res.status(400).json({ error });
      }
    };
    
    module.exports = isUserLoggedIn
    

    TODOコントローラcontrollers/TodoController
    // New Express Router
    const router = require("express").Router();
    const isUserLoggedIn = require("../utils/auth");
    
    // Index Route "/todo", returns all todos for that user
    router.get("/", isUserLoggedIn, async (req, res) => {
      try {
        const Todo = req.context.models.Todo;
        res.json(await Todo.find({ username: req.payload.username }));
      } catch (error) {
        res.status(400).json({ error });
      }
    });
    
    // Create Route "/todo", creates a new todo
    router.post("/", isUserLoggedIn, async (req, res) => {
      try {
        const Todo = req.context.models.Todo;
        req.body.username = req.payload.username;
        res.json(await Todo.create(req.body));
      } catch (error) {
        res.status(400).json({ error });
      }
    });
    
    // update Route "/todo/:id", updates a todo
    router.put("/:id", isUserLoggedIn, async (req, res) => {
      try {
        const Todo = req.context.models.Todo;
        const id = req.params.id;
        res.json(await Todo.findByIdAndUpdate(id, req.body, { new: true }));
      } catch (error) {
        res.status(400).json({ error });
      }
    });
    
    // destroy Route "/todo/:id", deletes a todo
    router.delete("/:id", isUserLoggedIn, async (req, res) => {
      try {
        const Todo = req.context.models.Todo;
        const id = req.params.id;
        res.json(await Todo.findByIdAndRemove(id));
      } catch (error) {
        res.status(400).json({ error });
      }
    });
    
    //export router
    module.exports = router;
    
  • それから、我々のミドルウェアでルータを登録してください
  • utils/middleware
    const express = require("express")
    const cookieParser = require("cookie-parser")
    const morgan = require("morgan")
    const cors = require("cors")
    const corsOptions = require("./cors")
    // import controllers
    const HomeController = require("../controllers/HomeController")
    const AuthController = require("../controllers/AuthController")
    const TodoController = require("../controllers/TodoController")
    // import models
    const User = require("../models/User")
    const Todo = require("../models/Todo")
    
    // function to create context property in every request with shared data
    const applicationContext = (req, res, next) => {
        // data to share can be added in this object
        req.context = {
            models: {User, Todo}
        }
        // move on to next middleware
        next()
    }
    
    
    const registerMiddleware = (app) => {
        app.use(cors(corsOptions)) // cors headers
        app.use(cookieParser()) // parse cookies
        app.use(express.json()) // parse json bodies
        app.use(morgan("tiny")) // logging
        app.use(applicationContext) // add context object to request
        app.use("/", HomeController) // register homecontroller routes for  "/" urls
        app.use("/auth", AuthController) // register homecontroller routes for  "/auth" urls
        app.use("/todo", TodoController) // register todocontroller routes for  "/todo" urls
    }
    
    module.exports = registerMiddleware
    
    conGATS今すぐ認証を使用してmongo/Expressで藤堂APIを構築している!
    この点までコードを見ることができます.
    https://github.com/Alex-Merced-Templates/express_starter/tree/mongo

    今すぐPostgresで!
  • シーケンスをインストールnpm install sequalize
  • データベースドライバのインストールnpm install pg pg-hstore
  • いくつかのデータベースプロバイダからPostgresデータベースを取得するか、Postgresサーバーをコンピュータ上で実行します
  • PORT=4000
    DATABASE_URL=postgres://username:password@localhost:5432/express_app
    SECRET=thisIsMySecret
    
    *上記のデータベースURLは例ですが、独自のURLを取得する必要があります

    接続
    インサイドconnection/db.js
    require("dotenv").config();
    const { Sequelize } = require("sequelize");
    
    // connect to database
    const sequelize = new Sequelize(process.env.DATABASE_URL)
    
    
    // check if connection established
    async function checkConnection() {
        try {
          await sequelize.authenticate();
          console.log("Connection has been established successfully.");
        } catch (error) {
          console.error("Unable to connect to the database:", error);
        }
      }
    
    checkConnection()
    
    //export connection
    module.exports = sequelize;
    

    ユーザモデル
    ファイルを作るmodels/User.js
    // import connection, grab schema and model
    const sequalize = require("../connection/db")
    const {DataTypes} = require("sequelize")
    
    // Define User Model
    const User = sequalize.define("User", {
        username: {type: DataTypes.STRING, allowNull: false, unique: true},
        password: {type: DataTypes.STRING, allowNull: false},
        role: {type: DataTypes.STRING, allowNull: false, defaultValue: "general"}
    }, {tableName: "users", timestamps: true})
    
    // create the table if doesn't exist
    
    async function createTable(){
        await User.sync()
    }
    
    createTable()
    
    // export User
    module.exports = User
    

    藤堂モデルmodels/Todo.js
    // import connection, grab schema and model
    const sequalize = require("../connection/db")
    const {DataTypes} = require("sequelize")
    
    // Define Todo Model
    const Todo = sequalize.define("Todo", {
        username: {type: DataTypes.STRING, allowNull: false},
        message: {type: DataTypes.STRING}
    }, {tableName: "todos", timestamps: true})
    
    // create the table if doesn't exist
    
    async function createTable(){
        await Todo.sync()
    }
    
    createTable()
    
    // export User
    module.exports = Todo
    
    すべてのコントローラーが容易にそれらにアクセスできるようになりました.utils/middleware.js
    const express = require("express")
    const cookieParser = require("cookie-parser")
    const morgan = require("morgan")
    const cors = require("cors")
    const corsOptions = require("./cors")
    // import controllers
    const HomeController = require("../controllers/HomeController")
    // import models
    const User = require("../models/User")
    const Todo = require("../models/Todo")
    
    // function to create context property in every request with shared data
    const applicationContext = (req, res, next) => {
      // data to share can be added in this object
      req.context = {
        models: { User, Todo },
      }
      // move on to next middleware
      next()
    }
    
    const registerMiddleware = app => {
      app.use(cors(corsOptions)) // cors headers
      app.use(cookieParser()) // parse cookies
      app.use(express.json()) // parse json bodies
      app.use(morgan("tiny")) // logging
      app.use(applicationContext) // add context object to request
      app.use("/", HomeController) // register homecontroller routes for  "/" urls
    }
    
    module.exports = registerMiddleware
    

    Authコントローラ
  • bcryptとjwtのインストールnpm install bcryptjs jsonwebtoken
  • クリエイトcontrollers/AuthController.js
  • // New Express Router
    const router = require("express").Router();
    const bcrypt = require("bcryptjs");
    const jwt = require("jsonwebtoken");
    require("dotenv").config();
    
    // Router Middleware
    
    //signup route "/auth/signup"
    router.post("/signup", async (req, res) => {
      try {
        // grab model from context
        const User = req.context.models.User;
        // hash the password
        req.body.password = await bcrypt.hash(req.body.password, 10);
        // create new User
        const user = await User.create(req.body);
        // respond, send back user without password
        const response = { username: user.username, role: user.role };
        res.json(response);
      } catch (error) {
        res.status(400).json({ error });
      }
    });
    
    // login route "/auth/login"
    router.post("/login", async (req, res) => {
      try {
        console.count("login");
        // grab model from context
        const User = req.context.models.User;
        console.count("login");
        // grab username and password
        const { username, password } = req.body;
        // see if user exists
        const user = await User.findOne({ where: { username } });
        if (user) {
          // check if password matches
          const doesItMatch = await bcrypt.compare(password, user.password);
          if (doesItMatch) {
            // remove password from user data
            const userData = { username: user.username, role: user.role };
            // sign token
            const token = jwt.sign(userData, process.env.SECRET);
            // respond
            res.cookie("token", token, { httpOnly: true }).json(userData);
          } else {
            throw "Passwords do not match";
          }
        } else {
          throw "User Does Not Exist";
        }
      } catch (error) {
        res.status(400).json({ error });
      }
    });
    
    // logout "/auth/logout"
    router.get("/logout", async (req, res) => {
      res.clearCookie("token").json({ response: "You are Logged Out" });
    });
    
    // Export Router
    module.exports = router;
    
  • 私たちのミドルウェアでAuthControllerを登録してくださいutils/middleware.js
  • const express = require("express")
    const cookieParser = require("cookie-parser")
    const morgan = require("morgan")
    const cors = require("cors")
    const corsOptions = require("./cors")
    // import controllers
    const HomeController = require("../controllers/HomeController")
    const AuthController = require("../controllers/AuthController")
    // import models
    const User = require("../models/User")
    const Todo = require("../models/Todo")
    
    // function to create context property in every request with shared data
    const applicationContext = (req, res, next) => {
        // data to share can be added in this object
        req.context = {
            models: {User, Todo}
        }
        // move on to next middleware
        next()
    }
    
    
    const registerMiddleware = (app) => {
        app.use(cors(corsOptions)) // cors headers
        app.use(cookieParser()) // parse cookies
        app.use(express.json()) // parse json bodies
        app.use(morgan("tiny")) // logging
        app.use(applicationContext) // add context object to request
        app.use("/", HomeController) // register homecontroller routes for  "/" urls
        app.use("/auth", AuthController) // register homecontroller routes for  "/auth" urls
    }
    
    module.exports = registerMiddleware
    
    これで、ログイン、ログインとログアウト!それでは、ユーザが彼らのためにTODOSを作成できるルートを作成しましょう.

    Authミドルウェア
    正しいユーザーだけがアクセスできるようにし、TODOを修正するには、ユーザがログインしてログインしているかどうかをチェックする必要があります.これは、送信されたトークンクッキーをチェックすることによってすべて行うことができます.我々は、我々が我々が必要とする認可を望むどんなルートででも使うことができるカスタムメイドのミドルウェアをつくります!utils/auth.js
    require("dotenv").config();
    const jwt = require("jsonwebtoken");
    
    const isUserLoggedIn = async (req, res, next) => {
      try {
        // check if the token is in the cookies
        const { token = false } = req.cookies;
        if (token) {
          // verify token
          const payload = await jwt.verify(token, process.env.SECRET);
          // add payload to request
          req.payload = payload;
          // move on
          next();
        } else {
          throw "Not Logged In";
        }
      } catch (error) {
        res.status(400).json({ error });
      }
    };
    
    module.exports = isUserLoggedIn
    

    TODOコントローラcontrollers/TodoController
    // New Express Router
    const router = require("express").Router();
    const isUserLoggedIn = require("../utils/auth");
    
    // Index Route "/todo", returns all todos for that user
    router.get("/", isUserLoggedIn, async (req, res) => {
      try {
        const Todo = req.context.models.Todo;
        res.json(await Todo.findAll({ where: { username: req.payload.username } }));
      } catch (error) {
        res.status(400).json({ error });
      }
    });
    
    // Create Route "/todo", creates a new todo
    router.post("/", isUserLoggedIn, async (req, res) => {
      try {
        const Todo = req.context.models.Todo;
        req.body.username = req.payload.username;
        res.json(await Todo.create(req.body));
      } catch (error) {
        res.status(400).json({ error });
      }
    });
    
    // update Route "/todo/:id", updates a todo
    router.put("/:id", isUserLoggedIn, async (req, res) => {
      try {
        const Todo = req.context.models.Todo;
        const id = req.params.id;
        res.json(await Todo.update(req.body, { where: { id } }));
      } catch (error) {
        res.status(400).json({ error });
      }
    });
    
    // destroy Route "/todo/:id", deletes a todo
    router.delete("/:id", isUserLoggedIn, async (req, res) => {
      try {
        const Todo = req.context.models.Todo;
        const id = req.params.id;
        res.json(await Todo.destroy({ where: { id } }));
      } catch (error) {
        res.status(400).json({ error });
      }
    });
    
    //export router
    module.exports = router;
    
  • それから、我々のミドルウェアでルータを登録してください
  • utils/middleware
    const express = require("express")
    const cookieParser = require("cookie-parser")
    const morgan = require("morgan")
    const cors = require("cors")
    const corsOptions = require("./cors")
    // import controllers
    const HomeController = require("../controllers/HomeController")
    const AuthController = require("../controllers/AuthController")
    const TodoController = require("../controllers/TodoController")
    // import models
    const User = require("../models/User")
    const Todo = require("../models/Todo")
    
    // function to create context property in every request with shared data
    const applicationContext = (req, res, next) => {
        // data to share can be added in this object
        req.context = {
            models: {User, Todo}
        }
        // move on to next middleware
        next()
    }
    
    
    const registerMiddleware = (app) => {
        app.use(cors(corsOptions)) // cors headers
        app.use(cookieParser()) // parse cookies
        app.use(express.json()) // parse json bodies
        app.use(morgan("tiny")) // logging
        app.use(applicationContext) // add context object to request
        app.use("/", HomeController) // register homecontroller routes for  "/" urls
        app.use("/auth", AuthController) // register homecontroller routes for  "/auth" urls
        app.use("/todo", TodoController) // register todocontroller routes for  "/todo" urls
    }
    
    module.exports = registerMiddleware
    
    あなたは今、認証を持つpostgress/expressでtodo APIを構築しました!
    この点までコードを見ることができます.
    https://github.com/Alex-Merced-Templates/express_starter/tree/postgres

    さらに取る
    ネオ4 JやAlanGodbのようなグラフデータベースで再びこれを達成しようとすると、両方の無料層と自分のクラウドデータベースサービスを持っているので、なぜ!
  • Neo4J Tutorial
  • ArangoDB Tutorial