본문 바로가기
  • 실행력이 모든걸 결정한다
Spring Series/Spring Framework

[Spring] Spring Security(7) - 자동 로그인 기능 추가

by 김코더 김주역 2021. 2. 9.
반응형

포털 사이트에 로그인을 해본 경험이 있다면 로그인 상태 유지 여부를 선택할 수 있는 기능을 봤을 것이다.

이 기능을 영어로는 remember-me 라고도 한다.

 

네이버

 

다음

 

이 체크 박스를 눌러 체크하고 로그인을 진행하면 브라우저에 쿠키가 남게 되며, 다음 방문 때도 로그인 상태를 유지시켜준다. 단, 쿠키 시간이 만료되거나 로그아웃을 했다면 다음 방문때는 다시 로그인을 해야한다.

이러한 기능을 우리가 만드는 웹사이트에 추가해보자.

 

 

예제 프로젝트의 구성은 이렇다.

빨간 상자로 표시해둔 파일들은 이번에 수정할 파일들이다.

 

1. 로그인 VIEW에 로그인 유지 체크박스 추가

<login.jsp>

아래 한줄을 추가했다.

<input type="checkbox" name="_spring_security_remember_me">로그인 유지<br>

input 타입은 체크박스로 하고, 따로 커스터마이징 하지 않았다면 name 속성은 "_spring_security_remember_me"로 설정해줘야 한다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<link rel="stylesheet" href="/demo/resources/sample.css">
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>LOGIN</h1>
	<form action="login_check" method="post">
		<c:if test="${LoginFailMessage!=null}">
			<p> Error : <c:out value="${LoginFailMessage}"/> </p>
		</c:if>
		USER : <input type="text" name="user_id"><br>
		PASSWORD : <input type="password" name="user_pw"><br>
		<input type="checkbox" name="_spring_security_remember_me">로그인 유지<br>
		<input type="submit" value="Login">
	</form>
</body>
</html>

 

 

2. Security 설정 파일

<security-context.xml>

security schema의 remember-me 설정을 추가했는데, 이 것이 자동 로그인 설정이다.

remember-me 에는 이러한 속성들을 설정할 수 있다.

  • key : 웹 검사를 해보면 쿠키 값은 암호화가 되어있음을 확인 할 수 있는데, 암호화 혹은 복호화를 할 때 기준이 되는 key값이다.
  • token-validity-seconds : 쿠키 유효 기간, 초 단위(604800초=7일=1주)
  • authentication-success-handler-ref : 자동 로그인 성공 처리 핸들러 (저번에 커스터마이징한 success 핸들러임)
  • user-service-ref : UserDetailsService 인터페이스를 상속받은 구현체의 bean 객체를 넣어주면 되는데, provider을 커스터마이징 했다면 생략한다.

그리고 로그아웃 했을 때는 쿠키를 삭제하여 자동 로그인을 풀어줘야 한다.

이를 위해 logout 설정의 delete-cookies 속성에 "JSESSIONID" 와 "SPRING_SECURITY_REMEMBER_ME_COOKIE" 라는 이름의 쿠키를 지정했다. 이렇게 설정해주면, 사용자가 로그아웃 할 때 저 2개의 쿠키가 제거된다.

방금 언급한 두 쿠키들은 잠시 후 설명하겠다.

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:s="http://www.springframework.org/schema/security"
	xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

	<s:http auto-config="true" use-expressions="true">
		<s:form-login
			username-parameter="user_id"
			password-parameter="user_pw"
			login-processing-url="/login_check" 
			login-page="/login.html" 
			authentication-failure-handler-ref="loginFailHandler"
			authentication-success-handler-ref="loginSuccessHandler"
		/>
		<s:logout 
			logout-url="/security_logout"
			logout-success-url="/"
			invalidate-session="true"
			delete-cookies="JSESSIONID,SPRING_SECURITY_REMEMBER_ME_COOKIE"
		/>
		<s:remember-me key="kimcoder" token-validity-seconds="604800"
			authentication-success-handler-ref="loginSuccessHandler"/>
			
		<s:intercept-url pattern="/vip.html*" access="hasRole('ROLE_USER')"/>
		<s:intercept-url pattern="/admin.html*" access="hasRole('ROLE_ADMIN')"/>
		<s:access-denied-handler ref="accessFailHandler"/>
	</s:http>
	<s:authentication-manager>
		<s:authentication-provider ref="loginAuthenticationProvider"/>
	</s:authentication-manager>
	
