본문 바로가기

프로그래밍/Spring Security

흔히 보게되는 절대 써먹을 수 없는 Spring Security의 초간단 셋팅

저번엔 Spring Security를 이해하는데 필수적인 요소라 할수 있는 인증, 권한, Filter 클래스들에 대해 알아보았다. 이번엔 본격적으로 우리 프로젝트에 Spring Security를 적용해보자(지금부터 설명하는 내용은 Java 6, Spring Framework 3.2.9, Spring Security 3.2.4를 적용했다)

프로젝트는 일반적인 Maven 구조의 Web 프로젝트이다(이 프로젝트의 구조가 이해가지 않으신 분들은 STS(Spring Tool Suite)를 개발툴로 삼아서 진행하면 된다. 이 툴에서 만드는 프로젝트 구조가 Maven Web Project이다). Maven Web Project가 아닌 이클립스에서 Dynamic Web Project로 만들어서 진행해도 상관은 없다. 설정에 대한 핵심만 알아두면 된다.

 

제일 먼저 web.xml에 다음의 내용을 넣어두도록 하자.

 

<filter>
	<filter-name>springSecurityFilterChain</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
	<filter-name>springSecurityFilterChain</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

 

여기서 주의해야 할 셋팅은 filter-name 태그에 준 springSecurityFilterChain 이 부분이다. 반드시 이 이름으로 주어야 한다. Spring Security 내부에서 이 이름(springSecurityFilterChain)으로 DelegatingFilterProxy 클래스를 찾기 때문에 다른 이름을 줄 경우 적용할 수가 없다. 그리고 해당 필터가 적용될 URL 패턴을 모든 패턴을 의미하는 /*를 주도록 한다.

 

이 부분에 대해 설명할 내용이 있다. 흔히 Spring 으로 Web 개발을 할때(아마 Spring MVC를 이용해서 많이들 할 것이다) 웹페이지의 확장자로 .do나 기타 html이나 jsp가 아닌 다른 단어를 사용하는 확장자를 이용할 것이다. 이것을 생각해서 어차피 여기 패턴에 적용된 애들만 Spring Security를 적용할려고 하는 의미에서 /*가 아닌 *.do를 하는 경우가 있을수 있다. 나도 첨엔 그러했다. 그러나 이렇게 하면 안된다. Spring Security에서는 내부적으로 작업을 위해 사용되는 URL들이 있는데 이 URL들이 뒤에 확장자가 붙는 스타일이 아니다(예를 들어 로그인 아이디와 패스워드를 입력받아 인증 관련 작업을 하는 URL이 j_spring_security_check이다) 그래서 확장자로 패턴을 줄 경우 로그인을 하지 못하는 상황이 발생한다.

 

그러면 이런 질문을 할 수도 있을 것이다. 만약 전체 URL 패턴으로 줄 경우 이미지 파일이나 css 파일 같이 Spring Security가 적용되어야 할 필요가 없는 것들도 있는데 이런것까지도 Spring Security가 관여하게 되는 것 아니냐..할 수 있다. 그러나 Spring Security는 Spring Security가 관여하지 않는 url 패턴을 따로 지정할 수 있다. 이런 URL 패턴 지정시에 이미지 파일들이 들어 있는 디렉토리나 css 파일이 들어있는 디렉토리를 지정해주면 된다. 이 내용에 대해선 차후에 다시 설명하겠다

 

이렇게 web.xml에서 설정이 끝나면 우리가 web.xml에 흔히 셋팅하게 되는 Root Context 설정시에 Spring  Security 설정 내용이 들어있는 XML을 넣어주면 된다.

 

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>
		/WEB-INF/spring/root-context.xml
		/WEB-INF/spring/spring-security.xml
	</param-value>
</context-param>

<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

 

그위의 셋팅 내용을 보면 Spring Framework의 Root Context 설정 내용이 있는 파일인 root-context.xml 파일과 Spring Security 설정 내용이 있는 파일인 spring-security.xml 파일이 있는 것을 볼수 있다. 이 2개의 파일의 설정 내용이 Root Context로 올라가게 된다.

 

그럼 이제 Spring Security의 아주아주 기본적인 셋팅을 보도록 하겠다. 이 글을 보기 전에도 아마 많은 곳에서 이런 스타일의 기본 설정은 보아왔을 것이다.

 

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

  <!-- 확장자를 이용해 패턴을 걸때는 /**/*.해당 확장자 로 할 것(Ant Pattern 규칙) --> 
  <http pattern="/**/*.js" security="none"/>
  <http pattern="/**/*.css" security="none"/>
  <http pattern="/images/*" security="none"/>
  <http auto-config="true">
    <intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
    <intercept-url pattern="/**" access="ROLE_USER"/>
  </http>
  <authentication-manager>
    <authentication-provider>
      <user-service>
        <user name="user1" password="user1password" authorities="ROLE_USER"/>
        <user name="admin1" password="admin1password" authorities="ROLE_ADMIN"/>
      </user-service>
    </authentication-provider>
  </authentication-manager>
