블로그 이전

이전까지 세션과 인터셉터를 모두 개발 완료했다.
이제 매 요청 전 세션만 체크해주면 된다.

일단 해당 기능 적용 전 이전 글에서 Request 객체에 세션과 관련된 함수를 추가해주는 기능을 인터셉터로 변경해줬다.

SessionManagerInterceptor


class SessionManagerInterceptor extends HandlerInterceptor {

    /**
     * @type {SessionFactory}
     */
    #sessionFactory;

    /**
     * @param {SessionStore} store
     */
    constructor(store) {
        super();
        this.#sessionFactory = new SessionFactory(store);
    }

    /**
     * @param {Request} req
     * @param {Response} res
     * @return {boolean}
     */
    preHandle = (req, res) => {
        this.#addSessionGetterMethod(req, res);
        this.#addSessionRemoveMethod(req, res);
        return true;
    }

    /**
     * @param {Request} req
     * @param {Response} res
     * @return {boolean}
     */
    postHandle = (req, res) => {
    }


    /**
     * @param {Request} req
     * @param {Response} res
     */
    #addSessionGetterMethod = (req, res) => {
        /**
         * @param { boolean } [status=true]
         * @return {HttpSession}
         */
        req.getSession = async (status = true) => {

            let cookieSessionKey;
            if (req.cookies) cookieSessionKey = req.cookies[SessionStore.sessionKey];

            return await this.#sessionFactory.getSession(cookieSessionKey, res, status);
        }
    }

    /**
     * @param {Request} req
     * @param {Response} res
     */
    #addSessionRemoveMethod = (req, res) => {

        req.removeSession = async () => {

            let cookieSessionKey;
            if (req.cookies) cookieSessionKey = req.cookies[SessionStore.sessionKey];

            if (cookieSessionKey) await this.#sessionFactory.removeSession(cookieSessionKey);
            res.clearCookie(SessionStore.sessionKey);
        }
    }

}

이렇게 매 요청마다 getSession(), removeSession()함수를 Request 객체에 정의해주는 함수를 호출하도록 인터셉터를 개발했다.

ConfigLoader에 추가

#loadSessionStore = () => {
    let storeRegister = new SessionStoreRegister();
    if (config.setSessionStore) storeRegister = config.setSessionStore(storeRegister);

    this.#interceptorRegister.addInterceptor(new SessionManagerInterceptor(storeRegister))
        .setOrder(0)
        .addPaths("/*");
}

ConfigLoader객체에서 loadInterceptorConfig()를 통해 이용자가 개발한 인터셉터를 등록하기 전 loadSessionStore() 함수에서 해당 인터셉터를 먼저 등록하도록 해줬다.

SessionInterceptor

class HandlerInterceptor {

    /**
     * @param {Request} req
     * @param {Response} res
     * @return {boolean}
     */
    preHandle = (req, res) => {
        const session = req.getSession();
        const userId = req.params[userId];
        if (!session || session.getAttribute("userId") !== userId) {
            res.status(401).send('Unauthorized');
          	return false;
        }
        return true;
    }

    /**
     * @param {Request} req
     * @param {Response} res
     * @return {boolean}
     */
    postHandle = (req, res) => {
    }

}

이런식으로 세션을 체크하고, 요청한 유저와 세션 정보를 조회해서 일치하지 않으면 401, Unauthorized 응답을 보내도록 개발했다.

완성된 기능을 express.js에 적용한 코드는 깃헙 레포에 공유해뒀다.

이렇게 보안점검 지적사항 중 세션과 관련된 문제를 해결했다.

세션만 적용하던가 다른 방법으로 유효한 요청인지 체크하는 로직만 추가하면 됐지만 앞으로 다른 동료직원분들이 개발할 때 조금이라도 편하게 개발할 수 있게끔 세션과 인터셉터 기능을 개발했다.

이번 기능 개발을 통해 느낀점은 정말 많은것 같다.

스프링 공부를 하면서 당연히 사용하던 기술들을 직접 구현하려 하니 고려할 사항도 너무 많고 구조적으로 복잡해지니 어려운점도 많았다.

하지만 한번 이렇게 개발해놓고 나니 앞으로 세션, 인터셉터를 추가할땐 정말 간편히 객체 생성/설정에 추가 행위를 통해 기능 구현이 되는것을 보고 나름 뿌듯했다.

확실히 개발자가 불편하면 이용자는 편하게 된다.

profile
이용자가 아닌 개발자가 되자!

0개의 댓글