Coding 공부/IntelliJ

[IntelliJ_Servlet_MariaDB] 웹 어플리케이션에서 데이터의 흐름, 보안 토큰과 프로그래밍 토큰, 웹 서비스, 필터(filter)

CBJH 2024. 4. 30. 16:22
728x90
반응형

1. 웹 어플리케이션에서 데이터의 흐름

  1. 사용자 인터페이스:
    • 사용자는 웹 브라우저를 통해 웹 사이트에 접속합니다. 브라우저는 사용자에게 HTML 페이지를 표시하며, 이 페이지에는 CSS로 스타일이 적용되고 JavaScript로 동적인 요소가 처리됩니다.
  2. 데이터 입력:
    • 사용자가 웹 페이지의 양식(form)에 데이터를 입력하고 제출(submit) 버튼을 클릭합니다. 예를 들어, 이름과 이메일을 입력하는 양식이 있을 수 있습니다.
  3. HTTP 요청 전송:
    • 사용자가 데이터를 입력하고 양식을 제출하면, 이 데이터는 HTTP 요청(주로 POST 또는 GET 메서드)으로 웹 서버에 전송됩니다. 이 요청은 웹 서버가 호스팅하는 웹 어플리케이션 서버(WAS)로 라우팅됩니다.
  4. 서버 처리:
    • 웹 어플리케이션 서버(WAS)는 HTTP 요청을 받아 데이터를 추출하고, 필요한 로직을 처리합니다. 이 과정에서 서버는 데이터를 검증하고, 로그인 정보를 확인하는 등의 작업을 수행할 수 있습니다.
  5. 데이터베이스 상호작용:
    • 처리 로직의 일부로, WAS는 데이터베이스(DB)에 접근하여 데이터를 조회, 삽입, 수정 또는 삭제할 수 있습니다. 예를 들어, 사용자 정보를 데이터베이스에 저장하거나, 기존 사용자 정보를 업데이트할 수 있습니다.
  6. 응답 생성:
    • 데이터베이스와의 상호작용 후, WAS는 결과 데이터를 가공하여 HTTP 응답을 생성합니다. 이 응답은 HTML, JSON, XML 등 다양한 형식으로 반환될 수 있습니다.
  7. 클라이언트에 응답 전송:
    • 생성된 응답은 웹 브라우저로 다시 전송됩니다. 브라우저는 응답 데이터를 사용자에게 적절한 형태로 표시합니다. 예를 들어, 작업의 성공 여부를 알리는 메시지를 표시하거나, 새로운 페이지를 로드할 수 있습니다.
  8. 화면 갱신:
    • 최종적으로, 브라우저는 받은 응답에 따라 화면을 갱신합니다. 이 과정에서 JavaScript가 다시 실행될 수 있어, 사용자 인터랙션을 동적으로 처리하거나 데이터를 다시 서버로 전송하는 등의 작업이 진행될 수 있습니다.

이렇게 웹 어플리케이션은 클라이언트(브라우저)와 서버(WAS), 그리고 데이터베이스 간의 상호작용을 통해 사용자의 요청을 처리하고 결과를 반환합니다. 이 모든 과정은 사용자의 경험을 원활하게 하고, 필요한 정보를 효과적으로 처리 및 저장하기 위해 중요합니다.

 

2. 보안 토큰과 프로그래밍 토큰

 

1. 보안 토큰 (Security Tokens)

보안 토큰은 인증 및 권한 부여 과정에서 사용되는 데이터 조각입니다. 사용자나 프로세스의 신원을 증명하거나, 시스템에 접근 권한을 부여하는데 사용됩니다. 보안 토큰의 주요 유형은 다음과 같습니다:

  • 액세스 토큰 (Access Tokens): 사용자가 로그인을 성공적으로 완료하면, 서버는 액세스 토큰을 발행하여 사용자가 이후의 요청에서 이를 제시함으로써 인증받을 수 있도록 합니다. 예를 들어, OAuth에서는 이 액세스 토큰을 사용하여 다른 서비스에 대한 접근을 제어합니다.
  • 리프레시 토큰 (Refresh Tokens): 액세스 토큰과 함께 사용되며, 액세스 토큰이 만료된 후 새로운 액세스 토큰을 발급받기 위해 사용됩니다. 이는 사용자가 자주 로그인을 다시 하지 않도록 해주는 보안 기능입니다.
  • JWT (JSON Web Tokens): 인증 및 정보 교환에 사용되는 자가 포함형(JSON) 토큰으로, 토큰 자체가 정보를 포함하며, 이 정보는 디지털 서명되어 변조될 수 없습니다.

