【Node.js】トークンを保持しているユーザーのみ、特定のデータを送信する
Node.js

【Node.js】トークンを保持しているユーザーのみ、特定のデータを送信する

作成日:2021年11月11日
更新日:2021年11月11日

トークンを検証して、トークンを保持しているユーザーのみ、book のデータを表示させます。

コードは、前回までのコードを使用します。

nodejs-jwt

【Node.js】jsonwebtokenを使って、JWTを設定する

index.js

js
const express = require("express");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const authRoutes = require("./routes/auth");
const bookRoutes = require("./routes/book");
const app = express();
app.use(bodyParser.json());
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader(
"Access-Control-Allow-Methods",
"GET, POST, PUT, PATCH, DELETE, OPTION"
);
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
next();
});
app.use("/auth", authRoutes);
app.use("/book", bookRoutes);
mongoose
.connect(
"mongodb+srv://Nao:abcdefgh.q1atv.mongodb.net/myFirstDatabase?retryWrites=true&w=majority"
)
.then(() => {
app.listen(8000);
console.log("Server is running ...");
})
.catch((err) => {
console.log(err);
throw err;
});

routes/auth.js

js
const express = require("express");
const { body } = require("express-validator");
const authController = require("../controllers/auth");
const router = express.Router();
router.post(
"/sign_up",
[
body("email")
.isEmail()
.withMessage("メールアドレスを入力してください")
.normalizeEmail(),
body("password").trim().isLength({ min: 4 }),
],
authController.signUp
);
router.post("/login", authController.login);
module.exports = router;

routes/book.js

js
const express = require("express");
const { body } = require("express-validator");
const bookController = require("../controllers/book");
const router = express.Router();
router.get("/get", bookController.getBook);
router.post(
"/post",
body("content").trim().isLength({ min: 4 }),
bookController.postBook
);
module.exports = router;

model/user.js

js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const userShema = new Schema({
email: String,
password: String,
});
module.exports = mongoose.model("User", userShema);

model/book.js

js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const bookShema = new Schema({
title: String,
content: String,
});
module.exports = mongoose.model("Book", bookShema);

controllers/auth.js

js
const User = require("../model/user");
const { validationResult } = require("express-validator");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
exports.signUp = (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res
.status(400)
.json({ message: "validation errors", errors: errors.array() });
}
const email = req.body.email;
const password = req.body.password;
bcrypt
.hash(password, 10)
.then((hashedPassword) => {
const user = new User({
email: email,
password: hashedPassword,
});
return user.save();
})
.then(() => {
res.status(201).json({
message: "User transmission completed!",
});
})
.catch((err) => console.log(err));
};
exports.login = (req, res, next) => {
const email = req.body.email;
const password = req.body.password;
User.findOne({ email: email })
.then((result) => {
if (!result) {
const error = new Error("メールアドレスが存在しません");
error.statusCode = 401;
throw error;
}
return bcrypt.compare(password, result.password);
})
.then((result) => {
if (!result) {
const error = new Error("パスワードが間違っています");
error.statusCode = 401;
throw error;
}
const token = jwt.sign(
{
email: email,
},
"abcabcabcabcd",
{ expiresIn: "1h" }
);
return res.status(200).json({ token: token });
})
.catch((err) => console.log(err));
};

controllers/book.js

js
const { validationResult } = require("express-validator");
const Book = require("../model/book");
exports.getBook = (req, res, next) => {
Book.find().then((books) => {
res.status(200).json({ message: "fetch books successfully", books: books });
});
};
exports.postBook = (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res
.status(400)
.json({ message: "validation errors", errors: errors.array() });
}
const title = req.body.title;
const content = req.body.content;
const bookData = new Book({
title: title,
content: content,
});
bookData
.save()
.then((result) => {
console.log(result);
res.status(201).json({
message: "Book transmission completed!",
book: result,
});
})
.catch((err) => {
console.log(err);
});
};
// const { validationResult } = require("express-validator");
// exports.getBook = (req, res, next) => {
// res.status(200).json({
// books: [
// {
// id: "aaaaaa",
// title: "オズの魔法使い",
// content: "前に進んでいったら、何とかなるという話。",
// },
// ],
// });
// };
// exports.postBook = (req, res, next) => {
// const errors = validationResult(req);
// if (!errors.isEmpty()) {
// return res
// .status(400)
// .json({ message: "validation errors", errors: errors.array() });
// }
// const id = (Math.random() + 1).toString(36).slice(2, 8);
// const title = req.body.title;
// const content = req.body.content;
// res.status(201).json({
// message: "Data transmission completed!",
// post: { id: id, title: title, content: content },
// });
// };

