본문 바로가기

spring

[스프링 시큐리티] 로그인과 로그아웃 처리(2)

1. 여러 권한을 가지는 사용자 설정

정상적으로 로그인이 처리되는 것을 확인했다면 '/sample/admin'을 처리하도록 한다. '/sample/admin'은 'ROLE_ADMIN'이라는 권한을 가진 사용자가 접근할 수 있도록 지정하는데 사용자는 'ROLE_ADMIN'이라는 권한을 가진 사용자가 접근할 수 있도록 지정하는데 사용자는 'ROLE_ADMIN'과 'ROLE_MEMBER'라는 2개의 권한을 가지도록 지정한다.

	<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')"/>
	
		<security:form-login/>
	</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}member" authorities="ROLE_MEMBER, ROLE_ADMIN"/>
			
			</security:user-service>
		</security:authentication-provider>
		
	</security:authentication-manager>

새롭게 추가된 <security:intercept-url>은 '/sample/admin'에 대한 접근을 설정한다. <security:user>에 추가된 admin 사용자는 'ROLE_MEMBER'와 'ROLE_ADMIN'이라는 2개의 권한을 가지도록 설정한다. Admin 계정을 가진 사용자는 '/sample/member'와 '/sample/admin'모두에접근할수잇다.

 

2. 접근제한 메시지의 처리

특정한 사용자가 로그인은 했지만, URI를 접근할 수 있는 권한이 없는 상황이 발생할 수도 있다.이 경우에는 접근 제한 에러 메시지를 보게 된다. 예제의 경우 member라는 권한을 가진 사용자는 '/sample/member'에는 접근할 수 있지만, '/sample/admin'은 접근할 수 없다.

스프링 시큐리티에서는 접근 제한에 대해서 AccessDeniedHandler를 직접 구현하거나 특정한 URI를 지정할 수 있다.

	<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')"/>
	
		<security:form-login/>
		
		<security:access-denied-handler error-page="/accessError"/>
		
	</security:http>

<security:access-denied-hander>는 org.springframework.security.web.access.AccessDeniedHandler 인터페이스의 구현체를 지정하거나 error-page를 지정할 수 있다. 위의 경우 '/accessError'라는 URI로 접근 제한 시 보이는 화면을 처리한다.

com.grindman.web.controller에 CommonController 클래스를 생성해서 '/accessError'를 처리하도록 지정한다.

 

CommonController.java

package com.grindman.web.controller;

import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class CommonController {

	@GetMapping("/accessError")
	public void accessDenied(Authentication auth, Model model) {
		
		System.out.println("access Denied : " + auth);
		
		model.addAttribute("msg", "AccessDenied!");
	}
}

CommonController에서는 간단히 사용자가 알아볼 수 있는 에러 메시지만을 Model에 추가한다. '/accessError'는 Authentication 타입의 파라미터를 받도록 설계해서 필요한 경우에 사용자의 정보를 확인할 수 있도록 한다. view 폴더에는 'accessError.jsp' 파일을 생성한다.

 

3. AccessDeniedHandler 인터페이스를 구현하는 경우

<security:access-denied-handler error-page="/accessError" />와 같이 error-page만을 제공하는 경우에는 사용자가 접근했떤 URI 자체의 변화는 없다.

접근이 제한이 된 경우에 다양한 처리를 하고싶다면 직접 AccessDeniedHandler 인터페이스를 구현하는 편이 좋다. 예를 들어 접근 제한이 되었을 때 쿠키나 세션에 특정한 작업을 하거나 HttpServletResponse에 특정한 헤더 정보를 추가하는 등의 행위를 할 경우에는 직접 구현하는 방식이 더 권장된다.

com.grindman.web.security 패키지를 생성하고 CustomAccessDeniedHandler 클래스를 추가하자.

package com.grindman.web.security;

import java.io.IOException;

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

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;

public class CustomAccessDeniedHandler implements AccessDeniedHandler {

	@Override
	public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {

		System.out.println("Access Denied Handler");
		
		System.out.println("Redirect...");
		
		response.sendRedirect("/accessError");
	}

}

 

CustomAccessDeniedHandler  AccessDeniedHandler 인터페이스를 직접 구현한다. 인터페이스의 메서드는 handle() 뿐이기 때문이고 HttpServletRequest, HttpServletResponse를 파라미터로 사용하기 때문에 직접적으로 서블릿 API를 이용하는 처리가 가능하다. 위의 코드에서는 접근 제한에 걸리는 경우 리다이렉트 하는 방식으로 동작하도록 지정되었다.

security-context.xml에서는 error-page 속성 대신에 CustomAccessDeniedHandler를 빈으로 등록해서 사용한다.

 

	<bean id="customAccessDenied" class="com.grindman.web.security.CustomAccessDeniedHandler"></bean>

	<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')"/>
	
		<security:form-login/>
		
		<!-- <security:access-denied-handler error-page="/accessError"/> -->
		<security:access-denied-handler ref="customAccessDenined"/>
		
	</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}member" authorities="ROLE_MEMBER, ROLE_ADMIN"/>
			
			</security:user-service>
		</security:authentication-provider>
		
	</security:authentication-manager>

<security:access-denied-handler>는 error-page 속성과 ref 속성 둘 중 하나만을 사용한다. 위와 동일한 방식으로 '/sample/admin'에 member/member 계정으로 로그인하면 이전과 달리 '/accessError'로 다이렉트 되는 것을 확인할 수 있다.