본문 바로가기
프로그래밍/스프링프레임워크

스프링프레임웍 - Spring Security(2) : 커스텀 로그인 화면 및 권한에 따른 접근 제어

by pentode 2018. 4. 8.

앞의 글 "스프링프레임웍 - Spring Security(1) : 기본 설정"에 이어 이번에는 커스텀 로그인 화면을 만드는 방법을 알아보겠습니다. 전체소스는 하단에 추가되어 있습니다.

 

 

1. 스프링 시큐리티 태그라이브러리를 사용하기 위한 의존성을 추가합니다.

 

메인화면에서 로그인이 되어 있지 않으면 로그인 링크를 보여주고, 로그인이 되어 있으면 로그아웃 링크를 보여주기 위해서 사용됩니다.

 

<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-taglibs</artifactId>
	<version>4.2.1.RELEASE</version>
</dependency>

 

 

2. 커스텀 로그인 화면을 사용하기 위한 설정을 추가 합니다.

 

커스텀 로그인 화면 제공 및 권한(역할)에 따른 접근 제어도 테스트 해봅니다.

 

<!-- 검사 URL -->
<http use-expressions="true">
	<intercept-url pattern="/login/loginForm.do" access="permitAll" />
	<intercept-url pattern="/home.do" access="permitAll" />
	<intercept-url pattern="/admin/**" access="hasRole('ADMIN')" />
	<intercept-url pattern="/**" access="hasAnyRole('USER, ADMIN')" />

	<form-login login-page="/login/loginForm.do"
                default-target-url="/home.do"
                authentication-failure-url="/login/loginForm.do?error"
                username-parameter="id"
                password-parameter="password" />
	<logout logout-url="/logout" logout-success-url="/home.do" />
	<access-denied-handler error-page="/login/accessDenied.do" />
</http>

<!--  provider  -->
<authentication-manager>
	<authentication-provider>
		<user-service>
			<user name="user" password="password" authorities="ROLE_USER" />
			<user name="admin" password="password" authorities="ROLE_ADMIN" />
		</user-service>
	</authentication-provider>
</authentication-manager>

 

- provider 를 통해 ROLE_USER, ROLE_ADMIN 두 개의 역할을 가지는 사용자를 추가 합니다. 각각 아이디가 "user"와 "admin" 입니다.

 

- <intercept-url pattern="/login/loginForm.do" access="permitAll" /> : 로그인 폼 URL에는 로그인 하지 않아도 접근할 수 있도록 합니다.

- <intercept-url pattern="/home.do" access="permitAll" /> : 메인화면도 로그인 없이 접근할 수 있도록 합니다.

- <intercept-url pattern="/admin/**" access="hasRole('ADMIN')" /> : /admin/** URL 은 ADMIN 권한이 있어야 접근할 수 있도록 합니다.

- <intercept-url pattern="/**" access="hasAnyRole('USER, ADMIN')" /> : 그 외 나머지는 USER 또는 ADMIN중 하나로도 권한이 있으면 접근할 수 있습니다.

 

- <form-login> 요소를 커스터마이징 합니다.

  + login-page="/login/loginForm.do" : 커스텀 로그인 페이지를 지정합니다.

  + default-target-url="/home.do" : 로그인 후에 기본으로 보여질 페이지 입니다. 특정 페이지를 클릭해서 로그인 화면이 나왔다면 로그인 성공후 앞서 클릭한 페이지로 이동합니다.

  + authentication-failure-url="/login/loginForm.do?error" : 로그인 실패시 보여질 페이지를 지정합니다. 여기서는 로그인 페이지를 다시 보여줍니다. 기본값은 /login?error 입니다.

  + username-parameter="id" : 로그인 폼에 아이디 입력 필드에 사용될 name 입니다. 기본값은 "username"입니다.

  + password-parameter="password" : 로그인 폼에 비밀번호 입력 필드에 사용될 name 입니다. 기본값은 "password" 입니다.

 

- <logout> 요소를 커스터마이징 합니다.

  + logout-url="/logout" : 로그아웃에 사용될 페이지를 지정합니다. 기본값은 "/logout" 입니다.

  + logout-success-url="/home.do" : 로그아웃에 성공하였을 때 이동할 페이지를 지정합니다.

 

- <access-denied-handler error-page="/login/accessDenied.do" /> : 로그인 하였으나 권한이 없는 요청을 하였을 경우 보여지는 페이지를 지정합니다.

 

 

3. 추가된 페이지들에 대해서 간단히 알아보겠습니다.

 

- 메인화면인 home.jsp 파일 입니다.

 

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<!DOCTYPE html>
<html lang="ko">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<title>Home</title>
</head>
<body>
	<h1>Home!</h1>

	<sec:authorize access="isAnonymous()">
	<p><a href="<c:url value="/login/loginForm.do" />">로그인</a></p>
	</sec:authorize>

	<sec:authorize access="isAuthenticated()">
	<form:form action="${pageContext.request.contextPath}/logout" method="POST">
		<input type="submit" value="로그아웃" />
	</form:form>
	</sec:authorize>

	<h3>
		[<a href="<c:url value="/intro/introduction.do" />">소개 페이지</a>]
		[<a href="<c:url value="/admin/adminHome.do" />">관리자 홈</a>]
	</h3>
</body>
</html>

 

- 스프링 시큐리티 태그를 사용하기 위해서 추가합니다.

 

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>

 

- 로그인 안된 상태를 확인합니다.

 

<sec:authorize access="isAnonymous()"></sec:authorize>

 

- 로그인 된 상태를 확인합니다.

 

<sec:authorize access="isAuthenticated()"></sec:authorize>

 

- 로그인폼 페이지입니다.(loginForm.jsp)

 

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html lang="ko">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<title>로그인 페이지</title>
</head>
<body>

	<h3>아이디와 비밀번호를 입력해주세요.</h3>

	<c:url value="/login" var="loginUrl" />
	<form:form name="f" action="${loginUrl}" method="POST">
		<c:if test="${param.error != null}">
			<p>아이디와 비밀번호가 잘못되었습니다.</p>
		</c:if>
		<c:if test="${param.logout != null}">
			<p>로그아웃 하였습니다.</p>
		</c:if>
		<p>
			<label for="username">아이디</label>
			<input type="text" id="id" name="id" />
		</p>
		<p>
			<label for="password">비밀번호</label>
			<input type="password" id="password" name="password"/>
		</p>
		<button type="submit" class="btn">로그인</button>
	</form:form>
</body>
</html>

 

- 아이디 입력 필드의 name 을 기본값이 "username"이 아니라 "id" 로 변경하였습니다.

- 로그인에 실패했을 경우 param.error 가 따로 오므로 메세지를 보여줍니다.

- param.logout 는 로그아웃 페이지에서 사용할 수 있습니다. 현재 예제는 로그아웃 성공시 /home.do 로 가므로 사용되지 않습니다.

 

 

4. 실행결과 화면 입니다.

 

스프링시큐리티 커스텀 로그인 화면

 

※ 일반적인 내장 표현식을 정리하였습니다.

표현식 설명
hasRole([role]) 현재 로그인된 사용자가 지정된 role을 가지고 있으면 true를 반환합니다. 제공된 role이 'ROLE_'로 시작하지 않으면 기본적으로 'ROLE_'를 추가합니다. 이것은 DefaultWebSecurityExpressionHandler에서 defaultRolePrefix를 수정하여 커스터마이즈할 수 있습니다.
hasAnyRole([role1,role2]) 현재 로그인된 사용자가 콤마(,)로 분리하여 주어진 role들 중 하나라도 가지고 있으면 true를 반환합니다. 제공된 role이 'ROLE_'로 시작하지 않으면 기본적으로 'ROLE_'를 추가합니다. 이것은 DefaultWebSecurityExpressionHandler에서 defaultRolePrefix를 수정하여 커스터마이즈할 수 있습니다.
hasAuthority([authority]) 현재 로그인된 사용자가 지정된 권한이 있으면 true를 반환합니다.
hasAnyAuthority([authority1,authority2]) 현재 로그인된 사용자가 콤마(,)로 분리하여 주어진 권한들중 하나라도 가지고 있으면 true를 반환합니다.
principal 현재 사용자를 나타내는 principal 객체에 직접 접근할 수 있습니다.
authentication SecurityContext로 부터 얻은 Authentication 객체에 직접 접근할 수 있습니다.
permitAll 항상 true로 평가 됩니다.
denyAll 항상 false로 평가 됩니다.
isAnonymous() 현재 사용자가 익명사용자(로그인 안됨) 사용자이면 true를 반환합니다.
isRememberMe() 현재 로그인된 사용자가 remember-me 사용자이면 true를 반환합니다.(로그인 정보 기억 기능에 의한 사용자)
isAuthenticated() 현재 사용자가 로그인된 사용자라면 true를 반환합니다.
isFullyAuthenticated() 로그인 정보 기억(remember-me)이 아니라 아이디/비밀번호를 입력하여 로그인 했다면 true를 반환합니다.
hasPermission(Object target, Object permission) 사용자가 주어진 권한으로 제공된 대상에 액세스 할 수 있으면 true 를 반환합니다. 예, hasPermission(domainObject, 'read')
hasPermission(Object targetId, String targetType, Object permission) 사용자가 주어진 권한으로 제공된 대상에 액세스 할 수 있으면 true 를 반환합니다. 예, hasPermission(1, 'com.example.domain.Message', 'read')

 

※ 전체소스

spring_security(2).zip
다운로드

 

반응형