トークンを検証するためのミドルウェアを作成

トークンの認証が出来た場合は、データを見られる様にします。

また、認証が出来ない場合は、エラーを表示するためのミドルウェアを作成します。

middleware フォルダを作成し、isAuth.js を作成します。

jsonwebtoken を呼び出します。

js
const jwt = require("jsonwebtoken");

外部で使える様にするため、エクスポートします。

またトークンは、リクエストヘッダーに乗せますので、リクエストヘッダーのAuthorizationを取得します。

js
module.exports = (req, res, next) => {
const authHeader = req.get("Authorization");
};

リクエストヘッダーにAuthorizationがない場合、エラーを出力します。

js
module.exports = (req, res, next) => {
const authHeader = req.get("Authorization");
if (!authHeader) {
const error = new Error("Not Authenticated");
error.statusCode = 401;
throw error;
}
};

Authorization の中身は、通常『Bearer トークン』です。

『Bearer 』が必要ないので split を使って分割し、トークンの方を指定します。

js
module.exports = (req, res, next) => {
const authHeader = req.get("Authorization");
if (!authHeader) {
const error = new Error("Not Authenticated");
error.statusCode = 401;
throw error;
}
const token = authHeader.split(" ")[1];
};

トークンをデコードして、バックエンドのトークンとあっているか、verify を使って確認します。

js
module.exports = (req, res, next) => {
const authHeader = req.get("Authorization");
if (!authHeader) {
const error = new Error("Not Authenticated");
error.statusCode = 401;
throw error;
}
const token = authHeader.split(" ")[1];
let decodedToken;
try {
decodedToken = jwt.verify(token, "abcabcabcabcd");
} catch (err) {
err.statusCode = 500;
throw err;
}
};

処理を継続させたいので、next()を書きます。

js
module.exports = (req, res, next) => {
const authHeader = req.get("Authorization");
if (!authHeader) {
const error = new Error("Not Authenticated");
error.statusCode = 401;
throw error;
}
const token = authHeader.split(" ")[1];
let decodedToken;
try {
decodedToken = jwt.verify(token, "abcabcabcabcd");
} catch (err) {
err.statusCode = 500;
throw err;
}
next();
};

これで、認証のミドルウェアの作成が一通り完了しました。

ミドルウェアをデータに接続

先程作成したミドルウェアを、book のデータに接続します。

routes フォルダの book.js を開きます。

まずは、isAuthを呼び出します。

js
const isAuth = require("../middleware/isAuth");

getの第二引数に、isAuthを入れます。

js
router.get("/get", isAuth, bookController.getBook);

これで完成しましたので、Postman で確認します。

image2

Send ボタンを押してみると、

image3

ステータスコード 401 のエラーが発生しました。

これは、Postman でトークンを設定していなかったから起こったエラーです。

トークンを設定するために、まずは、auth/login に POST して、トークンを取得します。

image4

GET の book/get に戻り、URL 下にある Authorizationw をクリックします。

image5

Type で『Bearer Token』を選択し、先程のトークンを貼り付けます。

image6

改めて、『Send』をクリックすると、

image7

データが取得できました。

トークンが違っていたり、トークンを取得してから、バックエンドで設定した 1 時間が経過すると、以下の様にステータスコード 500 のエラーが発生します。

image8

© 2024あずきぱんウェブスタジオ