</beans:beans>

 

 

3. Success 핸들러 수정

<LoginSuccessHandler.java>

사용자가 웹 사이트에 방문해서 로그인 한다고 할 때 쿠키가 없다면 사용자가 직접 로그인 해줘야 하고, 쿠키가 있다면 자동 로그인된다.

사용자가 수동적으로 로그인 할 때는 provider을 거쳐 success 핸들러로 이동하는데, 브라우저에 쿠키가 남아있어 자동 로그인되는 경우에는 TokenBasedRememberMeServices 객체에서 대신 인증을 해주고 success 핸들러로 이동한다.

Spring Security(6) 포스팅에서는 로그인 인증을 마치고 UserDetails 객체의 패스워드를 null로 가려주는 작업을 provider에서 수행했는데, 자동 로그인되는 경우에는 provider을 거치지 않으므로 success 핸들러에서 UserDetails 객체의 패스워드를 null로 가려주는 처리를 추가해준 것이다.

수동/자동 로그인 모두 success 핸들러를 거치기 때문에 provider에서 UserDetails 객체의 패스워드를 null로 설정하는 처리는 지워도 무관하다.

추가된 코드는 주석에 (추가)라고 표시했다.

package com.example.demo;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.stereotype.Service;

@Service("loginSuccessHandler")
public class LoginSuccessHandler implements AuthenticationSuccessHandler{

	//(추가)
	@Autowired
	UserDetailsService loginService;
	
	@Override
	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
			Authentication authentication) throws IOException, ServletException {
		// TODO Auto-generated method stub
		
		//경우1 : 로그인 인증을 위해 Spring Security가 요청을 가로챈 경우
		RequestCache requestCache = new HttpSessionRequestCache();
		SavedRequest savedRequest = requestCache.getRequest(request, response);
		
		//경우2 : 사용자가 직접 로그인 버튼을 누른 경우
		String prevPage = (String) request.getSession().getAttribute("prevPage");
		if(prevPage!=null) request.getSession().removeAttribute("prevPage");
		
		String uri = request.getContextPath();
		//경우1 일 경우 uri에 경로 저장
		if(savedRequest!=null) uri = savedRequest.getRedirectUrl();
		//경우2 일 경우 uri에 경로 저장
		else if(prevPage!=null) uri = prevPage;
		
		//(추가)
		UserDetailsDto userDetailsDto = (UserDetailsDto) loginService.loadUserByUsername(authentication.getName());
		userDetailsDto.setPassword(null);
		
		response.sendRedirect(uri);
	}
}

 

 

4. 로그인 VIEW

 

5. 실행(웹 검사)

이미지가 잘 보이지 않을 때는 해당 이미지를 클릭하면 잘 보일 것이다.

 

로그인 유지 체크박스를 클릭하여 쿠키 생성

"JSESSIONID" 와 "SPRING_SECURITY_REMEMBER_ME_COOKIE" 쿠키가 존재하는 모습을 볼 수 있다.

JSESSIONID 쿠키는 기본적으로 있는 쿠키이며, 로그아웃하여 JSESSIONID 쿠키를 삭제하면 다른 value값을 가진 새 JSESSIONID 쿠키가 자동으로 생성된다.

SPRING_SECURITY_REMEMBER_ME_COOKIE 쿠키가 바로, 로그인 유지를 위해 생성된 쿠키이다.

 

로그아웃

기존 JSESSIONID 쿠키와 SPRING_SECURITY_REMEMBER_ME_COOKIE 쿠키는 지워졌고, 새로운 value값을 가진 새 JSESSIONID 쿠키가 만들어졌다. 

반응형

댓글