오늘은 로그인과 로그아웃 기능에 대해 배우고 코딩해보는 시간을 가졌다.
인증된 사용자 정보를 서버의 HttpSession 객체에 저장하는 것
HttpSession 객체는 고유한 아이디를 가지고 있고, 그 세션과 관련있는 클라이언트도 같은 아이디를 가지고 있기 때문에,
클라이언트마다 사용하는 세션객체가 고유하게 구별된다.
HttpSession 객체는 요청/응답이 완료되어도 사라지지 않고, 명시적으로 로그아웃하거나 일정시간 이상 해당 사이트를
이용하지 않을 때 사라진다.
따라서 HttpSession에 저장한 정보는 여러 JSP에서 이용할 수 있다.
로그인 로그아웃을 학습하기 전 테이블 생성 및 수정
CREATE TABLE SAMPLE_BOARD_USERS (
USER_ID VARCHAR2(20) PRIMARY KEY,
USER_PASSWORD VARCHAR2(20) NOT NULL,
USER_NAME VARCHAR2(100) NOT NULL,
USER_EMAIL VARCHAR2(255) UNIQUE,
USER_DELETED CHAR(1) DEFAULT 'N',
USER_CREATED_DATE DATE DEFAULT SYSDATE,
USER_UPDATED_DATE DATE DEFAULT SYSDATE
);
-- SAMPLE_BOARDS 테이블의 BOARD_WRITER 컬럼에 외래키 제약조건을 추가한다.
-- SAMPLE_BOARDS 테이블의 BOARD_WRITER 컬럼의 값은 SAMPLE_BOARD_USERS 테이블의 USER_ID를 참조한다.
ALTER TABLE SAMPLE_BOARDS ADD CONSTRAINT BOARD_WRITER_FK
FOREIGN KEY (BOARD_WRITER) REFERENCES SAMPLE_BOARD_USERS (USER_ID);
-- SAMPLE_BOARD_REVIEWS 테이블의 REVIEW_WRITER 컬럼에 외래키 제약조건을 추가한다.
-- SAMPLE_BOARD_REVIEWS 테이블의 REVIEW_WRITER 컬럼의 값은 SAMPLE_BOARD_USERS 테이블의 USER_ID를 참조한다.
ALTER TABLE SAMPLE_BOARD_REVIEWS ADD CONSTRAINT BOARD_REVIEW_WRITER_FK
FOREIGN KEY (REVIEW_WRITER) REFERENCES SAMPLE_BOARD_USERS (USER_ID);
public class User {
private String id;
private String password;
private String name;
private String email;
private String deleted;
private Date createdDate;
private Date updatedDate;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getDeleted() {
return deleted;
}
public void setDeleted(String deleted) {
this.deleted = deleted;
}
public Date getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
public Date getUpdatedDate() {
return updatedDate;
}
public void setUpdatedDate(Date updatedDate) {
this.updatedDate = updatedDate;
}
}
회원가입 기능 구현
(아이디, 이메일이 중복이 아닐 경우 유저 정보 등록하기)
users.xml
<!--
아이디를 전달받아서 sample_board_users 테이블에서 사용자정보를 조회해서 반환한다.
-->
<select id="getUserById" parameterClass="string" resultClass="com.sample.vo.User">
select
user_id as id,
user_password as password,
user_name as name,
user_email as email,
user_deleted as deleted,
user_created_date as createdDate,
user_updated_date as updatedDate
from
sample_board_users
where
user_id = #value#
</select>
<!--
이메일를 전달받아서 sample_board_users 테이블에서 사용자정보를 조회해서 반환한다.
-->
<select id="getUserByEmail" parameterClass="string" resultClass="com.sample.vo.User">
select
user_id as id,
user_password as password,
user_name as name,
user_email as email,
user_deleted as deleted,
user_created_date as createdDate,
user_updated_date as updatedDate
from
sample_board_users
where
user_email = #value#
</select>
<!--
사용자정보를 전달받아서 sample_board_users 테이블에서 저장한다.
-->
<insert id="insertUser" parameterClass="com.sample.vo.User">
insert into sample_board_users
(user_id, user_password, user_name, user_email)
values
(#id#, #password#, #name#, #email#)
</insert>
public class UserDao {
public User getUserById(String id) {
return (User) SqlMapper.selectOne("getUserById", id);
}
public User getUserByEmail(String email) {
return (User) SqlMapper.selectOne("getUserByEmail", email);
}
public void insertUser(User user) {
SqlMapper.insert("insertUser", user);
}
}
register.jsp
<%
// 회원가입 폼에서 register.jsp를 실행요청할 때 제출한 폼 입력값을 요청객체에서 조회한다.
String id = request.getParameter("id");
String password = request.getParameter("password");
String name = request.getParameter("name");
String email = request.getParameter("email");
// User 객체를 생성해서 조회된 값을 저장한다.
User user = new User();
user.setId(id);
user.setPassword(password);
user.setName(name);
user.setEmail(email);
// UserDao 객체를 생성한다.
UserDao userDao = new UserDao();
// 아이디로 사용자정보를 조회한다. 사용자정보가 null이 아니면 form.jsp를 재요청하는 URL을 응답으로 보낸다.
User savedUser = userDao.getUserById(id);
if (savedUser != null) {
response.sendRedirect("form.jsp?error=invalid");
return;
}
// 이메일로 사용자정보를 조회한다. 사용자정보가 null이 아니면 form.jsp를 재요청하는 URL을 응답으로 보낸다.
savedUser = userDao.getUserByEmail(email);
if (savedUser != null) {
response.sendRedirect("form.jsp?error=invalid");
return;
}
// insertUser(user)를 실행해서 사용자정보를 데이터베이스 저장시킨다.
userDao.insertUser(user);
// 재요청 URL을 응답으로 보낸다
response.sendRedirect("completed.jsp");
%>
form.jsp
<%
// 회원가입 실패로 form.jsp?error=invalid로 재요청했을 때 errorCode의 값은 invalid다.
String errorCode = request.getParameter("error");
%>
<p>아이디, 비밀번호, 이름, 이메일을 입력해서 회원가입을 신청하세요</p>
<%
if ("invalid".equals(errorCode)){
%>
<div class="alert alert-danger">
<strong>회원가입 오류</strong> 아이디 혹은 이메일이 이미 사용중입니다.
</div>
<%
}
%>
중복된 아이디 혹은 이메일로 회원가입을 하였을 경우
정상적으로 회원가입이 완료된 경우
로그인 기능 구현
(가입된 아이디가 있고 비밀번호와 일치할 때 로그인되는 기능)
login.jsp
<%
// 로그인폼에서 화면에서 login.jsp를 실행 요청할 때 제출한 폼 입력값을 요청객체에서 조회한다.
String id = request.getParameter("id");
String password = request.getParameter("password");
// 사용자 정보 조회를 위해서 UserDao 객체를 생성한다.
UserDao userDao = new UserDao();
// 아이디로 사용자 정보를 조회해서 사용자정보가 null이면 가입된 아이디가 아니므로 로그인화면을 재요청하는 URL을 응답으로 보낸다.
User savedUser = userDao.getUserById(id);
if (savedUser == null) {
response.sendRedirect("loginform.jsp?error=fail");
return;
}
// 조회된 사용자정보의 비밀번호와 입력한 비밀번호가 일치하지 않으면 로그인화면을 재요청하는 URL을 응답으로 보낸다.
if (!savedUser.getPassword().equals(password)){
response.sendRedirect("loginform.jsp?error=fail");
return;
}
// 사용자 인증이 완료된 경우, 세션객체에 사용자정보를 저장한다.
// HttpSession의 setAttribute(String name, Object value) 메소드를 사용해서 세션객체에 사용자 정보를 저장한다.
session.setAttribute("loginedUser", savedUser);
// 재요청 URL을 응답으로 보낸다
response.sendRedirect("../home.jsp");
%>
loginform.jsp
<%
// 회원가입 실패로 form.jsp?error=invalid로 재요청했을 때 errorCode의 값은 invalid다.
String errorCode = request.getParameter("error");
%>
<p>아이디, 비밀번호 입력해서 로그인하세요</p>
<%
if ("fail".equals(errorCode)){
%>
<div class="alert alert-danger">
<strong>로그인 오류</strong> 아이디 혹은 비밀번호를 확인하세요.
</div>
<%
}
%>
header.jsp
<%
User user = (User) session.getAttribute("loginedUser");
%>
<%
if (user == null){
%>
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link <%="login".equals(menu) ? "active bg-danger" : "" %>" href="/web-board/user/loginform.jsp">로그인</a></li>
<li class="nav-item"><a class="nav-link <%="register".equals(menu) ? "active bg-danger" : "" %>" href="/web-board/user/form.jsp">회원가입</a></li>
</ul>
<%
} else {
%>
<span class="navbar-text"><strong class="text-white"><%=user.getName() %></strong>님 환영합니다.</span>
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link" href="/web-board/user/logout.jsp">로그아웃</a></li>
</ul>
<%
}
%>
아이디 혹은 비밀번호가 일치하지 않는 경우
정상적으로 로그인 된 경우
로그아웃 기능 구현
(session.invalidate() 메소드로 세션객체를 무효화시키기)
logout.jsp
<%
// 로그아웃은 기존 세션객체를 무효화시킨다.
session.invalidate();
// 재요청 URL을 응답으로 보낸다.
response.sendRedirect("../home.jsp");
%>
익명 게시판을 게시판으로 변경
(로그인한 사용자만 게시글 등록하기)
- 로그인 한 상태에서만 새 글 등록이란 메뉴가 나타나도록 하기
list.jsp
<%
// HttpSession 객체에 저장된 인증된 사용자 정보를 조회한다.
User user = (User) session.getAttribute("loginedUser");
// 인증된 사용자 정보가 존재하면, 새글 등록 링크를 출력한다.
if (user != null) {
%>
<a href="form.jsp" class="btn btn-primary btn-sm float-end">새 글 등록</a>
<%
}
%>
로그인을 하지 않은 상태에서는 "새 글 등록" 버튼이 비활성화된다.
로그인 한 상태에서는 "새 글 등록" 버튼이 활성화된다.
만약, 로그인을 한 상태에서 새 글 등록을 들어갔을 때,
그 주소를 복사한 후 붙여놓으면 접속이 가능하기 때문에 form.jsp에서도 인증된 사용자 정보를 확인하고 null이면
로그인 폼으로 돌아가도록 해야한다.
form.jsp
<%
// HttpSession 객체에 저장된 인증된 사용자 정보를 조회한다.
User user = (User) session.getAttribute("loginedUser");
// 인증된 사용자 정보가 존재하지 않으면, 로그인 폼을 재요청하게 한다.
if (user == null) {
response.sendRedirect("../user/loginform.jsp?error=deny");
return;
}
%>
loginform.jsp
<%
// 회원가입 실패로 form.jsp?error=invalid로 재요청했을 때 errorCode의 값은 invalid다.
String errorCode = request.getParameter("error");
%>
<p>아이디, 비밀번호 입력해서 로그인하세요</p>
<%
if ("fail".equals(errorCode)){
%>
<div class="alert alert-danger">
<strong>로그인 실패</strong> 아이디 혹은 비밀번호가 일치하지 않습니다.
</div>
<%
} else if ("deny".equals(errorCode)) {
%>
<div class="alert alert-danger">
<strong>요청 거부</strong> 로그인한 사용자만 이용가능한 페이지입니다.
</div>
<%
}
%>
글 수정/삭제 기능 수정
(작성한 아이디로만 삭제 및 수정이 가능하도록 하기)
detail.jsp
<%
// 인증된 사용자 정보를 조회한다.
User user = (User) session.getAttribute("loginedUser");
%>
<%
// 인증된(로그인된) 사용자 정보가 존재하고,
// 로그인한 사용자의 아이디와 게시글 작성자의 아이디가 일치하는 경우 삭제/수정 링크 출력
if (user != null && user.getId().equals(board.getWriter())) {
%>
<a href="delete.jsp?no=<%=board.getNo() %>" class="btn btn-danger">삭제</a>
<a href="modifyform.jsp?no=<%=board.getNo() %>" class="btn btn-warning">수정</a>
<%
}
%>
작성자가 hong인 게시글을 다른 유저는 수정/삭제가 불가능하다.
글을 작성한 유저만 글을 삭제/수정이 가능하다.
링크만 숨기는 것이 아닌 로그인한 사용자의 아이디와 게시글 작성자의 아이디가 서로 다르면 게시글을 수정/삭제할 수 없도록 하기.
delete.jsp
<%
// 인증된 사용자 정보를 조회한다.
User user = (User) session.getAttribute("loginedUser");
// 로그인한 사용자의 아이디와 게시글 작성자의 아이디가 서로 다르면 게시글을 삭제할 수 없다.
// detail.jsp를 재요청하는 URL을 응답으로 보낸다.
if (!user.getId().equals(board.getWriter())) {
response.sendRedirect("detail.jsp?no=" + boardNo + "&error=deny");
return;
}
%>
detail.jsp / update.jsp
<%
String errorCode = request.getParameter("error");
%>
<%
if ("deny".equals(errorCode)) {
%>
<div class="alert alert-danger">
<strong>수정/삭제 거부</strong>다른 사용자가 작성한 게시글은 수정/삭제할 수 없습니다.
</div>
<%
}
%>
홍길동이 작성한 글은 김유신이 링크로 직접 들어간다하더라도 수정/삭제를 할 수 없다.