Spring Security | Spring Security이란? | Java Configuration


Spring Security 3.2에서는 Spring 3.1에 추가된 Java Configuration 사용할 수 있도록 되어있다. Java Configuration을 사용하면 XML 파일 없이도 namespace과 같은 설정을 작성할 수 있다.

Java Configuration을 사용하면 어느 정도의 설정 오류는 컴파일러가 체크 해주고, 리팩토링이 용이해진다는 장점이 있다.

Hello World를 Java Configuration에서 대체하기

.
├── build.gradle
└── src
    └── main
        ├── java
        │   └── sample
        │       └── spring
        │           └── security
        │               └── MySpringSecurityConfig.java
        └── webapp
            ├── WEB-INF
            │   └── web.xml
            └── index.jsp

컨테이너의 구현

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

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

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

    <context-param>
        <param-name>contextClass</param-name>
        <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
    </context-param>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>sample.spring.security.MySpringSecurityConfig</param-value>
    </context-param>
</web-app>

기본적으로 사용된 XmlWebApplicationContext 대신에 AnnotationConfigWebApplicationContext를 사용하도록 설정을 변경한다.

contextClass라는 이름으로 <context-param>을 정의하면, ContextLoaderListener는 거기에서 지정한 클래스를 컨테이너로 이용하게 된다.

또한 AnnotationConfigWebApplicationContext가 로드하는 설정 클래스는 contextConfigLocation이라는 이름으로 <context-param>을 정의하여 지정할 수 있다.

src/main/java/sample/spring/security/MySpringSecurityConfig.java

package sample.spring.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin();
    }

    @Autowired
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("devkuma").password("1234").roles("USER");
    }
}

Spring Security를 활성화하기

Spring Security의 Java Configuration을 사용하려면 AnnotationConfigWebApplicationContext에서 로드한 클래스를 @EnableWebSecurity으로 어노테이션을 지정한다.

왜 이것으로 Spring Security가 활성화되는지는 이 어노테이션의 구현을 보면 알 수있다. 구현된 내용은 아래와 같다.

EnableWebSecurity.java

...
package org.springframework.security.config.annotation.web.configuration;

...

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;

...
@Import({ WebSecurityConfiguration.class,
        SpringWebMvcImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
...
}

@Import를 사용하여 WebSecurityConfiguration가 로드되고 있다. 이 클래스도 @Configuration 어노테이션이 부여된 설정 클래스가 있다.

Spring Security의 설정

Spring Security의 구체적인 설정을 해가지 위해, 먼저 WebSecurityConfigurerAdapter라는 클래스를 상속한다.

WebSecurityConfigurerAdapter는 디폴트로 간단한 SecurityFilterChain을 구축하게 된다. 이 클래스를 상속하고 configure (HttpSecurity) 등의 메소드를 오버라이드하여 구축되는 SecurityFilterChain을 임의로 정의할 수 있다.

인수 받을 HttpSecurity가 namespace에서의 <http> 태그에 대응된다. 기본적으로 <http>에 있는 설정은 HttpSecurity으로 가능하게 되어 있다.

위의 구현은 다음 XML과 같은 의미가 된다.
(※ 로그 아웃은 WebSecurityConfigurerAdapter가 내부에서 자동으로 등록하고 있기 때문에 생략하고 있다.)

applicaitonContext.xml

    <sec:http>
        <sec:intercept-url pattern="/login" access="permitAll" />
        <sec:intercept-url pattern="/**" access="isAuthenticated()" />
        <sec:form-login />
        <sec:logout />
    </sec:http>

authorizeRequests() 메소드는 namespace에서의 <intercept-url>의 설정을 시작하는 메소드가 된다. 메소드의 반환 값의 형태가 ExpressionInterceptUrlRegistry라는 클래스가 되고, 전용 설정 메소드가 부를 수 있게 되어 있다.

<intercept-url>의 설정을 종료시켜 다른 설정을 계속하려면, and() 메소드를 이용한다. and() 메소드는 HttpSecurity를 반환하므로, 메소드 체인을 중단시키지 않으면서 다음 설정을 계속할 수 있다.

formLogin()는 이름 그대로 Form 인증으로 사용한다.

사용자 정보 설정

AuthenticationManagerBuilder는 AuthenticationManager의 클래스를 정의하기 위한 지원 클래스로 위에 구현처럼 메소드 체인을 사용하여 UserDetailsService 등을 정의할 수 있다.

혹은 AuthenticationManagerBuilder를 사용하지 않고 직접 Bean을 정의하는 메소드를 선언할 수도 있다.

Bean정의로 설정을 하는 경우

package sample.spring.security;

import org.springframework.context.annotation.Bean;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
...

@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {

    ...

    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("hoge").password("HOGE").roles("USER").build());
        return manager;
    }
}