Spring Security 3.2.5 Release 적용 후기

2014. 9. 11. 18:27Java/Spring Framework

프로젝트를 시작할 때 마다 전에 누락된 부분을 채워 넣고 싶은 맘이 간절해서, 간략하게 스터디 해서 정리 해봅니다.

이번에는 Spring Security 를 적용해 보려고 합니다.



Spring Security 에 대한 구성이라든지, History, Spring Security 가 무엇인가? 를 정리하기엔.. 좀 무리가..

Spring Security란 다시 말하자면 스프링 기반의 어플 들의 인증, 권한(접근 컨트롤)등의 보안을 위한 사실상의 표준입니다.

라고 간략히 해석이 가능 하겠습니다.


Reference 는 아래 링크를 참고 하시면 됩니다. (사실 아래 레퍼런스를 기준으로 Getting Started 부분 중에 Java Configuration을 제외하고 Security Namespaces Configuration 파트를 번역을 해 놓은 것이라고 보시면 됩니다.)
Spring Security Reference (Version 3.2.5 RELEASE )
http://docs.spring.io/spring-security/site/docs/3.2.5.RELEASE/reference/htmlsingle/ 

자 그렇다면 아래 내용의 서술을 처음 적용하던지, 아니면 기존 Spring Framework 하에 있는 프로젝트 상에 추가반영 여부에 상관없이.. 제 맘대로 정리 하겠습니다.


1. Maven 

Spring Security를 적용하기 위한 jar 파일... 뭐 라이브러리 파일을 우선적으로 가져와야 합니다.
pom.xml 상에 적용하는 거죠...
샘플로....전 이넘의 Spring Security를  Web 환경하에 반영을 할것이고, 또한 Class 상에 설정을 Annotation를 사용하여 하지 않고, xml namespace로 설정을 하려고 하기에, 아래 Spring-security-web 과 Spring-security-config 라는 두 가지 항목이 Spring Security 를 함에 있어서 최소 사양 이라고 보시면 됩니다.
아래 영문으로 아주 자세한 설명이 되어 있으니... 해석 바랍니다.

1.Spring-security-web.jar

: Contains filters and related web-security infrastructure code. Anything with a servlet API dependency. You’ll need it if you require Spring Security web authentication services and URL-based access-control. The main package is org.springframework.security.web


2.Spring-security-config.jar

: Contains the security namespace parsing code. You need it if you are using the Spring Security XML namespace for configuration. The main package is org.springframework.security.config. None of the classes are intended for direct use in an application.


<dependencies>

 <!-------------------------other dependency elements-------------------------------->

    <dependency>

        <groupId>org.springframework.security</groupId>

        <artifactId>spring-security-web</artifactId>

        <version>3.2.5.RELEASE</version>

    </dependency>

  <dependency>

    <groupId>org.springframework.security</groupId>

    <artifactId>spring-security-config</artifactId>

    <version>3.2.5.RELEASE</version>

  </dependency>

</dependencies>

저는 3.2.8 RELEASE 의 Spring Framework 를 사용할 예정입니다. 현 시점에서 가장 최근이니까요 :D
혹 4.0.2 RELEASE 이상 버전의 Spring Framework 시에는 별도로 진행시 설정해 줘야 하는 <dependencymanagement> 에 대한 부분도 생각 합니다만, 여기서는 상세 설명은 생략합니다. 
(Referenece 에 가면 잘 나와 있습니다.)


2. Configuration(Spring-Security 설정)

Namespace  Configuration(using XML)방식과 Java Configuration (using Annotation) 방법이 있는데, 
둘 다 적용하는 것은 동일하나,  직관적으로 보기 편한  XML 파일 설정으로 진행 합니다.
기 설명 드린 내용이지만, 첫 번째로는 spring-security-config.jar 가 있어야 한다.(물론 Maven 설정에서 받아 왔지요)


자!!! 그럼 
Spring-security.xml 로 따로 파일을 만든 후, 아래와 같이 정의를 하는데...
그리고 아래와 같이 schema 선언 에 Spring-security와 관련된 해당 사항을 추가해야 합니다.
그럼 첫번째와 두번째의 정의 차이는 무엇일까요?
.................

<beans:beans xmlns="http://www.springframework.org/schema/beans"

  xmlns:security="http://www.springframework.org/schema/security"

  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-3.0.xsd

  http://www.springframework.org/schema/security

  http://www.springframework.org/schema/security/spring-security-3.2.xsd">

</beans:beans>


<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-3.0.xsd

  http://www.springframework.org/schema/security

  http://www.springframework.org/schema/security/spring-security-3.2.xsd">

</beans:beans>



기본 네임스페이스를 beans에서 security 로 한다는 뜻입니다.
잘 모르겠다면... 아래 Configuration 상세 부분에서 보시면 좀 더 명확할것입니다. 