</beans:beans>

 

이런 형식의 아주아주 간단 설정 파일은 많이 봐 왔을 것이다. 구글링을 통해 많이 접해봐서 내용을 아는 사람도 있겠지만..다시 복습 차원에서 설명을 덧붙여 보도록 하겠다.

 

<http pattern="/**/*.js" security="none" /> 의 의미는 확장자가 js인 것은 spring security를 적용하지 않는다는 의미이다. 예전 버전(구체적인 버전은 생각이 안나네요..ㅠㅠ.)의 Spring Security는 <http> 태그를 하나만 쓸수 밖에 없어서 이렇게 Spring Security를 적용하지 말아야 할 URL 패턴 등록을 하기 곤란 했었는데..버전업이 되면서 <http> 태그를 여러개 사용이 가능하도록 바뀌었다. pattern 속성에는 URL 패턴을 입력하면 되는데 주석에 설명했다시피 ANT 스타일의 패턴 규칙을 사용하면 된다. 위의 설정 내용을 보면 확장자가 js와 css 인 것과 image 폴더 밑의 여러 단계에 걸친 서브 폴더와 파일들은 Spring Security의 적용을 받지 않도록 했다

 

Spring Security의 핵심적인 셋팅은 <http> 태그에서 많이 이루어진다고 봐도 과언이 아니다. Spring Security의 가이드 문서 부록에 있는 The Security Namespace에서 <http> 태그와 관련된 내용을 읽어보면 이 태그의 속성과 하위 태그들에 대한 설명들이 있다. 관련 설명들을 읽어보면 인증 부분을 제외한 나머지 부분을 이 <http> 태그와 그 하위 태그에서 이루어지는 것을 확인할 수 있다

 

auto-config 속성은 자동 설정을 하겠냐는 의미이다. 이것을 적용할 경우 form 인증과 클라이언트 basic 인증, 그리고 logout에 대한 기본적인 셋팅이 자동으로 설정된다. 태그로 설명하자면 다음의 태그가 자동으로 셋팅되는 것을 의미한다

 

<http>
  <form-login />
  <http-basic />
  <logout>
</http>

 

이러한 태그들이 자동으로 설정이 되는 것이지만 실제로는 자동 설정을 하질 않는다. 다음 편에서도 설명하겠지만 Spring Security가 제공하는 아주아주 기본적인 설정은 우리의 현실에는 맞지 않기 때문이다. 어디까지나 간단 설정을 보여주기 위해 사용한 것이지 이렇게 사용해야 한다는 의미로 쓴 것이 아님을 밝혀둔다(이렇게 써야 한다고 오해하는 사람도 있을것 같아서..)

 

<intercept-url> 태그는 Spring Security가 감시해야 할 URL 과 이 URL이 접근가능한 권한을 정의하는 태그이다. 앞에서 인증과 권한 설명시 권한에 해당하는 작업을 하기 위한 설정인것이다. pattern 속성은 <http> 태그 설명했을때 언급했으니 별도로 언급하지 않겠다. access 속성은 해당 URL 패턴을 이용할 수 있는 권한을 지정하는 부분이다. 위에 설정한 내용을 가지고 종합적으로 설명해보면 URL 패턴이 /admin의 하위 모든 디렉토리와 파일을 지정하는 패턴인 그런 패턴을 만족하는 URL은 ROLE_ADMIN 권한을 가지고 있어야 접근할 수 있다는 것을 의미한다. 

 

