스프링 기초_Security_3

bitna's study note·2022년 7월 23일

스프링

목록 보기
27/54

7월 23일

1.커스텀 로그인 페이지
별도의 URI를 이용해서 로그인 페이지를 다시 제작해서 사용할때,
접근제한 페이지와 유사하게 직접 특정한 uri를 지정할수 있음

(1)security-context.xml 수정

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

<security:http>

		<security:intercept-url pattern="/sample/all" access="permitAll"/>
		<security:intercept-url pattern="/sample/member" access="hasRole('ROLE_MEMBER')"/>
		<security:intercept-url pattern="/sample/admin" access="hasRole('ROLE_ADMIN')"/> 
	
	<!--/accessError 라는 uri로 접근제한 시 보이는 화면을 처리합니다-->
	<security:access-denied-handler error-page="/accessError"/>
	
	<!-- 로그인페이지 따로 URI 설정하기 -->
	<security:form-login login-page="/customLogin" />
	
</security:http>

<security:authentication-manager>
	<security:authentication-provider>
		<security:user-service>
			<security:user name="member" password="{noop}member" authorities="ROLE_MEMBER" />
			<security:user name="admin" password="{noop}admin" authorities="ROLE_ADMIN" />
		</security:user-service>
	</security:authentication-provider>
</security:authentication-manager>

</beans>

(2)CommonController 클래스에 추가하기
로그인페이지 속성의 uri는 반드시 get방식으로 접근하는 url를 지정
customLogin.jsp를 get방식으로 접근

package com.keduit.controller;

@Controller
@Log4j
public class CommonController {
	
	
	@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!!!!");
		}
		
	}
}

(3)customLogin.jsp 작성
form 태그의 action의속성을 '/login'을 지정되어있는데,
실제 로그인의 처리는 '/login' 통해서 이뤄지는데 꼭 Post방식으로 보내야함.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>Custom Login Page!!!!!!!!</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>
    
    <!-- EL의 값은 실제 브라우져에서는 '_csrf'라는 이름으로 처리됩니다. -->
	<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</form>
</body>
</html>

2.CSRF(Cross-site request forgery) 토큰
스프링 시큐리티는 post방식을 이용하는 경우 기본적으로 CSRF 토큰이라는 것을 이용하게 됨.
이것은 '사이트간 위조 방지'를 목적으로 특정한 값의 토큰을 사용하는 방식이다.

CSRF 토큰이란 사용자가 이의로 변하는 특정한 토큰값을 서버에서 체크하는 방식임.
서버에서 브라우져에 데이터를 전송 할 때 CSRF 토큰도 같이 전송 합니다.
사용자가 post방식 등으로 특정한 작업을 할때는 브라우저에서 전송된 CSRF 토큰의 값과 서버가 보관하고 있는 토큰의 값을 비교하여, 만일 토큰값이 다르다면 작업을 처리하지 않는 방식이다.

서버에서 생성하는 토큰은 일반적으로 난수를 생성해서 해커가 패턴을 찾을수 없도록한다.

일반적으로 CSRF 토큰은 세션을 통해서 보관하고, 브라우져에서 전송된 CSRF 토큰값을 검사하는 방식으로 처리됨.스프링 시큐리티에서는 CSRF 토큰 생성을 비활성화하거나 CSRF 토큰을 쿠키를 이용해서 처리하는 등의 설정을 지원합니다.

<security:csrf disabled="true" />

3.로그인 성공과 AuthenticationSuccessHandler
로그인을 처리하다 보면 로그인 성공 이후에 특정한 동작을 하도록 제어하고 싶은 경우가 있다.
이런경우 스프링 시큐리티에서는 AuthenticationSuccessHandler 라는 인터페이스를 구현해서 설정할수 있음.

(1)CustomLoginSuccessHandler 클래스 작성
로그인 한 사용자에 부여된 권한Authentication 객체를 이용해서 사용자가 가진 모든 권한을 문자열로 체크함.
만일 사용자가 ROLE_ADMIN 권한을 가졌다면 로그인후에 '/sample/admin'으로 이동하게하는 방식

package com.keduit.security;