3. Filter 추가

Web.xml 에 필터 선언을 해주시길 바랍니다.

이건 Spring Security web infrastructure 안에서 지정된 범위에 대해 hook을 제공합니다.
hook 이란 말... 모든 Request 에 대한 부분에 Filter를 걸쳐서 걸러준다는 뜻 아닐까요? Interceptor 와 비슷한??


<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>

자 이러면 끝!!!



4. Configuration 상세(Spring-Security.xml)


1. <HTTP> 의 최소 구성

네임스페이스 단에 바로 테스트 데이터를 입력하는 방법이 되겠습니다.

<beans:beans></beans:beans>사이에 추가하시면 됩니다...
여기서 좀 신기하다고 생각한 건 로그인 페이지를 구성하지도 않았지만, 로그인 페이지가 생겨져 있다는 점…
단어에서 바로 나오듯이 http 상에 모든 url에 pattern (정규식)과 포함되는 것에는 ROLE_USER 라는 접근 권한이 있고, 이걸 모두 인터셉터 해서 form-login 으로 이동시켜 준다는 이야기 인데... 로그인 form 페이지는 
자동으로 생성해 줍니다.
(예~~)

 <http>

  <intercept-url pattern="/**" access="ROLE_USER" />

  <form-login />

  <logout />

</http>


<authentication-manager>

  <authentication-provider>

    <user-service>

      <user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />

      <user name="bob" password="bobspassword" authorities="ROLE_USER" />

    </user-service>

  </authentication-provider>

</authentication-manager>


2. Form 과 기본 로그인 옵션

하지만 기존 제공하는 페이지는 구립니다... 사용자들이 일반적으로 로그인 페이지는 커스터마이징 할것이고,
그래서 직접 만들어 놓은 login.jsp 로 이동 시키는 명령은 아래와 같습니다.
또한 intercept-url 요소는 한 개 이상 등록이 가능해서,
아래는 익명의 사용자에게는 login.jsp 페이지에 접근이 가능하도록 한것 이죠.
(로그인 페이지가 권한에 막혀서 로그인을 못하면.. 어쩌라구..)
암튼 그리 해야 로그인을 할 수 있고, 이 이상으로 각각의 역할에 맞게 인증처리 하도록 할 수 있는 것이죠.

<http>

  <intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>

  <intercept-url pattern="/**" access="ROLE_USER" />

  <form-login login-page='/login.jsp'/>

</http>


http 요소 또한 Spring Security 3.1 버전 이상부터 한 개 이상 사용이 가능하고,security에 대한 bypass 가 가능한 설정을 아래와 같이 진행 할 수 있다. (단, pattern 을 생략하면 모든 요청에 다 적용 된다는 점!)

<http pattern="/css/**" security="none"/>

<http pattern="/login.jsp*" security="none"/>

<http>

  <intercept-url pattern="/**" access="ROLE_USER" />

  <form-login login-page='/login.jsp'/>

</http>


마지막 예제는 Form login 대신에  기본 인증시 사용하는 http-basic 의 예제 이다.
http-basic는 별도로 http 에 대한 공부를 하면서 반영해 보시길..
아직까지 내가 목표로 한 DB 연동 부분이 안나와서... 계속 진행 합니다. 

<http>

  <intercept-url pattern="/**" access="ROLE_USER" />

  <http-basic />

</http>


3. 로그인 후 이동하는 페이지 설정

로그인 처리 후에 이동하는 페이지 설정은 always-use-default-target 에 true 하시고 default-target-url 이라는 속성에 기재 하면 됩니다.

<http pattern="/login.htm*" security="none"/>

<http>

  <intercept-url pattern='/**' access='ROLE_USER' />

  <form-login login-page='/login.htm' default-target-url='/home.htm'

          always-use-default-target='true' />

</http>


4. 로그아웃 다루기

기본 로그아웃 URL 은  /j_sprint_security_logout  이고 , logout-url  속성에 직접 URL을 지정 하는 방법이 있습니다.

예제는 엄써요...


5. 다른 권한 제공자 사용하기

드뎌... DB을 이용해서 사용자 정보를 불러오는 형태로 하고 싶다면, 아래와 같이 진행하면 되겠습니다.
“securityDataSource” 는 DataSource bean의 이름이고, 
표준 Spring Security user data tables를  가리키고 있습니다.

물론 위에 http 태그에 대한 설정은 원하는 대로 하시구요... 

<authentication-manager>

  <authentication-provider>

    <jdbc-user-service data-source-ref="securityDataSource"/>

  </authentication-provider>

</authentication-manager>


두번째 예제는 jdbcDaoImpI 상에서의 dataSource 를 참조해서 활용하는 방법입니다.

<authentication-manager>

  <authentication-provider user-service-ref='myUserDetailsService'/>

</authentication-manager>

