본문 바로가기
Spring공부/4-스프링시큐리티

로그인과 로그아웃처리(2)

by 으노으뇨 2021. 10. 18.
728x90
반응형
SMALL

커스템 로그인 페이지

앞서 언급했듯이 스프링 시큐리티에서 기본적으로 로그인페이지를 제공하긴한다.

현실적으로 화면 디자인등의 문제로 사용하기 불편하고 안예쁘다. 

때문에 거의 대부분 경우 별도의  URI를 이용해서 로그인 페이지를 제작해서 사용한다.

이를 이용하는 방식은 접근 제한 페이지와 유사하게 직접 특정 URI를 지정할 수 있다.

<bean id="customAccessDenied"
		class="org.study.security.CustomAccessDeniedHandler"></bean>
	<security:http auto-config="true" use-expressions="true">
		<security:intercept-url pattern="/sample/all"
			access="permitAll" />
		<security:intercept-url
			pattern="/sample/member" access="hasRole('MEMBER')" />
		<security:intercept-url pattern="/sample/admin"
			access="hasRole('ADMIN')" />
		<security:form-login login-page="/customLogin"/>
		
<!-- 		<security:access-denied-handler -->
<!-- 			error-page="/accessError" /> -->
		<security:access-denied-handler
			ref="customAccessDenied" />
	</security:http>

보이는 바와같이

<security:form-login login-page="/customLogin"/>한줄을 수정 추가해주었다. 이제 로그인화면은 customLogin으로 이동하게 될것이다.

login-page속성의 Uri는 get방식으로 접근하는 uri로 지정한다.

그리고 CommonController에 /customLogin에 해당하는 매서드를 추가해준다.

	@GetMapping("/customLogin")
	public void loginInput(String error, String logout, Model model) {

		log.info("error: " + error);
		log.info("logout: " + logout);

		if (error != null) {
			model.addAttribute("error", "Login Error Check Your Account");
		}

		if (logout != null) {
			model.addAttribute("logout", "Logout!!");
		}
	}

loginInput()은 Get방식으로 접근하고, 에러 메세지와 로그아웃 메시지를 파라미터로 사용할 수 있다.

view폴더에 customerLogin.jsp 파일을 추가해주자

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

	<h1>고객 로그인 화면</h1>
	<h2>
		<c:out value="${error}" />
	</h2>
	<h2>
		<c:out value="${logout}" />
	</h2>

	<form method='post' action="/login">

		<div>
			<input type='text' name='username' value='admin'>
		</div>
		<div>
			<input type='password' name='password' value='admin'>
		</div>
		<div>
			<input type='submit'>
		</div>
		<input type="hidden" name="${_csrf.parameterName}"
			value="${_csrf.token}" />
	</form>

</body>
</html>

대강 코드만 작성하고, 브라우저에서 http://localhost:8080/customLogin 접근이 제한이 필요한 URI에 접근하면 작성된 

페이지의 내용을 볼 수 있다..

jsp 파일에 대해 설명을 하자면,

<form >태그의 액션 속성값이 login으로 지정되어있다.

실제로 로그인의 처리작업은 login을 통해 이루어지는데, 반드시 POST방식으로 데이터를 전홍 해야한다.

<input>태그의 name속성은 기본적으로 username과 password속성을 이용한다.

마지막으로 <input type="hidden">태그는 특이하게 ${_csrf.parameterName}으로 처리한다.

이 값은 실제 브라우저에서는 _csrf라는 이름으로 처리된다.

브라우저에서 페이지 소스 보기를 하면 태그값이 생성된것을 볼 수 있다.

이때  value값은 임의 값을 얻는다.

만일 사용자가 패스워드등을 잘못 입력하는 경우에는 자동으로 다시 로그인 페이지로 이동하게 된다.


CSRF(Cross-site request forgery)공격과 토큰

[해당 URL은 추후 제작하여 연동시키겠음]


로그인 성공과 AuthenticationSuccessHandler

로그인을 처리하다보면 로그인 성공 이후에 특정한 동작을 하도록 제어하고 싶은 경우가 있다.

예를 들어 만일 로그인 할때, admin계정 /admin 패스워드로 로그인 했다면, 사용자가 어떤 경로로

로그인 페이지로 들어오건 무조건 /sample/admin으로 이동하게 하거나, 별도의 쿠키 등을 생성해서 

처리하고 싶은 경우를 생각해 볼 수 있다.

