본문 바로가기

Programming/Spring Security

[Spring Security][회원가입 및 로그인 예제 5/9] Web Security Configuration 설정

들어가며...

이번 시간에는 Spring Security Configuration 에 대해서 알아보겠습니다. web.xml 또는 Spring MVC의 security-context.xml 에 접속할 페이지의 권한 설정 등을 할 수도 있으나 이런 모든 설정들을 Java 코드 기반으로도 할 수 있습니다. 

 

Java 코드 방식을 사용하려면 Spring Security에서 이미 만들어 놓은 "WebSecurityConfigurationAdapter"를 상속받아 필요한 기능들을 사용자가 구현하여 주면 됩니다. 물론 아무런 설정을 하지 않으면 Spring Security의 디폴트 형상으로 동작할 것입니다.

 

  • Table of Contents
    • SecurityConfig.java 구현 
    • WebSecurityConfigurationAdapter 설정
    • Password Encoder

SecurityConfig.java 구현

● SecurityConfig.java

package com.demo.security.auth.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import com.demo.security.auth.eo.ERole;
import com.demo.security.auth.handler.CustomAuthFailureHandler;
import com.demo.security.auth.handler.CustomAuthSuccessHandler;
import com.demo.security.auth.service.CustomUserDetailsService;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	@Autowired
	private BCryptPasswordEncoder bCryptPasswordEncoder;
	@Autowired
	private CustomUserDetailsService userDetailsService;
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userDetailsService)
			.passwordEncoder(bCryptPasswordEncoder);
	}
	
	@Override
	public void configure(WebSecurity web) throws Exception {
		web.ignoring()
			.antMatchers("/css/**", "/js/**", "/images/**", "/lib/**");
	}
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests()
				.antMatchers("/", "/login", "/registration", "/h2/**").permitAll()
				.antMatchers("/home/admin").hasAuthority(ERole.ADMIN.getValue())
				.antMatchers("/home/user").hasAuthority(ERole.MANAGER.getValue())
				.antMatchers("/home/guest").hasAuthority(ERole.GUEST.getValue())
				.anyRequest().authenticated()
				.and()
			.csrf()
				.disable()
			.headers()
				.frameOptions().disable()
				.and()
			.formLogin()
				.loginPage("/login")
				.defaultSuccessUrl("/home")
				.failureUrl("/login?error=true")
				.successHandler(successHandler())
				.failureHandler(failureHandler())
				.usernameParameter("username")
				.passwordParameter("password")
				.and()
			.logout()
				.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
				.logoutSuccessUrl("/login")
				.and()
			.exceptionHandling()
				.accessDeniedPage("/access-denied");
	}
	
	@Bean
	public BCryptPasswordEncoder passwordEncoder() {
		BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
		return bCryptPasswordEncoder;
	}
	
	@Bean
	public AuthenticationSuccessHandler successHandler() {
		return new CustomAuthSuccessHandler();
	}
	
	@Bean
	public AuthenticationFailureHandler failureHandler() {
		return new CustomAuthFailureHandler();
	}
}



WebSecurityConfigurationAdapter 추상화 클래스에는 다양한 함수들을 Override하여 사용자가 필요한 기능을 추가할 수 있습니다. 이 중에서 Form 로그인 기능에 필요한 부분들을 중심으로 구현한 예제입니다.

WebSecurityConfigurerAdapter 설정

● @EnableWebSecurity

해당 어노테이션을 설정하면 해당 클래스에 정의한 내용이 실질적으로 적용되게 됩니다.

● 사용자 정의 UserDetailsService 및 password encoder 등록

@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userDetailsService)
			.passwordEncoder(bCryptPasswordEncoder);
	}

 

앞선 글에서 구현한 "UserDetailsService"를 상속받아 구현한 객체를 할당하고 password encoding 방식을 설정하였습니다. password encoding에 관해서는 아래에서 설명하겠습니다.

● Spring Security 인증 시 제외될 항목 정의

@Override
public void configure(WebSecurity web) throws Exception {
	web.ignoring()
		.antMatchers("/css/**", "/js/**", "/images/**", "/lib/**");
}

html 페이지 외의 css, javascript, image, 외부 라이브러리 들은 인증 대상에서 제외합니다.

 

● HTTP 요청에 따른 인증 설정

이번 글에서 가장 핵심이 되는 내용이 될 것 입니다. 추후에 구현해 볼 controller에 등록된 file path별로 필요할 경우 권한을 할당하고 로그인 요청 시 성공, 실패에 따른 각종 설정을 할 수 있습니다.