2. 프로그래밍 토큰 (Programming Tokens)

프로그래밍에서 토큰은 소스 코드 내의 기본적인 구성 요소를 나타냅니다. 이는 보통 다음과 같은 요소로 구성됩니다:

  • 키워드 (Keywords): 예약된 단어들로, 프로그래밍 언어에서 특정한 의미를 가집니다 (예: if, else, function 등).
  • 식별자 (Identifiers): 변수, 함수, 클래스 등의 이름을 지정할 때 사용됩니다.
  • 리터럴 (Literals): 소스 코드에 직접 나타나는 값(예: 숫자 10, 문자열 "hello" 등).
  • 연산자 (Operators): 산술, 비교, 논리 연산 등을 수행하는 기호 (예: +, -, ==, && 등).
  • 구분자 (Delimiters): 프로그램의 구조를 돕는 기호 (예: {}, (), ; 등).

 

3. 웹 서비스

1. 사용자 인증 과정

  1. 로그인 요청: 사용자가 웹사이트에 로그인 요청을 합니다. 일반적으로 이메일과 비밀번호 같은 인증 정보를 입력하여 로그인 폼을 통해 서버로 전송합니다.
  2. 인증 처리: 서버는 받은 인증 정보를 검증합니다. 정보가 유효하면, 서버는 사용자의 세션을 생성하고, 해당 세션에 대한 식별자를 포함한 인증 토큰(예: JWT)을 발급합니다.
  3. 토큰 발급 및 저장: 발급된 토큰은 HTTP 응답을 통해 사용자의 브라우저로 전송되고, 보통 쿠키에 저장됩니다. 이 쿠키는 이후의 모든 요청에 자동으로 포함되어 서버로 전송됩니다.

2. 세션 및 쿠키 관리

  • 쿠키 (Cookies): 사용자의 브라우저에 저장되는 작은 데이터 조각으로, 사용자가 서버에 다시 연결할 때마다 자동으로 서버에 전송됩니다. 쿠키는 사용자의 로그인 상태를 유지하는 데 사용될 수 있습니다.
  • 세션 (Session): 서버 측에서 유지되는 사용자 상태 정보입니다. 사용자가 로그인하면, 서버는 세션 ID를 생성하고 이를 쿠키를 통해 사용자 브라우저에 전송합니다. 세션 ID는 사용자가 서버와의 상호 작용을 계속하는 동안 서버가 사용자를 식별하는 데 사용됩니다.

3. 인증 토큰의 역할

  • JWT (JSON Web Tokens): 이 토큰은 사용자의 인증 정보, 권한, 유효 기간 등을 포함할 수 있습니다. JWT는 자체적으로 정보를 포함하기 때문에, 서버는 데이터베이스를 조회하지 않고도 토큰을 검증할 수 있습니다. 이는 상태가 없는(stateless) 인증 방식을 가능하게 하여 서버의 부하를 줄일 수 있습니다.

4. 보안 고려사항

  • HTTPS: 모든 인증 요청과 데이터 전송은 HTTPS를 통해 암호화되어야 합니다. 이는 중간자 공격(man-in-the-middle attacks)으로부터 사용자 데이터를 보호합니다.
  • 쿠키 속성: 쿠키는 Secure 속성을 사용하여 HTTPS를 통해서만 전송되도록 설정되어야 합니다. 또한, HttpOnly 속성을 설정하여 JavaScript를 통한 접근으로부터 보호받을 수 있습니다.
  • 토큰 만료: 인증 토큰은 유효 기간이 짧게 설정되어야 하며, 필요한 경우 리프레시 토큰을 통해 갱신될 수 있어야 합니다.

 