이런 경우 스프링 시큐리티에서는 AuthenticationSuccessHandler 라는 인터페이스를 구현해서 설정할 수 있따.

이는 security 패키지에 CustomLoginSuccessHandler클래스를 생성해서 공부해보자

package org.study.security;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import lombok.extern.log4j.Log4j;
@Log4j
public class CustomLoginSuccessHandler implements AuthenticationSuccessHandler {
	@Override
	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication auth)
			throws IOException, ServletException {
		log.warn("Login Success");
		List<String> roleNames = new ArrayList<>();
		auth.getAuthorities().forEach(authority -> {
			roleNames.add(authority.getAuthority());
		});
		log.warn("ROLE NAMES: " + roleNames);
		if (roleNames.contains("ADMIN")) {
			response.sendRedirect("/sample/admin");
			return;
		}
		if (roleNames.contains("MEMBER")) {
			response.sendRedirect("/sample/member");
			return;
		}
		response.sendRedirect("/");
	}
}

CustomLoginSuccessHandler는 로그인 한 사용자에 부여된 권한 Authentication객체를 이용해서 사용자가 가진 모든 권한을 문자열로 체크한다.

만일 사용자가 ADMIN권한을 가졌다면, 로그인 후 바로 /sample/admin으로 이동하게 하는 방식이다.

security-context.xml에서는 작성된 CustomLoginSuccessHandler를 빈으로 등록하고 로그인 성공 후 처리를 담당하는

핸들러로 지정한다.

	<bean id="customLoginSuccess"
		class="org.study.security.CustomLoginSuccessHandler"></bean>
		<security:form-login login-page="/customLogin"
			authentication-success-handler-ref="customLoginSuccess" />

브라우저에서 기존과 달리 /customLogin의 호출부터 시작해서 로그인을 하면 사용자의 권한에 따라 다른페이지를 호출하는 것을 확인해보자

 

로그인처리가 된것을 알수가있다.


로그아웃의 처리와 LogoutSuccessHandler

로그인과 마찬가지로 특정한 uri를 지정하고 , 로그아웃 처리 후 직접 로직을 처리할 수 있는 핸들러를 등록할 수있다.

<security:logout logout-url="/customLogout"
			invalidate-session="true" />

시큐리티 컨텍스트xml파일에 위와같은 한줄을 추가해준다.

로그아웃 시 세션을 무효화시키는 설정이나 특정한 쿠키를 지우는 작업을 지정할 수 있다.

그리고 컨트롤러 패키지에 CommonController에는 get방식으로 로그아웃을 결정하는 페이지에 대한 메서드를 처리한다.

	@GetMapping("/customLogout")
	public void logoutGET() {

		log.info("custom logout");
	}

그리고 이를 처리하기위한 customLogout.jsp파일을 추가한다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
  
<h1> Logout Page</h1>

<form action="/customLogout" method='post'>
<input type="hidden"name="${_csrf.parameterName}"value="${_csrf.token}"/>
<button>로그아웃</button>
</form>

</body>
</html>

로스아웃 역시 로그인과 동일하게 실제작업은 /customLogout으로 처리하고, POST방식으로 이루어진다. 

POST방식으로 처리되기 때문에 CSRF토큰값을 같이 지정한다.

POST방식으로 처리되는 로그아웃은 스프링 시큐리티의 내부에서 동작한다.

만일 로그아웃 시 추가적인 작업을 해야한다면 logoutSuccessHandler를 정의해서 처리한다.

= 로그인과 같다.

로그아웃의 처리는 실제로는 어떤 결과를 이용해도 크게 상관은 없다.

로그아웃을 테스트 하기위해 /sample/admin.jsp페이지에 로그아웃으로 이동하는 링크를 추가한다.

로그인이 된 화면에서 /sample/admin 에서  logout 을 선택하면 get방식으로 

LogoutPage화면을 보게된다.

post방식으로  로그아웃버튼을 누르게되면 로그인화면을 보게된다.

자동으로 로그인 페이지를 호출하게 된다. 이 부분은 스프링 시큐리티의 기본 설정이므로, 

필요하다면 logout-success-ril속성들을 이용해서 변경할 수 있따.

지금 사진과 같은 화면에서 로그아웃이 정상적으로 되었는지 보려면

http://localhost:8080/sample/admin을 통해 시도하면 로그인창이 어김없이 나타날 것이다.

 

728x90
반응형
LIST

댓글