<beans:bean id="myUserDetailsService"

    class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">

  <beans:property name="dataSource" ref="dataSource"/>

</beans:bean>


마지막은 표준 AuthenticationProvider 를 상속 받은 것을 활용해서 사용 하는 방법이고,  Authentication-provider 요소는 한 개 이상 사용 가능합니다.


 <authentication-manager>

    <authentication-provider ref='myAuthenticationProvider'/>

  </authentication-manager>


6. 패스워드 암호화 하기

패스워드는 보안 해쉬 알고리즘을 사용하여 암호화 되어져야 하죠.
여기선 <password-encoder> 요소 에 의해 지원됩니다.(SHA 나 MD5 같은 표준 알고리즘 아님)
Bcrypt 라는 것으로 통해 암호화 되어지고 , 아래는 일반 인증 제공 설정에 대해서 보여줍니다. (Bcrypt 가 좋데요…;;;)

<beans:bean name="bcryptEncoder"

    class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

<authentication-manager>

  <authentication-provider>

    <password-encoder ref="bcryptEncoder"/>

    <user-service>

      <user name="jimi" password="d7e6351eaa13189a5a3641bab846c8e8c69ba39f"

            authorities="ROLE_USER, ROLE_ADMIN" />

      <user name="bob" password="4e7421b1b8765d8f9406d87e7cc6aa784c4ab97f"

            authorities="ROLE_USER" />

    </user-service>

  </authentication-provider>

</authentication-manager>


7. 좀더 있어 보이는 기능 

(1) Remember-Me authentication

이 부분은 별도 섹션으로 구성되어 있어서, 그 부분에 대한 번역을 완료하면 정리 하겠습니다.


(2) Adding Http/HTTPS Channel Security

Intercept-url  요소 내에 requires-channel 속성을 사용하면 됩니다.
사용 가능한 옵션은 HTTP,HTTPS,ANY(HTTP와 HTTPS 둘 중 어떤것이 든지..) 

<http>

  <intercept-url pattern="/secure/**" access="ROLE_USER" requires-channel="https"/>

  <intercept-url pattern="/**" access="ROLE_USER" requires-channel="any"/>

  ...

</http>


그리고 마지막으로 표준 포트를 사용하지 않을 경우에 아래와 같이 지정해 줄 수 있다.

<http>

  ...

  <port-mappings>

    <port-mapping http="9080" https="9443"/>

  </port-mappings>

</http>


(3) Session 관리 - Timeouts 탐지

session-management 요소를 통해 특정  URL 로 redirect 시키거나,
엉뚱한 세션 아이디를 가진 분들을 탐지해주는 설정 요소 이다.

<http>

  ...

  <session-management invalid-session-url="/invalidSession.htm" />

</http>


정상적으로 로그아웃이 안되는 경우가 발생하는데, 
JESSIONID 쿠키를 로그아웃시 삭제 하는 구문을 넣으면 좀더 명확해진다는 내용이다.
(하지만 잘 모든 servlet container에서 장담 할 수 없다고 하니, 본인 환경에서 다시 테스트를 해보라구 하네요)

<http>

  <logout delete-cookies="JSESSIONID" />

</http>


(4) Session 관리 - 동시다발적인 세션 컨트롤

오 홋, 중복 로그인을 막아주는 방법은 아래와 같이  web.xml 에 Listener를 추가하면 됩니다.

(요 Listener 가 업데이트된 session 라이프 사이클에 대해서 Spring Security를 유지 킨다고 합니다. )

<listener>

  <listener-class>

    org.springframework.security.web.session.HttpSessionEventPublisher

  </listener-class>

</listener>


<http>

  ...

  <session-management>

     <concurrency-control max-sessions="1" />

  </session-management>

</http>

두 번째 로그인 한 사용자를 막는 방법은 아래와 같다.

이렇게 되면 두 번째 로그인 한 사용자는 리젝 당한다.
리젝 이란 form기반 로그인을 사용하고 있는 중이라면, authentication-failure-url 을 보낼 것이란 뜻이다.
Session-management 요소 내에 Session-authentication-error-url 를 활용하면 된다.
또 session-fixation-protextion 속성을 활용해서 세션 고정 공격을 방어 할 수 있다.

속성은  
 - none : 아무것도 안함.
 - newsession :  존재하는 세션 데이터의 복사하는 것 없이 새로운 Session을 만든다.  
  - migratesession : 존재하는 모든 세션 데이터를 복사해서, 새롭게 만든 session에 복사한다.
 - changeSessionID  : 새로운 세션을 만들지 않는다.
                                  서블릿 컨테이너에 의해서 만들어진 세션 고정 방어를 사용한다.
                                 (
Servlet 3.1이상에서만 가능하지요.)

<http>

  ...

  <session-management>

     <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />

  </session-management>

</http>


우선 여기까지 정리를 해 본다....