2022.09.28

Jimin·2022년 9월 28일
0

비트캠프

목록 보기
46/60
post-thumbnail
  • 서블릿 프로그래밍
    • 쿠키의 구동 원리와 다루는 방법
  • board-app 프로젝트 수행
      1. 로그인, 로그아웃 처리하기: HttpSession, Cookie 사용(계속)
      1. 로그인 기능 활용하기: HttpSession 활용
      1. 로그인이 필요한 요청인 경우 로그인으로 유도하기: Filter 활용

065. 로그인, 로그아웃 처리하기: HttpSession, Cookie 사용

  • HttpSession을 사용하여 로그인, 로그아웃을 처리하는 방법
  • 클라이언트 별로 세션을 구분하는 원리

1단계 - 로그인 폼을 만든다.

  • /webapp/auth/form.jsp 생성

2단계 - 로그인을 처리한다.

  • com.bitcamp.board.controller.LoginController 클래스 생성
    • 로그인 사용자 정보를 HttpSession 보관소에 저장한다.
  • /webapp/auth/loginResult.jsp 파일 생성
    • 로그인 결과에 따라 리프래시 경로를 지정한다.

3단계 - 로그아웃을 처리한다.

  • com.bitcamp.board.controller.LogoutController 클래스 생성

4단계 - 로그인할 때 입력한 사용자 이메일을 기억한다.

  • com.bitcamp.board.controller.LoginController 클래스 변경
    • 로그인 이메일을 쿠키로 웹브라우저에게 보낸다.
  • /webapp/auth/form.jsp 변경
    • 쿠키 테이블에 보관된 이메일 정보를 꺼내 이메일 입력란에 출력한다.
  • LoginController 클래스
@WebServlet("/auth/login")
public class LoginController extends HttpServlet{

  private static final long serialVersionUID = 1L;

  MemberDao memberDao;

  @Override
  public void init() {
    memberDao = (MemberDao) this.getServletContext().getAttribute("memberDao");
  }

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    try {
      String email = request.getParameter("email");
      String password = request.getParameter("password");
      Member member = memberDao.findByEmailPassword(email, password);

      if(member != null) {
        HttpSession session = request.getSession(); // 요청한 클라이언트의 전용 HttpSession 보관소를 얻는다.
        session.setAttribute("loginMember", member); // 로그인한 멤버 정보를 세션 보관소에 저장한다.
      }

      // 클라이언트에게 쿠키 보내기
      // 쿠키 데이터는 문자열만 가능하다.
      Cookie cookie = new Cookie("email", email); // 클라이언트 쪽에 저장할 쿠키 생성 

      if(request.getParameter("saveEmail") == null) {
        //여기서 파라미터는 url 창을 말한다.
        cookie.setMaxAge(0); // 클라이언트에게 해당 이름의 쿠키를 지우라고 명령한다.
      } else {
        // 쿠키에 지속 시간을 설정하지 않으면 웹 브라우저가 실행되는 동안만 유효하다.
        // 만약 웹브라우저를 종료하더라도 쿠키를 유지하고 싶다면, 
        // 지속 시간을 설정해야한다.
        cookie.setMaxAge(60 * 60 * 24 * 7); // 7일
      }
      response.addCookie(cookie); // 응답헤더에 쿠키를 포함시킨다.

      request.setAttribute("member", member);

      // Refresh
      response.setContentType("text/html;charset=UTF-8"); 
      request.getRequestDispatcher("/auth/loginResult.jsp").include(request, response); 
    } catch(Exception e) {
      request.setAttribute("exception", e);
      request.getRequestDispatcher("/error.jsp").forward(request, response);
    }
  }
}
  • /webapp/auth/form.jsp
<%@ page language="java" 
contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>JWS</title>
    </head>
    <body>
    <h1>지민이의 로그인!</h1>

    <form action='login'>
    <h2>지민이 로그인하기>o!</h2>

    <table border = '1'>

       <tr>
           <th>이메일</th><td><input name='email' type='email'  size='30' value="${cookie.email.value}"></td>
       </tr>

       <tr>
           <th>암호</th><td><input name='password' type='password'  size='20'></td>
       </tr>

    </table>
  <input type="checkbox" name="saveEmail">이메일 저장<br>
    <p>
    <button type='submit'>로그인</button>
    <a href='../'>취소</a>
    </p>
    </form>

    </body>
    </html>