@Log4j
public class CustomLoginSuccessHandler implements AuthenticationSuccessHandler{
	
	
	@Override
	public void onAuthenticationSuccess(HttpServletRequest request, 
	HttpServletResponse response,Authentication auth) 
	throws IOException, ServletException {
		
		//레벨별로 로그출력하는데 warn 은 처리 가능한 문제, 향후 시스템 에러의 원인이 될 수 있는정도 수준일때 메세지를 보여줌
		log.warn("Login Success");
		List<String> roleNames = new ArrayList<>();
		auth.getAuthorities().forEach(authority ->{roleNames.add(authority.getAuthority());
		});
		
		//레벨별로 로그출력하는데 warn 은 처리 가능한 문제, 향후 시스템 에러의 원인이 될 수 있는정도 수준일때 메세지를 보여줌
		log.warn("ROLE NAMES: "+roleNames);
		if(roleNames.contains("ROLE_ADMIN")) {
			response.sendRedirect("/sample/admin");
			return;
		}
		if(roleNames.contains("ROLE_MEMBER")) {
			response.sendRedirect("/sample/member");
			return;
		}
		response.sendRedirect("/");
		
	}

}

(2)security-context.xml 수정
security-context.xml에서는 작성된 CustomLoginSuccessHandler를 빈으로 등록하고 로그인 성공 후 처리를 담당하는 핸들러로 지정합니다.

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

<security:http>

		<security:intercept-url pattern="/sample/all" access="permitAll"/>
		<security:intercept-url pattern="/sample/member" access="hasRole('ROLE_MEMBER')"/>
		<security:intercept-url pattern="/sample/admin" access="hasRole('ROLE_ADMIN')"/> 
	
	<!--/accessError 라는 uri로 접근제한 시 보이는 화면을 처리합니다-->
	<security:access-denied-handler error-page="/accessError"/>
	
	<!-- 로그인페이지 따로 URI 설정하기, 성공후 authentication-success-handler-ref 설정으로 해당클래스 지정해주기-->
	<security:form-login login-page="/customLogin" authentication-success-handler-ref="CustomLoginSuccessHandler"/>
	
</security:http>

<security:authentication-manager>
	<security:authentication-provider>
		<security:user-service>
			<security:user name="member" password="{noop}member" authorities="ROLE_MEMBER" />
			<security:user name="admin" password="{noop}admin" authorities="ROLE_ADMIN" />
		</security:user-service>
	</security:authentication-provider>
</security:authentication-manager>

</beans>

	

4.로그아웃과 로그아웃성공시 화면 처리
로그인과 마찬가지로 특정한 uri를 지정하고 로그아웃 처리 후 직접 로직을 처리 할 수 있는 핸들러를 등록할수 있음

(1)security-context.xml 수정

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

<security:http>

		<security:intercept-url pattern="/sample/all" access="permitAll"/>
		<security:intercept-url pattern="/sample/member" access="hasRole('ROLE_MEMBER')"/>
		<security:intercept-url pattern="/sample/admin" access="hasRole('ROLE_ADMIN')"/> 
	
	<!--/accessError 라는 uri로 접근제한 시 보이는 화면을 처리합니다-->
	<security:access-denied-handler error-page="/accessError"/>
	
	<!-- 로그인페이지 따로 URI 설정하기 -->
	<security:form-login login-page="/customLogin" authentication-success-handler-ref="CustomLoginSuccessHandler"/>
	
	<!-- 로그아웃 페이지 URI 설정하기 -->
	<security:logout logout-url="/customLogout" invalidate-session="true"/>
	
</security:http>

<security:authentication-manager>
	<security:authentication-provider>
		<security:user-service>
			<security:user name="member" password="{noop}member" authorities="ROLE_MEMBER" />
			<security:user name="admin" password="{noop}admin" authorities="ROLE_ADMIN" />
		</security:user-service>
	</security:authentication-provider>
</security:authentication-manager>

</beans>

	

(2)CommonController 클래스 추가하기
로그아웃시 세션을 무효화 시키는 설정이나 특정한 쿠키를 지우는 작업을 지정할 수 있습니다.
CommonController에는 get방식으로 로그아웃을 결정하는 페이지에 대한 메서드를 처리합니다.

package com.keduit.controller;


@Controller
@Log4j
public class CommonController {

	@GetMapping("/customLogout")
	public void logoutGET() {
		log.info("custom logout!!!");
	}
}

(3)customLogout.jsp 작성하기
로그아웃역시 로그인과 동일하게 실제 작업은 '/customLogout'의 post방식으로 처리되기 때문에 CSRF토큰값을 같이 지정 합니다.

post방식으로 처리되는 로그아웃은 스프링 시큐리티의 내부에서 동작합니다.
만일 로그아웃시 추가적인 작업을 해야 한다면 logoutSuccessHandler를 정의해서 처리합니다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>Log Out Page!!!</h1>

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

(4)로그아웃 테스트를 위해서 admin.jsp 내용추가

<a href="/customLogout">로그아웃</a>
profile
좋은개발자가 되기위한 삽질기록 노트

0개의 댓글