Spring Security에서 다양한 기능을 제공하지만 현 프로젝트에서 구현된 내용에 대해서 간략하게 설명하면 다음과 같습니다.


▶ 파일 경로에 따른 접근 권한 설정

  • antMatchers() : 특정 경로의 요청에 대해 권한을 할당할 때 사용합니다. 주로 사용되는 함수일 것입니다. 위의 구현된 내용을 예로 차례대로 살펴보겠습니다.
  • .antMatchers("/", "/login", "/registration", "/h2/**").permitAll() : 루트패스 (localhost:8080), /login, /registration, /h2 이하 모든 하위 경로에 대해서 모든 사용자에게 접근을 허용한다는 의미입니다. 이때 설정한 파일 패스는 HTML파일이 존재하는 경로가 아니라 Controller에 설정한 파일 패스이니 혼돈하지 마시기 바랍니다. (/h2는 H2 database console 화면을 보기위해 포함시켰습니다. H2 database에 대해서는 이전 글을 참조하시기 바랍니다)

2020/03/11 - [Programming/Spring Boot] - [Spring Boot][H2 Database 연동 2/2] H2 Console 사용 및 설정

 

[Spring Boot][H2 Database 연동 2/2] H2 Console 사용 및 설정

들어가며... 지난번 포스팅에 이어 이번에는 H2 database 및 JPA 설정 방법에 대해 알아보도록 하겠습니다. Table of Contents H2 console 사용하기 H2 database 설정 이전글 참조 : 2020/03/10 - [Programming/S..

u2ful.tistory.com

  • .antMatchers("/home/admin").hasAuthority(ERole.ADMIN.getValue()) : "/home/admin" 경로는 "ADMIN" 권한을 가진 사용자만 접근 가능하다는 의미입니다. 특정 권한을 설정하는 방식으로는 ".hasRole()" 함수를 사용할 수 있습니다.
  • .anyRequest().authenticated() : 위에서 설정한 페이지 외 다른 모든 페이지는 인증된 사용자만 접근 가능하다는 의미입니다.

▶ formLogin() : 로그인폼 관련 설정

  • loginPage() : 로그인 페이지를 설정합니다. 해당 페이지를 설정하지 않으면 Spring Security에서 구현한 디폴트 화면이 노출됩니다.
  • defaultSuccessUrl() : 인가된 사용자일 경우 로그인 성공 후 이동할 페이지를 설정합니다. 물리적으로 이동할 페이지만 설정할 수 있으며 Spring Security에 의해 로그인 인증이 성공한 이후 특정 페이지로 이동하기 전에 처리해야 할 작업이 있을 경우에는 "AuthenticationSuccessHandler"를 상속받아 좀 더 세부적인 작업을 수행할 수도 있습니다. 이를 수행하는 내용에 대해서도 다음 글에서 설명할 예정이며 본 글에서는 설정하는 방법에 대해서만 설명토록 하겠습니다.
  • failureUrl() : 로그인 성공과 마찬가지로 로그인 검증이 실패한 경우 이동할 페이지를 설정합니다. 이 또한 성공 때와 마찬가지로 "AuthenticationFailerHandler"를 상속받아 좀 더 세부적인 작업을 수행할 수도 있습니다.
  • successHandler() : defaultSuccessUrl에서는 성공 시 이동할 페이지만 설정하였다면 handler를 구현하고 이를 등록하면 좀 더 세부적인 작업을 수행할 수 있습니다. 해당 파라미터로는 아래에 @Bean으로 등록한 custom AuthenticationSuccessHandler를 설정합니다.
  • failureHandler() : successHandler와 동일하게 로그인이 실패했을 때 AuthenticationFailureHandler를 상속받아 구현한 객체를 설정합니다.
  • userNameParameter(), passwordParameter() : 로그인 시 반드시 필요한 항목은 사용자명과 패스워드입니다. Spring Security에서는 기본적으로 "username", "password"라는 필드값을 사용하지만 내부적으로 구현하다 보면 이와 다른 이름을 사용할 수도 있을 것입니다. 이 때 해당 함수에 해당 이름을 설정하면 됩니다.

▶ logout() : 로그아웃

logout 기능을 사용자가 직접 구현하려면 현재 session에 있는 인증된 사용자 정보를 여부를 살펴보고 사용자 정보 및 인증 정보들을 초기화 해 주는 작업들을 직접 해 주어야 하지만 이를 Spring Security 설정으로 처리하면 이러한 번거로운 작업을 하지 않아도 됩니다.

  • logoutRequestMatcher(new AntPathRequestMatcher("/logout")) : 로그아웃에 대한 요청 파일패스를 설정하여줍니다.
  • logoutSuccessUrl("/login") : 로그아웃 성공 시 이동할 페이지를 설정합니다.

▶ exceptionHandling() : 예외처리

접근이 불가한 페이지 요청 시 후처리 작업이 필요할 경우에 대한 설정들을 수행할 수 있습니다. 여기에서는 인증 실패 시 보여줄 페이지 경로만 설정하였지만 로그인 성공, 실패처럼 Handler를 등록하여 세부적인 작업들을 수행 할 수도 있습니다.

  • accessDeniedPage("/access-denied") : 접근 불가 페이지 요청 시 이동할 파일 패스를 설정합니다.

Password Encoder

패스워드 인코더에 대해서도 간단하게 살펴보도록 하겠습니다. 사용자의 패스워드 정보를 변형하여 저장해야 하는 이유는 굳이 설명하지 않아도 되리라 생각합니다.

Spring Security에서도 이러한 사용자의 암호화 인코딩을 위해 여러 인코더를 제공합니다. 이에 대해서 간략하게 살펴보도록 하겠습니다.

● BCryptPasswordEncoder

BCrypt 라는 해쉬 알고리즘을 기반으로 제공하는 인코더로 보통의 해쉬함수와는 다르게 동일한 값을 입력하여도 매번 다른 해쉬값을 반환합니다.

https://en.wikipedia.org/wiki/Bcrypt

 

bcrypt - Wikipedia

bcrypt is a password hashing function designed by Niels Provos and David Mazières, based on the Blowfish cipher, and presented at USENIX in 1999.[1] Besides incorporating a salt to protect against rainbow table attacks, bcrypt is an adaptive function: over

en.wikipedia.org

● DelegatingPasswordEncoder

여러개의 인코더중에서 사용자가 사용할 것을 그때 그때 선택할 수 있도록 만들어논 인코더입니다. 실제 인코딩할 데이터 앞에 추가적으로 어떤 인코디 방식을 사용하였지는 prefix로 붙혀 나중에 decoding할 때 이를 참조합니다.

"bcrypt, noop, pbkdf2, scrypt, sha256" 등을 지원하며 만약 사용자가 필요에 의해 여러개의 인코더가 필요할 경우 매번 해당 인코더를 @Bean으로 정의하여 DI(Dependency Injection) 해야하는 번거로움을 줄이기 위해 해당 인코더 하나만 등록하여 사용할 수 있도록 마련한 것이 아닐까 추측해 봅니다.

● SCryptPasswordEncoder

SCrypt 라는 해쉬 알고리즘을 사용한 인코더. 성능은 빠른 듯 하나 보안에 문제가 있는 듯 함

https://en.wikipedia.org/wiki/Scrypt

 

scrypt - Wikipedia

From Wikipedia, the free encyclopedia Jump to navigation Jump to search Password-based key derivation function created in 2009 scryptGeneralDesignersColin PercivalFirst published2009Cipher detailDigest sizesvariableBlock sizesvariableRoundsvariable In cryp

en.wikipedia.org

● Pbkdf2PasswordEncoder

PBKDF2 라는 해쉬 알고리즘을 사용한 알고리즘으로 이또한 BCrypt와 마찬가지로 salt값을 사용하여 동일한 입력값에도 매번 다른 인코딩값을 반환하게 됩니다. 차이점은 PBKDF2는 salk값의 길이가 8bytes이고 BCrypt는 salt값의 길이가 72bytes라 보안면에서는 BCrypt가 좀 더 나을 듯 합니다.

 

https://en.wikipedia.org/wiki/PBKDF2

 

PBKDF2 - Wikipedia

From Wikipedia, the free encyclopedia Jump to navigation Jump to search Standard for a password-based key derivation function In cryptography, PBKDF1 and PBKDF2 (Password-Based Key Derivation Function 2) are key derivation functions with a sliding computat

en.wikipedia.org

마무리...

이상으로 Spring Security의 다양한 설정을 하는 방법에 대해서 알아봤습니다. 프로젝트의 성격에 따라 매우 다양한 설정이 가능하기 때문에 이런저런 설정값을 바꾸면서 어떻게 동작하는 지 확인할 필요가 있을 듯 합니다.

 

다음 글에서는 로그인 성공, 실패 시 후처리 작업을 하게 도와주는 AuthenticationSuccessHandler, AuthenticationFailureHandler에 대해서 알아보도록 하겠습니다.

 


U2ful은 입니다. @U2ful Corp.