이번 글은 로그인/로그아웃 과정이 담겨있다.
우선 로그인을 했다면 웹페이지에서는 로그인 한것을 계속해서 기억을 하고 어딘가 담겨있어야 되지 않나?라는 고민을 하며 찾아봤는데, HttpSession을 이용한 과정이 있어 적용시켰다.
HttpSession이란?
HttpSession은 자바에서 클라이언트의 세션 데이터를 관리하는데 사용되는 인터페이스다.
세션은 클라이언트와 서버간의 지속적인 상태를 유지하기 위해 만들어진다.
Http는 기본적으로 상태를 저장하지 않는 프로토콜이기 때문에, 세션을 통해 로그인상태나 사용자별 데이터 같은 정보를 유지하기 위해 session을 사용해야한다. 쉽게 이야기하자면 보통 서버로 요청을 했을 때, "이 페이지를 보여줘"하면 서버에서는 그 페이지를 보여줌으로 응답을 한다. 근데 서버는 이전 요청에대해서 기억을 하지않는다. 왜그러냐면 HTTP가 설계될 당시, 간단하고 빠른 데이터 전송을 목표로 했다고한다. 상태를 저장하면 서버의 메모리 사용량과 복잡도가 증가하므로, 초기 HTTP설계에서는 상태를 유지하지 않게되었다고 한다.
그래서 이 문제를 해결하기 위해 HttpSession을 이용하여 클라이언트의 상태를 저장한다(로그인한 유저의 정보 등 계속해서 기억해야 할 데이터). 저장데이터는 서버 내에 저장이되고 그 저장된 세션을 다루기 위한 세션ID(세션을 읽을 수 있는 열쇠?)는 쿠키에 저장된다.
세션에 대해서 더 알아보니 세션은 그냥 사용자가 해당 웹페이지를 방문하면 그 방문자에 한하여 고유한 세션ID를 부여한다고 한다( 세션에 저장된 데이터가 없더라도 부여해준다고 함). 그 세션ID는 클라이언트 즉, 사용자가 서버에 요청을 할 때 저는 어떤 세션을 쓰고있어요~라는 열쇠?라고 생각하면 될 것 같다(세션ID는 쿠키에 자동저장). 서버 세션에 아무데이터도 저장하지 않은 상태로 클라이언트에서 세션정보를 요청하면 null값이 나오는거고, 세션에 저장되어있고 해당 키 값을 알고 있다면 데이터가 읽히는거다.
즉, 쉽게 비유해보자면 서버에서는 금고를 하나 생성해주는데, 이 때 사용자에게 금고 열쇠(열쇠는 쿠키에 저장됨)를 준다. 그럼 금고에 저장된 무엇인가를 필요할때마다 꺼내서 쓸 수 있는 과정이라고 보면된다
로그인 과정(session에 저장)
Controller Class
//로그인하기 _ 아이디 및 비밀번호 조회
@PostMapping("/login")
public String login(HttpSession session,@RequestParam String username, @RequestParam String password) {
return imLoginService.login(username, password, session);
}
아이디, 비밀번호를 입력한 데이터와 현재 사용자에게 부여된 session을 가져와
로그인을 담당하는 imLoginService.login 메소드로 데이터를 보내준다
Service Class
@Service
@RequiredArgsConstructor
public class ImLoginService {
private final ImUserRepository imUserRepository;
//로그인과정
public String login(String username, String password,HttpSession session) {
/* 접속하려는 아이디로 db조회 */
ImUser user = imUserRepository.findByUserId(username);
/* db에 없으면 error=id 전달 */
if (user == null) {
return "redirect:/?error=id";
}
// 아이디가 있다면 비밀번호 조회 후 불일치일 경우 error=pw 전달
if (!Encoder.matchPassword(password, user.getUserPassword())) {
return "redirect:/?error=pw";
}
// 다 맞으면 세션에 유저코드, 유저닉네임 담기 (+http 30분간 아무 요청없을시 세션자동삭제)
session.setAttribute("userCode", user.getUserCode());
session.setAttribute("userNickname", user.getUserNickname());
session.setMaxInactiveInterval(60 * 30);
return "redirect:/";
}
로그인 과정을 담당하는 서비스클래스이다.
ImUser user = imUserRepository.findByUserId(username);
사용자가 로그인버튼을 누를 시 입려된 아이디칸에 데이터인 username으로 DB user 테이블을 조회한다
if (user == null) {
return "redirect:/?error=id";
}
만약 user 객체에 데이터가 null값으로 나온다면 해당 아이디는 없는것이니
error=id를 응답해준다(error=id를 한 이유는 밑에서 설명할게요)
// 아이디가 있다면 비밀번호 조회 후 불일치일 경우 error=pw 전달
if (!Encoder.matchPassword(password, user.getUserPassword())) {
return "redirect:/?error=pw";
}
만약 아이디가 조회되어 null값이 아니라면 user 객체에는 그 아이디값의 정보가 담길것이다
그러면 유저가 로그인 시 입력한 비밀번호와 DB에 저장된 암호화된 비밀번호가 서로 일치하는지 검증해준다
(암호화된 비밀번호 일치여부는 전에 작성한 회원가입 암호화비밀번호에 내용이 있습니다)
불일치할 경우엔, error=pw로 응답해준다
session.setAttribute("userCode", user.getUserCode());
session.setAttribute("userNickname", user.getUserNickname());
session.setMaxInactiveInterval(60 * 30);
return "redirect:/";
아이디와 비밀번호가 조회되고 일치하게 된다면 로그인 정보에대해 알맞은 값을 입력한것이니,
세션에 그 아이디의 고유코드를 저장해준다.
(세상에 어떤 공격이 있을지 모르고.. 세션에 비밀번호나 이메일 주민번호 핸드폰번호를 다 담아두면 위험하다고 판단되어 유저 고유코드만 담았다. 그리고 유저의 고유코드만 있다면 어떤 페이지던간에 그 유저코드를 이용해서 나타낼 수 있으니까. 닉네임은 바로 노출해줘야하는곳이있어서 같이 담아줫다)
이제 세션에 유저코드가 담겼다. 그러면 현재 이 사이트에서는 내 계정이 세션에 담겼으니 로그인됏다고 볼 수 있다.
session.setMaxInactiveInterval(60 * 30);
세션의 유효 기간을 설정하는 코드이다.
이 세션에 대해서 서버에 어떠한 요청도 30분동안 이뤄지지않는다면 해당 세션은 자동으로 만료되어 더 이상 유효하지 않게된다. 이후 사용자가 다시 페이지를 요청할 때는 새로운 세션이 생성되거나, 만약 세션이 만료되지 않았다면 기존 세션이 재사용된다. 사용자가 로그인만 하고 서버에 어떠한 요청도 보내지 않고 30분이 지나면 세션 만료
이렇게 하면 불필요한 세션 정보가 서버에 계속 남아있지 않게 되어, 서버의 메모리 사용을 줄일 수 있다.
JSP
<!-- 세션에 유저코드가 없으면 로그인 폼 -->
<c:if test="${empty sessionScope.userCode }">
<div class="loginBox">
<form action="/login" method="post" id="loginForm">
<input type="text" name="username" id="username" placeholder="아이디">
<input type="password" name="password" id="password" placeholder="비밀번호">
<button type="submit">로그인</button>
<div class="signUpBox">
<a class="signUp" href="/login/signUp">회원가입</a>
<span class="divider">|</span>
<a class="signUp" href="/login/findId">아이디/비밀번호 찾기</a>
</div>
</form>
</div>
</c:if>
<!-- 세션에 유저코드가 있으면 유저정보 및 기능 -->
<c:if test="${not empty sessionScope.userCode}"> <!-- 세션에 userCode가 있으면 사용자 메뉴 출력 -->
<div class="userBox">
<p>${sessionScope.userNickname}</p>
<ul>
<li><a href="/writePost">글 쓰러가기</a></li>
<li><a href="/myPosts">내가 쓴 글 보기</a></li>
<div class="signUpBox">
<a class="signUp" href="/logout">로그아웃</a>
</div>
</ul>
</div>
</c:if>
<c:if test="${empty sessionScope.userCode }">
<c:if>는 JSP에서 JSTL의 태그 중 하나로 조건문을 처리하는 데 사용된다.
sessionScope란 JSP에서 세션에 저장된 객체에 접근 할 때 사용되는 것이다.
뒤에 userNickname은 세션에 저장된 키값인거다(key:value)
그러면 둘이 합치면 이런 명령어가 된다
만약 세션 내 userCode라는 키값에 데이터가 없다면(empty)
아래의 내용을 보여줘라
반대로
<c:if test="${not empty sessionScope.userCode}">
이건 세션 내 userCode라는 키값에 데이터가 있다면(not empty)
아래의 내용을 보여줘라
이렇게 세션에 로그인정보 저장된 내용이 있고없고의 차이로
로그인창 or 로그인된 유저의 닉네임, 로그아웃, 글작성 등 기능을 나타낸다.
로그아웃 과정(session 데이터 삭제 )
Controller Class
//로그아웃하기
@RequestMapping("/logout")
public String logout(HttpSession session) {
return imLoginService.logout(session);
}
사용자가 로그아웃 버튼을 눌렀을 때, 사용자가 부여받은 세션을 참고하여
로그아웃을 담당하는 imLoginService.logout 메소드를 호출하여준다.
Service Class
//로그아웃 _ 세션 내 모든내용 지우기
public String logout(HttpSession session) {
/* 세션전체삭제 */
session.invalidate();
return "redirect:/";
}
session.invalidate();
이 메소드가 호출이되면 기존 세션이 무효화가 된다. 기존의 세션에 저장되 정보나 사용자가 가지고있던(앞서 설명한 세션ID가 다 사라진다고 보면된다)
그러면 사용자는 세션이 이제 없는건가?
아니다
이후 클라이언트가 다시 요청을 보내면 서버는 자동으로 새로운 세션을 생성하여 클라이언트에게 새로운 세션 ID를 할당한다.
여기서는
session.invalidate();
세션을 삭제하고 곧바로
return "redirect:/";
에 의해 새로운 요청이 발생하여, 새로운 세션이 생성된다
그럼 JSP에서는 새로운 세션이 생성되어 세션 내에는 데이터가 없으니
로그인 창으로 유지가 될 것이다.
추가) error=id, pw
앞서 내용에서 아이디가 조회되지 않았을 때는 error=id, 비밀번호가 일치하지 않았을 때는 error=pw를 보내준 이유는
로그인 요청을 form태그로 했기 때문이다.
form태그는 서버와의 상호작용 후 결과를 페이지로 응답받게 되어, 서버에서 반환된 값에 따라 페이지 이동이나 렌더링이 이루어진다
let urlParams = new URLSearchParams(window.location.search);
if (urlParams.get('error') === 'id') {
alert("아이디가 존재하지 않습니다.");
window.location.href = '/';
} else if (urlParams.get('error') === 'pw') {
alert("비밀번호가 틀렸습니다.");
window.location.href = '/';
}
그래서 URL 파라마미터로 전달된 값을 자바스크립트로 읽어들여 각 에러값에 대해 클라이언트에 알림을 띄워줬다.
error === id면 "아이디가 존재하지 않습니다"를 나타내고 메인페이지 "/"로 이동하였고,
error === pw면 "비밀번호가 틀렸습니다"를 나타내고 메인페이지 "/"로 이동하게 만들었다.
'개인프로젝트 > I'm' 카테고리의 다른 글
Spring HandlerInterceptor 비로그인 시 페이지 진입이 불가한 페이지 설정 (0) | 2024.12.29 |
---|---|
Spring 게시글 작성하기 > DB 게시글 저장 (+MyBatis, CKeditor) (0) | 2024.12.29 |
Spring JPA 회원가입 + 비밀번호 암호화 Spring security(+회원 UUID 고유코드 생성)(241229 서버 2차 조건 검증 기능 추가) (0) | 2024.12.24 |
Spring 회원가입 시 인증번호 일치 여부 확인 / redis 사용 (2) | 2024.12.21 |
Spring Google 이메일 연동 + 메일 보내기(회원가입) + @Async로 인한 사용자 경험 개선 (0) | 2024.12.21 |