본문으로 바로가기

웹 시큐리티(Web Security)-2Day

category 웹/Spring 2019. 5. 26. 14:56

깃허브:https://github.com/leejeongchan/springSecurity.git

설명하기 앞서 웹 세큐리티 설정을 모르시는 분은 아래를 타고 다시 보시기 바랍니다.

 

2019/05/25 - [웹/Spring] - 웹 시큐리티(Web Security)-1Day

 

웹 시큐리티(Web Security)-1Day

스프링 웹 시큐리티 동작 방식은 필터와 인터셉터를 이용합니다. 인터셉터는 스프링에서 필터와 유사합니다. 두 공통점은 특정 서블릿이나 특정 컨트롤러의 접근에 관여하지만 차이점으로 필터는 스프링과 무관하..

dlwjdcks5343.tistory.com

로그인과 로그아웃 처리

 

우선 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:form-login/>
	</security:http>
	
	<security:authentication-manager>
	
	</security:authentication-manager>
</beans>

intercept-url을 이용해서 접근 제한을 설정합니다.

 

pattern 속성과 access 속성을 지정해줘야 합니다. 

 

/sample/member URI는 ROLE_MEMBER라는 권한이 있는 사용자만 접근이 가능하다는 것을 뜻합니다.

 

access는 표현식과 문자열 두 방법이 있는데 만약에 문자열만 이용하고 싶으면 use expressions="false"를 이용합니다.

 

이렇게 설정 시 /sample/all 경로 시 아래처럼 뜨지만

/sample/all

만약 /sample/member를 치게되면 스프링 세큐리티 기본 설정에 의해서 로그인 화면으로 이동합니다.

 

신기하게도 로그인 화면을 만들지도 않았는데 말이죠

 

/sample/member

스프링 세큐리티에서는 User와 username의 의미가 아래와 같습니다.

 

username: 아이디와 같다.
User: 인증 정보와 권한을 가진 객체

실제 인증 처리 권한 처리는 UserDetailsService가 처리를 진행하는데 이를 등록해줍시다.

 

위 security-context.xml에 이어서 작성합니다.

 

	<security:authentication-manager>
		<security:authentication-provider>
			<security:user-service>
				<security:user name="member" password="member" authorities="ROLE_MEMBER"/>
			</security:user-service>
		</security:authentication-provider>
	</security:authentication-manager>

위 설정은 저번 게시글에 그림과 비슷합니다.

 

member라는 정보를 추가한 후 로그인이 가능하게 하도록 한 것입니다.

 

그러나 위와 같은 에러가 발생하게 됩니다. 

 

왜냐하면 PasswordEncoder가 없기 때문입니다. 

 

만일 인코딩 없이 처리하고 싶으면 패스워드 앞에 {noop}을 작성합니다.

 

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

그럼 위처럼 로그인이 가능하게 됩니다.

 

이제 로그아웃을 처리합시다.

 

로그아웃은 브라우저에서 세션과 관련 정보를 삭제합니다. F12를 눌러서 Application을 보면 Cookies에 JSESSIONID로

 

유지하는 것을 볼 수 있습니다. 이는 톰캣 자체적으로 발생하는 쿠키 이름입니다.

 

 

로그아웃은 바로 이 JSESSIONID 쿠키를 삭제하는 겁니다.

 

그럼 바로 로그인 화면으로 이동하는 것을 볼 수 있습니다.

 

웹을 이용해 로그아웃을 하는 것은 조금 더 뒤에서 보도록 하겠습니다.

 

우선 sample/all과 sample/member는 처리가 됐고 남은 sample/admin이 있습니다.

 

이는 관리자 권한으로 member에 부여했던 것과 ROLE_ADMIN을 추가해줍니다.

 

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

 

이렇게 되면 ADMIN은 /sample/admin 과 /sample/member 모두에 접근 가능합니다.

 

admin으로 로그인 하면 member로 이동하는 것을 볼 수 있습니다. 

 

이제 접근 제한 메시지를 처리합시다.

 

특정 사용자가 로그인 후 접근 못하도록 하는 상황이 발생할 수 있습니다.

 

여기선 member가 /sample/admin을 접근하지 못하도록 막습니다.

 

위와 같은 에러 메시지를 출력합니다. 

 

이 접근 제한을 AccessDeniedHanlder에서 구현하거나 특정 URI를 지정합니다.

 

특정 URI

<security:access-denied-handler error-page="/accessError"/>

를 추가해줍니다.

 

/accessError를 처리하도록  CommonController를 작성합니다.

 

package org.zerock.controller;

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

import lombok.extern.log4j.Log4j;

@Controller  
@Log4j
public class CommonController {
	
	@GetMapping("/accessError")
	public void accessDenied(Authentication auth,Model model) {
		log.info("access Denied: "+auth);
		
		model.addAttribute("msg","Access Denied");
	}
}

Get방식으로 /accessError로 Authentication 타입 파라미터를 지정하고 Model에 에러메세지를 추가합니다.

 

그 다음 accessError.jsp를 작성해줍니다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="sec" %>
<%@ page import="java.util.*" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Access Denied page</h1>
<h2><c:out value="${SPRING_SECURITY_403_EXCEPTION.getMessage()}"/></h2>
<h2><c:out value="${msg}"></c:out></h2>
</body>
</html>

만약에 admin에 member가 접근하려 한다면 아래와 같이 출력이 됩니다.

 

만약 다양하게 처리하고 싶다면 직접 AccessDeniedHanlder에서 구현 하도록 합니다.

 

예를 들어서 접근 제한 될 경우 세션, 쿠키에 특정 작업, 헤더 정보 추가 등을 말합니다.

AccessDeniedHanlder에서 구현

우선 CustomeAccessDeniedHandler 클래스를 추가합니다.

 

그 다음 AccessDeniedHanlder 인터페이스를 구현해줍니다.

 

package org.zerock.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;

import lombok.extern.log4j.Log4j;


@Log4j
public class CustomeAccessDeniedHandler implements AccessDeniedHandler{
	
	@Override
	public void handle(HttpServletRequest request, HttpServletResponse response,
			AccessDeniedException accessDeniedException) throws IOException, ServletException {
		// TODO Auto-generated method stub
		
		log.error("Access Denied Handler");
		log.error("Redirect.....");
		
		response.sendRedirect("/accessError");
		
	}

}

구현 하고 리다이렉트로 /accessError를 해줍니다. 서블릿 API를 이용하는 처리가 가능합니다.

 

이를 이용하기 위해 위에서와는 달리 security-context.xml에서 

 

다음과 같이 작성을 해줍니다.( CustomeAccessDeniedHanlder를 bean으로 등록)

 

<bean id="customAccessDenied" class="org.zerock.security.CustomAccessDeniedHandler">
</bean>

을 <security:http> 위에 작성해주고 access-denied-hanlder에서 error-page가 아닌 ref로 바꿔줍니다.

 

둘중 하나를 작성합니다.

<!-- <security:access-denied-handler error-page="/accessError"/> -->
<security:access-denied-handler ref="customAccessDenied"/>

이렇게 되면 접근 제한시 리다이렉트로 동작을 하게 됩니다.

 

 

' > Spring' 카테고리의 다른 글

웹 시큐리티(Web Security)-4Day[CSRF]  (0) 2019.05.28
웹 시큐리티(Web Security)-3Day  (0) 2019.05.27
웹 시큐리티(Web Security)-1Day  (0) 2019.05.25
Spring 트랜잭션 설정  (0) 2019.05.06
Spring 트랜잭션 & 데이터베이스 설계  (0) 2019.05.06