Clean Code that Works.


스프링 버전 : 4.0.0.RELEASE
스프링 시큐리티 버전 : 3.2.5.RELEASE

아직 root-context를 xml 설정을 사용하고 있어 시큐리티 부터 java config를 사용하기 위해 시작.

스프링 시큐리티 java config 링크

3년전만해도 스프링 시큐리티 문서 진짜 부족한게 많았는데.. 
이렇게 좋아 지다니.. 놀랍구나.

기존 XML 설정 

<?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-3.2.xsd">

    <global-method-security pre-post-annotations="enabled" />
    <http use-expressions="true" authentication-manager-ref="authManager">
        <intercept-url pattern="/index.jsp" access="permitAll" />
        <intercept-url pattern="/" access="permitAll" />
        <intercept-url pattern="/home" access="permitAll" />
        <intercept-url pattern="/favicon.ico" access="permitAll" />
        <intercept-url pattern="/resources/**" access="permitAll" />
        <intercept-url pattern="/publish/**" access="permitAll" />

        <intercept-url pattern="/j_spring_security_logout" access="isAuthenticated()" />

        <intercept-url pattern="/user/updateUser" access="isAuthenticated()" />
        <intercept-url pattern="/user/info" access="isAuthenticated()" />

        <intercept-url pattern="/login/**" access="isAuthenticated()" />

        <intercept-url pattern="/gas/**" access="isAuthenticated()" />
        <intercept-url pattern="/payment/**" access="isAuthenticated()" />
        <intercept-url pattern="/etc/**" access="isAuthenticated()" />
        <intercept-url pattern="/stats/**" access="isAuthenticated()" />

        <intercept-url pattern="/secure/**" access="isAuthenticated()" />
        <intercept-url pattern="/manage/**" access="hasRole('ROLE_ADMIN')" />
        <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
        <intercept-url pattern="/comment/admin/**" access="hasRole('ROLE_ADMIN')" />

        <form-login login-page="/user/loginForm"    authentication-success-handler-ref="loginSuccessHandler" />

        <logout logout-url="/logout" logout-success-url="/home" />
    </http>

    <authentication-manager id="authManager">
        <authentication-provider user-service-ref="userDetailService">
            <password-encoder ref="passwordEncoder" />
        </authentication-provider>
    </authentication-manager>
</beans:beans>

거의 기본 설정이라서 아주 심플하다. 
딱히 특별한것도 없음..;

Java Config로 설정

@Configuration @EnableWebSecurity public class SecurityWebApplicationInitializer extends WebSecurityConfigurerAdapter { @Autowired private UserDetailService userDetailService; @Autowired private LoginSuccessHandler loginSuccessHandler; @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/", "/index.jsp", "/home", "/favicon.ico", "/resources/**", "/publish/**").permitAll() .antMatchers("/secure/**", "/manage/**", "/admin/**", "/comment/admin/**").hasRole("ADMIN") .anyRequest().authenticated() .and() .formLogin() .loginPage("/user/loginForm") .loginProcessingUrl("/j_spring_security_check") .usernameParameter("j_username") .passwordParameter("j_password") .successHandler(loginSuccessHandler) .permitAll() .and() .logout() .logoutUrl("/logout") .logoutSuccessUrl("/home") .and() .csrf().disable() .httpBasic(); } @Override protected UserDetailsService userDetailsService() { return userDetailService; } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }

위에 링크해둔 스프링 시큐리티 java config 문서에도 친절하게 설명이 잘 나와 있다.
문서 보면서 천천히 해보면 더 좋을 듯.

딱히 특별한건 없고 xml 설정에서 사용하던 authentication-manager를 사용하기 위해서 configre(AuthenticationmanagerBuilder auth)를 세팅했다.

.formLogin() 부분에 보면 processingUrl이랑 username, password 파라미터를 세팅하도록 되어 있는데 이게 원래 기본으로 설정되어 있던거였는데 안되더라..

HttpSecurity.java 의 formLogin()을 보면 FormLoginConfigurer를 사용하는데 여기서 사용하는 default 값이 "username", "password" 이 두가지로 세팅되어 있다.
UsernamePasswordAuthenticationFilter.java 에선 여전히 "j_username", "j_password"를 사용하고 있어서 위처럼 값을 세팅해줘야 사용 가능하다.

그리고 .csrf() 이 설정은 CSRF 공격을 막기위해 세팅되어있는데 .disable()을 세팅해 놓지 않으면 해당 작업을 수행하기 위한 파라미터가 없다고 에러가 발생하기 때문에 .disable()을 해주도록 한다.

csrf().disable()을 안해주면 만나는 에러 
HTTP Status 403 - Expected CSRF token not found. Has your session expired?

이렇게 설정을 바꿈으로서 xml 설정보다 코드를 약간 더 줄일 수 있었고, 조금더 읽기 쉬운 코드가 됬다.



Spring-Secury 버전이 3.1.4가 되면서 부터 org.springframework.security.authentication.encoding.PasswordEncoder@Deprecate 됬다.
이를 교체하기 위해서 org.springframework.security.cryptho.password.PasswordEncoder 사용을 유도하고 있다.

기존 PasswordEncoder에서는 별도의 SaltSource를 만들어서 사용하도록 하고 있었는데, 신규 PasswordEncoder에서는 이를 구현한 클래스(BCryptPasswordEncoder) 같은 친구들이 자동으로 만들어 준다.

국내에 출간된 스프링 시큐리티 3 책에는 이전 PasswordEncoder를 사용하는 예제가 들어있는데, Spring-Security 버전을 3.1.4 이상으로 사용할 려면 새로운 PasswordEncoder 인터페이스를 사용 하도록 하자.

생활 코딩 하면서 만들고 있는 사이트에 encoding.PasswordEncoder를 사용하고 있어서 @Deprecate 된게 거슬렸었는데, 이번 기회에 password.PasswordEncoder로 변경 하였다

아래 링크를 참조 하면 손쉽게 바꿀 수 있다.
http://stackoverflow.com/questions/17444258/how-to-use-new-passwordencoder-from-spring-security
(그냥 빈 패키지를 변경해주고 메서드를 .encode로 변경하고, 두번째 파라미터를 삭제 하면 됨)

SaltSource를 PasswordEncoder가 직접 만들어 주기(랜덤) 때문에 별도로 관리할 필요가 없다. 고로 SaltSoure로 사용한 값이 노출 될 가능성이 없다.