4. 필터(filter)

 

  4.1 필터의 주요 기능

  • 요청 데이터 처리: 요청 데이터를 수정하거나 검사할 수 있습니다. 예를 들어, 입력값 검증, 요청 헤더의 수정 및 추가 등이 가능합니다.
  • 응답 데이터 처리: 응답 데이터를 수정하거나 추가할 수 있습니다. 예를 들어, 응답에 특정 헤더를 추가하거나, 콘텐츠를 압축하거나 변형시키는 작업을 수행할 수 있습니다.
  • 보안: 인증과 권한 부여를 필터에서 처리하여 접근 제어를 수행할 수 있습니다.
  • 로그와 감사: 요청과 응답에 대한 로깅을 통해 시스템의 사용 상황을 기록하고 감사할 수 있습니다.

  4.2 필터의 구성

서블릿 필터를 구현하려면, javax.servlet.Filter 인터페이스를 구현해야 합니다. 이 인터페이스는 다음 세 가지 메서드를 포함합니다:

  1. init(FilterConfig filterConfig): 필터 인스턴스가 생성될 때 호출되며, 필터 설정과 관련된 초기화 작업을 수행합니다.
  2. doFilter(ServletRequest request, ServletResponse response, FilterChain chain): 실제 필터 로직을 구현하는 부분입니다. 이 메서드 내에서 요청 객체와 응답 객체를 조작할 수 있으며, 필터 체인 내의 다음 필터 또는 최종 목적지인 서블릿/JSP로 요청과 응답을 전달하도록 chain.doFilter() 메서드를 호출해야 합니다.
  3. destroy(): 필터가 서비스에서 제거될 때 호출되며, 필터에 할당된 자원을 정리하는 데 사용됩니다.

  4.3 필터 체인

여러 필터가 설정된 경우, 필터는 설정된 순서대로 실행됩니다. 각 필터는 필터 체인을 통해 다음 필터 또는 최종 자원(서블릿, JSP)에 요청과 응답을 전달할 수 있습니다. 필터 체인의 마지막 필터가 요청 처리를 완료하면, 응답은 체인을 역순으로 거슬러 오면서 최종적으로 클라이언트에게 반환됩니다.

 

  4.4 로그인 세션 체크(필터 사용 전 예제)

@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
        HttpSession session = req.getSession();
        if(session.isNew()){
            //기존에 JsessionID가 없는 사용자. 쿠키를 새로 만들도록 해야함.
            resp.sendRedirect("/login");
            return;
        }
        if(session.getAttribute("loginInfo") == null){
            // 다른 데이터는 있는데, 로그인 한 정보가 없는 사용자
            resp.sendRedirect("/login");
            return;
        }

        try{
            List<TodoDTO> dtoList = todoService.listAll();
            req.setAttribute("dtoList", dtoList);
            req.getRequestDispatcher("/WEB-INF/todo/list.jsp").forward(req, resp);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
  • 로그인이 되어있는지 세션값을 받아 확인하는 작업. 모든 페이지에 필요하므로 필터를 사용할 수 있다.

 

  4.5 로그인 세션확인 필터 예제

@Log4j2
@WebFilter(urlPatterns = {"/todo/*"})
public class LoginCheckFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        log.info("login check filter..");
//        filterChain.doFilter(request, response);
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        HttpSession session = req.getSession();

        if(session.getAttribute("loginInfo")==null){
            resp.sendRedirect("/login");
            return;
        }

        filterChain.doFilter(request, response);
    }
}
  • urlPatterns에 "/todo/*"으로 "/todo/"로 시작하는 URL에 접근할 경우 필터를 적용한다.
  • 로그인 세션이 없을 경우 로그인 페이지로 이동한다.

 

  4.6 로그인 컨트롤러 예제

@WebServlet(name="LoginController", urlPatterns = "/login")
public class LoginController extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getRequestDispatcher("/WEB-INF/login.jsp").forward(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String mid = req.getParameter("mid");
        String mpw = req.getParameter("mpw");

        String str = mid+mpw;

        HttpSession session = req.getSession();
        session.setAttribute("loginInfo", str);
        resp.sendRedirect("/todo/list");
    }
}
  • 로그인 페이지에서 얻은 정보를 세션에 SetAttribute한다. 
  • setAttribute메서드의 첫번째 매개변수에 세션 이름을 설정하고, 두번째 매개변수에 객체 정보를 저장한다.