이전에 배운 기술들 중에서 미들웨어라는 개념을 배웠었고, 아주 요긴하게 잘 사용하고 있었다.
특히 그 중에서 인증 미들웨어
인데, 로그인을 한 상태로 api호출을 해야 할 경우 항상 인증 미들웨어를 거치고 감에 따라서 해당 사용자가 인가된 사용자인지 확인 후, 잘못된 방식으로 접근 한 사용자라면 차단시켜 준다.
이 미들웨어는 단순 인가를 걸러주는 것 이상으로 편리함을 제공해 주는데 한 번 만들어 놓고 나면 개발자가 해당 사용자임을 알아야하는 정보들을 끌어다 써야 할 상황에서 미들웨어만 통과시켜주면 자동으로 미들웨어에서 발급 해주는 해당 사용자의 정보들과 함께 제공된다. 그렇기 때문에 현재 로그인 한 사용자의 정보를 이용해 여러가지 api 처리를 할 수 있어서 편리하다.
현재 우리가 구현하고자 하는 프로젝트에는 User
의 종류가 2가지 이다. 하나는 일반 소비자 유저, 또 다른 하나는 서비스를 제공하는 유저이다. 따라서 단 하나의 인증 미들웨어만을 통과하면 현재 두 유형의 유저들을 구별해줄 수 있는 방법이 없어서 향후 서버 개발자가 두 유형의 사용자를 구별해줘야 할 때마다 매번 어떤 사용자인지 식별해야하는 상황이 생긴다.
이름하여 사장님 미들웨어
이다.
일반 auth 미들웨어는 accessToken
기반으로 사용자의 UserId
로 식별하지만 내가 만들고자 하는 사장님 미들웨어는 사업장의 Id (이하 본문에서는 RestaurantId
라고 한다.) ID를 사용하여 해당 사용자가 사업장 등록을 했는지에 따라서 db 비교 후 사업장 테이블에 해당 user의 id값이 없다면 사업자 등록이 되어 있지 않다는 로직을 가지고 있다.
먼저 미들웨어는 auth_middleware
---> auth_owner_middleware
---> 해당라우터
로 통과한다.
import { prisma } from "../utils/prisma/index.js";
// 소유자 권한 미들웨어
const auth_owner_middleware = async (req, res, next) => {
try {
console.log("----미들웨어 시작------");
// 로그인한 사용자의 ID를 가져옴
const { userId } = res.locals.user;
// URL 파라미터에서 레스토랑 ID를 추출
const { restaurantId } = req.params;
// 사용자의 ID로 레스토랑 소유 여부 확인
const owner = await prisma.restaurants.findUnique({
where: {
UserId: userId,
},
});
// 미들웨어 실행 시작 로그 출력
console.log("----미들웨어 실행 중------");
// 소유자가 아닌 경우 권한 없음 응답
if (!owner) {
return res.status(401).json({
success: false,
message: "권한이 없습니다 (사장 등록 X)",
});
}
// 다른 레스토랑의 소유자인 경우 권한 없음 응답
if (owner.restaurantId !== +restaurantId) {
return res.status(401).json({
success: false,
message: "권한이 없습니다(사장 등록 O, 다른 사장 페이지)",
});
}
// 레스토랑 소유자 정보를 로컬 변수에 저장
res.locals.owner = owner;
// 다음 미들웨어로 이동
next();
} catch (error) {
// 에러가 발생한 경우 세션 쿠키를 클리어하고 권한 없음 응답
res.clearCookie("authorization");
return res.status(401).json({
message: "비정상적인 요청입니다.",
});
}
};
// 다른 파일에서 사용할 수 있도록 미들웨어 내보내기
export { auth_owner_middleware };
auth_middleware
를 통과하고 왔기 때문에 로컬 변수에 저장된 userId
와 url 파라미터의 restaurantId
를 가져와서 사업장 db에서 소유 여부를 확인하고 if문으로 원하는 조건을 달아주게 된다.
레스토랑의 소유자 정보를 res.locals.owner
에 저장하여 다른 미들웨어나 라우터에서 사용할 수 있게 한다.
그런 다음 모든 조건문을 통과하고 나면 다음 미들웨어로 이동한다.
하지만 에러가 발생한 경우 세션 쿠키를 클리어하고 (accessToken 초기화
) 권한 없음 응답을 반환한다.
보완점
1. 권한 부여 및 로직 분리
- 현재 미들웨어에서는 권한 확인 로직과 함께 다른 역할도 수행하고 있는데, 이로 인해 미들웨어가 너무 많은 책임을 지고 있을 수 있다.
- 권한 확인 로직을 별도의 함수로 분리하여, 미들웨어가 더 간결하고 읽기 쉽도록 만들 수 있다.
// 새로운 파일 (auth.js)에 권한 확인 로직 분리
import { prisma } from "../utils/prisma/index.js";
const checkOwnerPermission = async (userId, restaurantId) => {
const owner = await prisma.restaurants.findUnique({
where: {
UserId: userId,
},
});
if (!owner) {
return false; // 소유자가 아닌 경우
}
if (owner.restaurantId !== +restaurantId) {
return false; // 다른 레스토랑의 소유자인 경우
}
return true; // 권한이 있는 경우
};
export { checkOwnerPermission };
// 새로운 파일 (auth.js)에 권한 확인 로직 분리
import { prisma } from "../utils/prisma/index.js";
const checkOwnerPermission = async (userId, restaurantId) => {
const owner = await prisma.restaurants.findUnique({
where: {
UserId: userId,
},
});
if (!owner) {
return false; // 소유자가 아닌 경우
}
if (owner.restaurantId !== +restaurantId) {
return false; // 다른 레스토랑의 소유자인 경우
}
return true; // 권한이 있는 경우
};
export { checkOwnerPermission };
상수 이용
- 코드에서 사용하는 마법 숫자 및 문자열을 상수로 정의하여 코드를 읽기 쉽게 만들 수 있다.
// 상수 정의
const NO_OWNER_MESSAGE = "권한이 없습니다.";
const ABNORMAL_REQUEST_MESSAGE = "비정상적인 요청입니다.";
// 미들웨어에서 상수 사용
if (!hasPermission) {
return res.status(401).json({
success: false,
message: NO_OWNER_MESSAGE,
});
}
// ...
} catch (error) {
res.clearCookie("authorization");
return res.status(401).json({
message: ABNORMAL_REQUEST_MESSAGE,
});
세션 관리
- 보다 안전한 세션 관리를 위해
express-session
과 같은 세션 관리 미들웨어를 사용할 수 있다.- 세션 관리 미들웨어를 사용하면 세션의 유지 및 보안 관련 설정을 편리하게 구성할 수 있다.
- 필요한 경우 세션을 데이터베이스에 저장하여 보안을 강화할 수 도 있다.