들어가기 전에 검토할 내용이다.
.setAttribute(‘1’, 2);
위 메서드는 request 객체에 값을 넣어 JSP로 넘길 때도, request 내부의 Session에 값을 저장할 때도 생긴다.
이때, 1번은 key로 값에 대한 변수명이라고 이해하는게 편하다. 이보다 중요한 것은 2번 자리에 들어가는 인자로, 데이터 그 자체가 들어간다.
여기에는 어떤 데이터든 넣을 수 있는데, 이는 다형성을 응용하여 object 타입에 넣기 때문에 가능하다.
오늘은 기본적으로 로그인, 로그아웃, 회원탈퇴 기능을 구현하여 기본 Index에 대한 실습을 마무리할 것이다.
- 로그인 : Cookie, Session
- 로그인 기능 구현
- 로그아웃 기능 구현
- 회원 탈퇴 기능 구현
먼저 로그인이라는 개념이 뭘까?
음..쉽게 말하면 ‘본인 인증’ 정도일 것이다. 그리고 이는 하나의 객체라고 할 수 있다.
그 계정에 연결되어 실제 내가 쓴 글, 정보 등이 담기니까 말이다.
하지만 일반적인 로그인 과정은 다음과 같다.
Client의 데이터 전송 → 서버에서 받음 → 컨트롤러가 로그인 서블릿 전달 → 로그인 서블릿에서 모델에 접근해서 DB에 입력된 정보랑 비교 → 일치 로그인 상태로 변경
위 과정을 본다면 쉽지만, 제일 중요한 것은 마지막 ‘로그인 상태’가 어느 서블릿에서든지 적용되어야 한다는 것이다. 즉, DB를 통해 인증되는 순간 처리가 되어 서버 쪽 전체가 해당 인증을 알고 있어야 한다.
하지만 우리가 사용하는 HTTP는 stateless라는 특징이 있다. 이는 응답-요청이라는 한 패턴이 끝나면, 로그인 처리되어도 서버가 해당 내용을 기억하지 못한다는 것이다.
따라서 이를 극복한 해결책이 두 가지가 존재한다.
서버 쪽에서 클라이언트에게 전달하고 싶은 내용을 Response에 담아 보낸 것을 보관하는 파일
현재는 연계 광고, 장바구니 같은 것에만 쓰이지만, 예전에는 로그인 정보를 쿠키 파일로 저장하였다.
우리가 로그인 요청을 하게 되면 서버에서 클라이언트에 암호화된 인증 정보를 String으로 보내준다.
이 값을 브라우저가 가지고 있고, 요청을 보낼 때마다 자동으로 쿠키파일을 함께 담아서 보낸다. 그러면 컨트롤러에서 쿠키 안에서 필요한 정보를 꺼내서 처리한다.
하지만 그 자체가 파일이기 때문에 해당 파일만 뽑아낼 수 있다면 쉽게 해킹이 가능하다는 보안 이슈가 존재하였다.
쉽게 이해가 안간다면, 클럽 도장이나 놀이동산 티켓과 같은 원리라고 생각하면 된다.
일단 세션도 로그인 정보를 보관한다. 예를 들면 아이디, 비밀번호, 작성 글 번호 등등 사실 뭘 보관할 수 있는지는
개발자의 선택이다.
하지만 근본적으로 중요한 것은 그 정보가 클라이언트 쪽으로 넘어가지 않는다.
이런 세션은 다음과 같이 동작한다.
(1) 서버는 자신에게 접속하는 모든 클라이언트에게 ‘session 키’를 발급.
(2) 키 별로 서버 뒤편에 세션 보관함을 생성한다.
(3) 키를 갖고 요청할 때마다 서버에서 보관함을 열고 로그인 정보를 확인한다.
기본 구조는 ‘해쉬 테이블’을 사용한다. 따라서 클라이언트는 데이터에 대한 접근 권한인 ‘키’를 갖고, 서버의 보관함과 연계되어 동작한다. 그리고 보관함 내에서도 ‘키:값’을 통해서 정보를 넣고, 빼낼 수 있다.
결과적으로 세션은 하나의 객체인데, 해당 객체를 이용하기 위해선 ‘키’가 필요한 것이다. 그리고 각 서블릿에서 세션 키를 통해 로그인 정보를 끌어오는 것이 가능하다.
request
객체에서 꺼내온다. (request : 일시적 | session : 영구적)1) JSP에서 JSTL, EL을 이용해 로그인 상태에 따라 다른 화면을 구현한다.
2) 기본 보안을 위해 POST로 Controller로 데이터를 보낸다.
3) 입력받은 값을 불러온다. getParameter
4) DAO로 DB의 로그인 정보와 입력값을 대조한다.
5) 일치 시, 세션 객체에 저장한다.
6) 로그인 완료 후, View를 응답한다.
이때, 세션에 저장한 값은 EL로 입력시 설정한 이름을 통해 사용 가능하기 sendRedirect
를 사용할 수 있다.
<body>
<!-- 상황에 따라서 보여주는 화면이 구별된다 -->
<c:choose>
<!-- 로그인 시 -->
<c:when test="${loginID != null}">
<table border=1 align=center>
<tr>
<th colspan="4">${loginID} 님 안녕하세요.
</tr>
<tr>
<th>
<button id="toBoard">toBoard</button>
<th>
<button id="myPage">MyPage</button>
<th>
<button id="Logout">Logout</button>
<th>
<button id="memberout">MemberOut</button>
</tr>
</table>
<script>
$("#Logout").on("click", function(){
location.href="logout.member";
});
$("#memberout").on("click", function(){
window.open("/isdelete.member", "", "top=100,left=200,width=300, height=200");
});
$("#myPage").on("click", function(){
location.href="list.member";
});
$("#toBoard").on("click", function(){
location.href="list.board";
});
</script>
</c:when>
<!-- 비 로그인 시 -->
<c:otherwise>
<form action="/login.member" method="post">
<table border="1" align="center">
<tr>
<td align="center" colspan="2">Login Box</td>
</tr>
<tr>
<td>아이디:</td>
<td><input type="text" placeholder="input your id" name=id>
</td>
</tr>
<tr>
<td>패스워드:</td>
<td><input type="password" placeholder="input your pw"
name=pw></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit"
value="로그인"> <input type="button" value="회원가입" id=join><br>
<input type="checkbox" name="remember">ID기억하기</td>
</tr>
</form>
</table>
<script>
$("#join").on("click", function() {
location.href = "join.member";
});
</script>
</c:otherwise>
</c:choose>
</body>
// 로그인
else if (uri.equals("/login.member")) {
1. 값 끌어오기
String id = request.getParameter("id");
String pw = request.getParameter("pw");
System.out.println(id);
System.out.println(pw);
2. DAO 인증
boolean result = inDAO.login(id, pw);
if(result) {
3. 세션에 저장한다. session.getId는 세션 키 자체를 가져오는 메서드
HttpSession session = request.getSession(); // 세션을 반환
5. 보관함에 값을 저장한다.
session.setAttribute("loginID", id);
6. index 재요청을 응답
response.sendRedirect("/index.jsp");
} else {
response.sendRedirect("/index.jsp");
}
}
public boolean login(String id, String pw) throws Exception {
String sql = "select id, pw from member where id=? and pw=?";
pw = utils.EncryptUtils.SHA256(pw);
try(
Connection con = this.getConnection();
PreparedStatement pstat = con.prepareStatement(sql);
){
pstat.setString(1, id);
pstat.setString(2, pw);
try( ResultSet rs = pstat.executeQuery();){
return rs.next();
}
}
}
기본적으로 세션에 저장된 값은 remove(“키 이름”);
을 통해서 삭제한다. 하지만 그건 값을 하나씩 지우는 것이기 때문에 invaligate();
를 사용해서 세션 자체를 무효화시킨다.
// 로그아웃
else if (uri.equals("/logout.member")) {
1. 세션 안의 데이터를 밀어버린다.
request.getSession().invalidate();
2. 초기화면으로 재요청을 응답한다.
response.sendRedirect("/index.jsp");
}
기본 : memberOut 기능을 사용할 UI 생성.
1) memberOut 버튼에 onpenee 팝업창 이벤트 설정 window.open()
2) 팝업 오픈 시, 탈퇴 유무를 물어보는 JSP로 이동하는 컨트롤러를 URL로 지정
3) sendRedirect로 JSP 반환
4) 기본 UI : Yes / No 버튼 이벤트 처리
5) No 버튼 : 창 닫기 window.close();
6) Yes 버튼 : 삭제 컨트롤러(/memberout.member)로 요청
7) 세션에서 삭제 → DB 삭제
8) 초기화면을 로딩하는 jquery 이벤트 처리 opener.location.href="/index.jsp";
(reload)도 가능
9) 창 닫기 : widow.close();
이때, 제이쿼리 이벤트는 비동기로 일어나니 웬만하면 7)에서 세션 먼저 제거하는 것이 좋다.
1. 회원 탈퇴 유무 View로 이동
if (uri.equals("/isdelete.member")) {
response.sendRedirect("/isdelete.jsp");
2. 회원 탈퇴
} else if (uri.equals("/memberout.member")) {
// 값을 꺼내온다.
String delId = (String) (request.getSession().getAttribute("loginID"));
// 세션에서 지운다.
request.getSession().invalidate();
// DB에서 지운다.
int result = mDao.deleteMember(delId);
}
<table border=1 align=center>
<tr>
<th>
<button id="yes">Yes</button>
<th>
<button id="no">No</button>
</tr>
</table>
<script>
$("#yes").on("click", function(){
location.href="memberout.member";
opener.location.reload();
window.close();
});
$("#no").on("click", function(){
window.close();
});
</script>