Commit 6455666d authored by Yoon, Daeki's avatar Yoon, Daeki 😅
Browse files

backend auth 설정

parent 03fca606
{
"arrowParens": "always",
"bracketSpacing": true,
"configPath": "",
"documentSelectors": [],
"embeddedLanguageFormatting": "auto",
"enable": true,
"enableDebugLogs": false,
"endOfLine": "lf",
"htmlWhitespaceSensitivity": "css",
"ignorePath": ".prettierignore",
"insertPragma": false,
"jsxBracketSameLine": false,
"jsxSingleQuote": false,
"prettierPath": "",
"printWidth": 80,
"proseWrap": "preserve",
"quoteProps": "as-needed",
"requireConfig": false,
"requirePragma": false,
"resolveGlobalModules": false,
"semi": true,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "es5",
"useEditorConfig": true,
"useTabs": false,
"vueIndentScriptAndStyle": false,
"withNodeModules": false
}
...@@ -23,12 +23,21 @@ ...@@ -23,12 +23,21 @@
"author": "Daeki Yoon", "author": "Daeki Yoon",
"license": "ISC", "license": "ISC",
"devDependencies": { "devDependencies": {
"@types/bcryptjs": "^2.4.2",
"@types/cookie-parser": "^1.4.3",
"@types/express": "^4.17.13", "@types/express": "^4.17.13",
"@types/jsonwebtoken": "^8.5.8",
"@types/validator": "^13.7.4",
"nodemon": "^2.0.16", "nodemon": "^2.0.16",
"ts-node": "^10.8.1", "ts-node": "^10.8.1",
"typescript": "^4.7.4" "typescript": "^4.7.4"
}, },
"dependencies": { "dependencies": {
"express": "^4.18.1" "bcryptjs": "^2.4.3",
"cookie-parser": "^1.4.6",
"express": "^4.18.1",
"jsonwebtoken": "^8.5.1",
"mongoose": "^6.4.2",
"validator": "^13.7.0"
} }
} }
import express from "express"; import cookieParser from "cookie-parser";
import express, { Request, Response, NextFunction } from "express";
import router from "./routes";
const app = express(); const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
app.use("/api", router);
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
console.log("익스프레스 에러: ", err);
res.status(err.statusCode || 500).send(err.message || "서버 에러");
});
app.use(function (req, res, next) {
res.status(404).send("잘못된 경로를 요청했습니다");
});
export default app; export default app;
export const mongoUri = "mongodb://localhost/travelreport";
export const jwtCofig = {
secret: "HelloSecretString",
expires: "7d",
};
export const cookieConfig = {
name: "travel-report",
maxAge: 60 * 60 * 24 * 7 * 1000,
};
export const envConfig = {
mode: "development",
};
import bcrypt from "bcryptjs";
import { NextFunction, Request, Response } from "express";
import jwt, { JwtPayload } from "jsonwebtoken";
import isLength from "validator/lib/isLength";
import isEmail from "validator/lib/isEmail";
import { asyncWrap } from "../helpers";
import { userDb } from "../db";
import { jwtCofig, envConfig, cookieConfig } from "../config";
export interface TypedRequestAuth<T> extends Request {
auth: T;
}
export const login = asyncWrap(async (req, res) => {
const { email, password } = req.body;
console.log(`email: ${email}, password: ${password}`);
// 1) 사용자 존재 확인
const user = await userDb.findUserByEmail(email, true);
console.log("user =", user);
if (!user) {
return res.status(422).send(`${email} 사용자가 존재하지 않습니다`);
}
// 2) 비밀번호 확인
const passwordMatch = await bcrypt.compare(password, user.password);
if (!passwordMatch) {
return res.status(401).send("잘못된 비밀번호를 입력하셨습니다");
}
// 3) 비밀번호가 맞으면 토큰 생성
const token = jwt.sign({ userId: user.id }, jwtCofig.secret, {
expiresIn: jwtCofig.expires,
});
// 4) 토큰을 쿠키에 저장
res.cookie(cookieConfig.name, token, {
maxAge: cookieConfig.maxAge,
path: "/",
httpOnly: envConfig.mode === "production",
secure: envConfig.mode === "production",
});
// 5) 사용자 반환
res.json({
isLoggedIn: true,
email: user.email,
});
});
export const logout = (req: Request, res: Response) => {
res.clearCookie(cookieConfig.name);
res.send("Logout Successful");
};
export const requireLogin = asyncWrap(async (reqExp, res, next) => {
const req = reqExp as TypedRequestAuth<string | JwtPayload>;
try {
// 1) 쿠키 토큰 존재 여부 확인
const token = req.cookies[cookieConfig.name];
if (!token) {
throw new Error("토큰이 존재하지 않습니다");
}
// 2) 쿠키 유효성 검사
const decodedUser = jwt.verify(token, jwtCofig.secret);
// 3) 요청 객체에 토큰 사용자 객체 추가
req.auth = decodedUser;
next();
} catch (error) {
res.clearCookie(cookieConfig.name);
console.log("error in requreLogin===\n", error);
return res
.status(401)
.json({ message: "로그인이 필요합니다", redirectUrl: "/login" });
}
});
export const signup = asyncWrap(async (req, res) => {
const { name, email, password } = req.body;
// 1) name, email, password 유효성 검사
if (!isLength(name ?? "", { min: 2, max: 10 })) {
return res.status(422).send("이름은 2-10자로 입력해주세요");
} else if (!isLength(password ?? "", { min: 6 })) {
return res.status(422).send("비밀번호는 6자 이상으로 입력해주세요");
} else if (!isEmail(email ?? "")) {
return res.status(422).send("유효하지 않은 이메일입니다");
}
// 2) 사용자 중복 확인
const userExist = await userDb.isUser(email);
if (userExist) {
return res.status(422).send(`${email} 사용자가 이미 존재합니다`);
}
// 3) 비밀번호 암호화
const hash = await bcrypt.hash(password, 10);
// 4) 새로운 사용자 만들기
const newUser = await userDb.createUser({
email,
password: hash,
});
// 5) 사용자 반환
res.json(newUser);
});
export * as userCtrl from "./user.controller";
export * as authCtrl from "./auth.controller";
import { userDb } from "../db";
import { asyncWrap } from "../helpers/asyncWrap";
export const getUsers = asyncWrap(async (req, res) => {
const users = await userDb.getUsers();
return res.json(users);
});
export const createUser = asyncWrap(async (req, res) => {
const user = req.body;
console.log("user body", user);
const newUser = await userDb.createUser(user);
return res.json(user);
});
export * as userDb from "./user.db";
import { IUser, User } from "../models";
export const createUser = async (user: IUser) => {
const newUser = await User.create(user);
return newUser;
};
export const findUserByEmail = async (
email: string,
includePassword: boolean = false
) => {
let user;
if (includePassword) {
user = await User.findOne({ email }).select("+password");
} else {
user = await User.findOne({ email });
}
return user;
};
export const getUsers = async () => {
const users = await User.find({});
return users;
};
export const isUser = async (email: string) => {
const user = await User.findOne({ email });
if (user) {
return true;
} else {
return false;
}
};
import { Request, Response, NextFunction } from "express";
type AsyncRequestHandler = (
req: Request,
res: Response,
next: NextFunction
) => Promise<any>;
export const asyncWrap = (asyncFn: AsyncRequestHandler) => {
return async (req: Request, res: Response, next: NextFunction) => {
try {
return await asyncFn(req, res, next);
} catch (error) {
return next(error);
}
};
};
export { asyncWrap } from "./asyncWrap";
import { connect } from "mongoose";
import app from "./app"; import app from "./app";
import { mongoUri } from "./config";
app.listen(3000, () => { connect(mongoUri)
console.log(`server is running on port ${3000}`); .then((mgs) => {
}); console.log(`Mongoose is connected with version: ${mgs.version}`);
app.listen(3000, () => {
console.log(`server is running on port ${3000}`);
});
})
.catch((error) => {
console.log(`에러: ${error}`);
});
export { default as User, IUser } from "./user.model";
import { model, Schema } from "mongoose";
interface IRole {
name: string;
priority: number;
}
const schema = new Schema<IRole>({
name: { type: String },
priority: { type: Number },
});
export default model<IRole>("Role", schema);
import { model, Schema, Types } from "mongoose";
export interface IUser {
email: string;
name?: string;
password: string;
role?: Types.ObjectId;
}
const validateEmail = (email: string) => {
const re = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
return re.test(email);
};
const schema = new Schema<IUser>({
email: {
type: String,
rquired: true,
unique: true,
validate: [validateEmail, "이메일을 입력해주세요"],
},
name: { type: String },
password: { type: String, required: true, select: false },
role: { type: Schema.Types.ObjectId, ref: "Role" },
});
export default model<IUser>("User", schema);
import express from "express";
import { authCtrl } from "../controllers";
const router = express.Router();
router.route("/signup").post(authCtrl.signup);
router.route("/login").post(authCtrl.login);
router.route("/logout").get(authCtrl.logout);
export default router;
import express from "express";
import userRouter from "./user.route";
import authRouter from "./auth.route";
const router = express.Router();
router.use("/users", userRouter);
router.use("/auth", authRouter);
export default router;
import express from "express";
import { userCtrl, authCtrl } from "../controllers";
const router = express.Router();
router
.route("/")
.get(authCtrl.requireLogin, userCtrl.getUsers)
.post(authCtrl.requireLogin, userCtrl.createUser);
export default router;
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment