dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.springframework.security:spring-security-test'
}
토리맘의 한글라이즈 프로젝트 - Spring Security doc 5.3.2 기준 번역
Spring Security doc
코딩스타트 - Spring boot - Spring Security(스프링 시큐리티) 란? 완전 해결!
기존에는 WebSecurityConfigurerAdapter
를 상속받은 Config class 를 만들어 security 설정을 했으나 Spring Security 5.7.0-M2
부터 deprecated 되어버렸다.
(하지만 아직 공식 doc 에서도 기존방식을 쓰라고 가이드 중_22.06.22 기준)
하지만 Spring Blog - spring security without the websecurityconfigureradapter 라는 글에 새로운 구현 방법이 설명되어 있어서 참고해 작성해보려 한다.
WebSecurity
는 패턴에 해당하는 리소스에 아예 Spring Security
를 적용하지 않도록 설정한다. 그렇기에 자유롭게 access가 가능해진다.
This will omit the request pattern from the security filter chain entirely. Note that anything matching this path will then have no authentication or authorization services applied and will be freely accessible.
HttpSecurity
는 WebSecurity
에서 제외된 리소스 외의 부분에 Spring Securiy
를 설정할 수 있게 해준다. authorizeRequests
를 통해 리소스의 인증 부분을 ignore 할 수 있다고 해도 그외 다른 security 기능들에 대해서는 영향을 받는다.(secure header
, CSRF protection
등)
stackoverflow - Spring Security Configuration - HttpSecurity vs WebSecurity
WebSecurityCustomizer
를 통해 Spring Security
를 적용하지 않을 리소스를 설정한다. WebSecurityCustomizer
가 함수형 인터페이스라 아래와 같이 작성하면 로직이 작동한다.
public class SecurityConfig {
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return web -> {
web.ignoring()
.antMatchers(
"/api-document/**",
"/swagger-ui/**"
);
};
}
}
사이트 간 요청 위조라는 공격 방식으로 사용자가 의도하지 않았지만 공격자의 의도에 따라 자신도 모르게 서버를 공격하게 되는걸 CSRF 라고 한다.
쿠키를 기반으로 한 인증 방식일 때 사용되는 공격방식인데, Spring Security
에서는 기본 설정으로 이 방식이 적용되어 있다. 이 방식이 적용되어 있으면 CSRF 토큰 방식을 사용해서 인증을 진행해야하는데, REST API 방식을 사용할 때는 쿠키를 사용해서 인증하는 방식을 잘 사용하지 않기에 설정을 꺼두어도 무방하다.
아래 참고 글들을 보면 쿠키를 통한 인증방식을 사용하지 않는다면 CSRF
를 방어하지 않아도 좋다고 한다. 이번 설정에서도 쿠키는 사용하지 않기에 disable
처리 해두었다.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable();
return http.build();
}
}
Should I use CSRF protection on Rest API endpoints?
CSRF(Cross-Site Request Forgery) 공격과 방어
Spring Security
는 기본적으로 CORS 를 허용하지 않도록 설정되어있기 때문에 허용하고 싶다면 추가 설정을 해야 한다.
setAllowCredentials
: cross origin 으로부터 인증을 위한 쿠키 정보를 받을지 여부.
setAllowedOrigins
: 허용할 origin 정보.
setAllowedMethods
: 허용할 http methods.
addAllowedHeader, Method, Origin
: 위의 set
들은 리스트로 받았다면, add
는 개별 지정(*
등)
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors().configurationSource(corsConfigurationSource());
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(false); // 쿠키를 받을건지
configuration.setAllowedOrigins(Arrays.asList("http://localhost:8080"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST"));
configuration.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
Security filter
에서 발생하는 인증, 인가 관련 에러 처리를 해준다. 필터에서 발생한 에러기에 dispatcher servlet 까지 닿지 못하고, 내부에 만들어 놓은 custom exception 을 사용할 수 없다. 그렇기에 원하는 에러 처리를 하려면 추가 설정을 해줘야한다.
각각 AuthenticationException
과 AccessDeniedException
발생시 실행된다.
exceptionHandling().authenticationEntryPoint(설정)
으로 authenticationEntryPoint
설정. ExceptionTranslationFilter
필터에서 이 설정을 사용하는데 AuthenticationException
이 발생하면 authenticationEntryPoint
를 실행시킨다. accessDeniedHandler
도 동일하게 작동한다.
If an AuthenticationException is detected, the filter will launch the authenticationEntryPoint. This allows common handling of authentication failures originating from any subclass of AbstractSecurityInterceptor.
If an AccessDeniedException is detected, the filter will determine whether or not the user is an anonymous user. If they are an anonymous user, the authenticationEntryPoint will be launched. If they are not an anonymous user, the filter will delegate to the AccessDeniedHandler. By default the filter will use AccessDeniedHandlerImpl.
Spring doc - Class ExceptionTranslationFilter
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.authenticationEntryPoint() // 인증 에러 핸들링
.accessDeniedHandler() // 인가 에러 핸들링
;
return http.build();
}
Spring doc - AuthenticationEntryPoint
Spring doc - AccessDeniedHandler
hoons.log - 스프링시큐리티 JWT 예외처리
Spring Security JWT 토큰 검증 시 Exception 예외 처리
세션을 어떻게 관리할지 설정한다.
sessionCreationPolicy
: 어떤 생성 전략을 가져갈지 설정한다.
/**
* Always create an {@link HttpSession}
*/
ALWAYS,
/**
* Spring Security will never create an {@link HttpSession}, but will use the
* {@link HttpSession} if it already exists
*/
NEVER,
/**
* Spring Security will only create an {@link HttpSession} if required
*/
IF_REQUIRED,
/**
* Spring Security will never create an {@link HttpSession} and it will never use it
* to obtain the {@link SecurityContext}
*/
STATELESS (JWT 같은 토큰 방식을 쓸때 사용)
// SessionCreationPolicy.STATELESS 설정이 스프링 시큐리티가 무조건 세션을 생성하지 않는다는 의미보다는
// 인증 처리 관점에서 세션을 생성하지 않음과 동시에 세션을 이용한 방식으로 인증을 처리하지 않겠다는 의미로 해석할 수 있다는 점이고
// 인증메커니즘이 아닌 다른 특정한 곳에서 세션과 관련된 처리를 해야 하는 경우나
// 스프링이 아닌 다른 어플리케이션에서 세션을 사용하는 곳이 있다면 세션이 생성 될 수도 있다는 점입니다. (인프런 답변 참고)
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
;
return http.build();
}
path matching 기반으로 인증, 인가가 필요한 부분을 설정한다. antMatchers
를 통해 적용할 경로를 설정하고, hasRole
, permitAll
, authenticated
등을 통해 누가 접근할 수 있는지 설정할 수 있다.
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/member/**").permitAll() // token 설정 전까지 오픈
.antMatchers("/api-document/**").permitAll()
.antMatchers("/swagger-ui/**").permitAll()
.anyRequest().authenticated(); // 그 외 모든 요청에 대해 인증 필요
return http.build();
}