클라이언트 저장소와 서버 저장소

쿠키(Cookie)

클라이언트 저장소에 보관할 데이터

서버에서 쿠키를 다룰 때는 개발자의 코딩이 필요하다.
클라이언트에서 서버로부터 받은 쿠키는 웹 브라우저가 자동으로 처리한다.

클라이언트는 같은 폴더에 소속된 서블릿으로부터 받은 쿠키를, 같은 폴더에 소속된 서블릿을 요청할 때만 쿠키를 서버로 보낸다.

쿠키는 클라이언트 쪽에 저장되기 때문에 쉽게 노출된다. ⇒ 보안 데이터는 쿠키로 저장하면 안된다.


066. 로그인 기능 활용하기: HttpSession 활용

  • 로그인 사용자 정보를 활용하는 방법
  • 도메인 객체 간의 포함관계를 구현하는 방법
  • 여러 개의 테이블을 조인하여 그 결과를 자바 객체에 저장하는 방법

0단계 - 게시글을 출력할 때 작성자 이름을 출력한다.

  • com.bicamp.board.domain.Board 클래스 변경
    • 필드에 직접 접근하지 못하게 private으로 접근 범위를 변경한다.
  • com.bitcamp.board.dao.MariaDBBoardDao 클래스 변경
  • /webapp/board/list.jsp 변경
  • /webapp/board/detail.jsp 변경
  • 도메인 객체의 필드를 사용하는 대신에 프로퍼티를 사용한다.
  • com.bitcamp.board.controller.BoardAddController 클래스 변경
  • com.bitcamp.board.controller.BoardUpdateController 클래스 변경

1단계 - 게시글을 등록할 때 로그인 정보를 활용한다.

  • webapp/board/form.jsp 변경
    • 작성자 입력상자에 로그인 사용자의 번호를 자동으로 입력한다.
    • 로그인 사용자 정보를 요청 파라미터로 보낼 때의 문제점을 이해한다.
  • com.bitcamp.board.controller.BoardAddController 클래스 변경
    • 게시글 등록할 때 로그인 사용자의 번호를 작성자 번호로 설정한다.
 <table border = '1'>

       <tr>
           <th>이메일</th><td><input name='email' type='email'  size='30' value="${cookie.email.value}"></td>
       </tr>

       <tr>
           <th>암호</th><td><input name='password' type='password'  size='20'></td>
       </tr>

    </table>
  <input type="checkbox" name="saveEmail">이메일 저장<br>
    <p>
    <button type='submit'>로그인</button>
    <a href='../'>취소</a>
	
  • BoardAddController 클래스
@WebServlet("/board/add")
public class BoardAddController extends HttpServlet{

  private static final long serialVersionUID = 1L;

  BoardDao boardDao;

  @Override
  public void init() {
    boardDao = (BoardDao) this.getServletContext().getAttribute("boardDao");
  }

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    try {
      Board board = new Board();
      board.setTitle(request.getParameter("title"));
      board.setContent(request.getParameter("content"));

      // 로그인 사용자 정보는 파라미터로 받아서는 안된다.
      // 반드시 세션에서 꺼내 써야 한다.
      // 왜? 그래야만 클라이언트가 다른 사용자 정보를 보낼 수 있기 때문이다.
      Member loginMember = (Member)request.getSession().getAttribute("loginMember");
      board.setWriter(loginMember);

      if(boardDao.insert(board)==0) {
        throw new Exception("게시글 등록 실패"); 
      }

      // Refresh
      response.setContentType("text/html;charset=UTF-8"); 
      request.getRequestDispatcher("/board/add.jsp").include(request, response); 

      // Redirect
      // - client에게 콘텐트를 보내지 않는다.
      // - 응답 상태 프로토콜

      //      response.sendRedirect("list");

    } catch(Exception e) {
      request.setAttribute("exception", e);
      request.getRequestDispatcher("/error.jsp").forward(request, response);
    }
  }
}

2단계 - 게시글을 변경, 삭제할 때 로그인 정보를 활용한다.

  • com.bitcamp.board.controller.BoardUpdateController 클래스 변경

  • com.bitcamp.board.controller.BoardDeleteController 클래스 변경

  • BoardUpdateController 클래스