<intercept-url> 태그를 지정할때는 주의점이 하나 있다. 바로 설정 순서인데, 구체적인 URL 패턴을 먼저 설정하고 덜 구체적인 패턴을 나중에 설정해야 한다는 것이다. /admin/**과 /**을 비교해보자. /admin/**는 admin 디렉토리 밑을 가리키는 모든 디렉토리와 파일을 의미하는 패턴이다. 그러나 /**는 모든 디렉토리와 파일을 가르키는 패턴이다. 즉 /admin/** 패턴이 /** 보다는 더 구체적인 패턴이다. Spring Security는 먼저 URL이 /admin/** 패턴을 만족하는지 검사하고 그걸 만족하지 않을 경우 /**을 보게 되는것이다. 만약 /**을 먼저 지정해버리면 모든 URL 이 /**을 만족하기 때문에 URL이 admin 디렉토리 하위를 가르키는거라 해도 /**를 먼저 만족시켜버리기 때문에 ROLE_USER 권한을 가지고 있으면 admin 디렉토리와 그 하위를 접근하는 오동작을 하게 되는 것이다.

 

<authentication-manager> 태그는 인증과 관련된 정보를 설정하게 된다. 인증을 할려면 인증에 대한 정보를 제공하는 제공자를 지정해야 한다. 그것이 <authentication-provider> 태그이다. 그리고 <authentication-provider> 태그의 하위 태그인 <user-service> 태그에 사용자 정보를 <user> 태그로 설정하게 된다. <user> 태그 내용을 보면 사용자 이름과 비밀번호, 그리고 권한이 지정되어 있음을 알수 있다.

 

<user> 태그에서 자칫 헷갈릴수 있는 부분이 있는데 바로 이름(name)이다. <user> 태그를 보면 name 속성을 지정하는 부분이 있다. 그러나 이 name 속성에 지정해야 하는 값은 실제로는 사용자의 이름이 아니라 사용자의 로그인 ID(사용자간에 절대로 같은 값을 가질수 없는 고유 이름) 이다. Spring Security에서 회원 아이디를 가져오는 함수명이 getUsername() 으로 되어 있어서 자칫 이 함수가 사용자의 이름을 가져오는 것이 아닌가 하고 착각할수 있다. 사소한 것일수 있지만 오해의 소지가 있을 수 있어서 미리 밝혀둔다

 

그러면 기본적인 Spring MVC 프로젝트를 만든뒤에 웹브라우저에서 해당프로젝트에서 접근 가능한 URL을 입력해보라. 그러면 다음과 같은 아주아주 썰렁한 로그인 화면이 나타난다.

 

 

 

User 항목에는 <user> 태그의 name 속성에 입력한 값을 입력하고 Password 항목에는 <user> 태그의 password 속성에 입력한 값을 입력하면 로그인이 되면서 가고자 했던 URL의 화면을 보여주게 된다. 만약 엉뚱한 값을 입력하면 다음과 같이 인증 실패 화면을 보게 될 것이다.

 

 

 

 

만약 이동하고자 하는 URL이 /admin 밑에 있는 URL을 접근하고자 할때 User 항목과 Password 항목을 <user> 태그의 user1 것으로 입력할 경우엔 접근을 못한다는 의미의 에러페이지를 보게될 것이다(이 부분은 따로 그림을 보여주지 않도록 하겠다. Http 코드의 403 에러 코드가 발생한 내용의 화면을 보여줄 것이다)

 

이렇게 아주 기본적인 Spring Security에 대한 셋팅을 보여주었다. 그러나 지금까지 설명한 내용을 보았을때 과연 이런 설정과 화면으로 프로젝트에 써먹을수 있겠는가? 절대 못써먹는다. 이렇게 써먹었다간 아마 짤릴 각오 해야 할 것이다. 정말 조악하기 그지없기 때문이다. 그렇기 때문에 다음부터는 이런 설정을 어떻게 커스터마이징을 해서 실제 프로젝트에 써먹을지..그런 부분을 고민해보도록 하겠다.