@WebServlet("/board/update")
public class BoardUpdateController extends HttpServlet {
  private static final long serialVersionUID = 1L;

  BoardDao boardDao;

  @Override
  public void init() {
    boardDao = (BoardDao) this.getServletContext().getAttribute("boardDao");
  }

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    try {
      Board board = new Board();
      board.setNo(Integer.parseInt(request.getParameter("no")));
      board.setTitle( request.getParameter("title"));
      board.setContent( request.getParameter("content"));

      Member loginMember = (Member) request.getSession().getAttribute("loginMember");
      if (boardDao.findByNo(board.getNo()).getWriter().getNo() != loginMember.getNo()) {
        throw new Exception("게시글 작성자가 아닙니다.");
      }

      if (boardDao.update(board) == 0) {
        throw new Exception("게시글 변경 실패!");
      }

      response.setHeader("Refresh", "1;url=list"); // 응답 헤더에 refresh를 삽입한다.
      response.setContentType("text/html;charset=UTF-8");
      request.getRequestDispatcher("/board/update.jsp").include(request, response); 

    } catch (Exception e) {
      request.setAttribute("exception", e);
      request.getRequestDispatcher("/error.jsp").forward(request, response);
    }
  }
}

3단계 - 도메인 객체의 필드 대신 프로퍼티를 사용한다.

  • com.bitcamp.board.domain.Member 클래스 변경
    • 필드에 직접 접근하지 못하게 private으로 접근 범위를 변경한다.
  • com.bitcamp.board.dao.MariaDBMemberDao 클래스 변경

067. 로그인이 필요한 요청인 경우 로그인으로 유도하기: Filter 활용

  • Filter를 활용하여 로그인을 검사하는 방법
  • 로그인이 필요한 요청에 대해 로그인 화면으로 유도하는 방법

1단계 - 로그인 검사 및 유도를 수행하는 필터를 만든다.

  • com.bitcamp.board.filter.LoginCheckFilter 클래스 생성

  • LoginCheckFilter class

@WebFilter("*")
public class LoginCheckFilter implements Filter {

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
    // 요청 URL을 통해 로그인 여부를 검사할 지 결정한다.
    // 요청 URL은 HTTP 프로토콜과 관련된 값이다.
    // ServletRequest 타입은 HTTP 프로토콜과 관련된 기능을 다룰 수 있는 메서드가 없다.
    // ServletRequest 타입의 객체를 HttpServletRequest객체로 형변환 해야한다.
    // 필터의 파라미터로 넘어오는 객체는 원래 HttpServletRequest 객체이기 때문에 형변환 할 수 있다.
    // 즉,  HTTP 프로토콜과 관련된 기능을 쓰고 싶다면,
    // 원래 타입으로 형변환 한 다음에 사용하라!
    HttpServletRequest httpRequest = (HttpServletRequest) request;

    // 응답 기능에 대해서도 HTTP 관련 메서드를 사용하고 싶다면, 형변환 하라!
    HttpServletResponse httpResponse = (HttpServletResponse) response;

    // 요청 URL에서 서블릿 경로만 추출한다.
    // 예) 요청 URL: http://localhost:8000/app/board/add?title=aaa&content=bbb
    //      서블릿 경로: /board/add  <== 웹 애플리케이션 경로는 뺀다.
    String servletPath = httpRequest.getServletPath();
    //System.out.println(servletPath);

    // 콘텐트를 등록, 변경, 삭제하는 경우 로그인 여부를 확인한다.
    if(servletPath.endsWith("add") || servletPath.endsWith("update") ||  servletPath.endsWith("delete") ) {
      Member loginMember =(Member)httpRequest.getSession().getAttribute("loginMember");
      if(loginMember == null) { // 로그인 하지 않았다면
        httpResponse.sendRedirect(httpRequest.getContextPath() + "/auth/form.jsp");
        return;
      }
    }


    // 다음 필터를 실행한다.
    // 다음으로 실행할 필터가 없다면, 원래 목적지인 서블릿이 실행될 것이다.
    chain.doFilter(request, response);
  }

}
profile
https://github.com/Dingadung

0개의 댓글