본문 바로가기
프로그래밍/Spring Boot

스프링 시큐리티(Spring Security) 간략히 정리

by yonmoyonmo 2021. 5. 27.

이번에 인증서버를 하나 만들면서 스프링 시큐리티를 사용해 보았다. 여태까지는 그냥 대충 구글링 해서 썼는데 이번 기회에 제대로 알고 좀 써볼까 싶어서 스프링 시큐리티 프레임워크의 철학과 동작 방식을 알아보았다.

스프링 시큐리티는 스프링 어플리케이션 보안 프레임워크이다. 어플리케이션 레벨에서 요청의 인증, 인가를 처리해 준다. 스프링 프레임워크의 필터 기능을 이용하여 스프링 디스패쳐 서블릿(Dispathcer Servlet)에 요청이 도달하기 전에 보안관련 작업을 할 수 있게 되어있다. 스프링 시큐리티를 쓰지 않을 시 필터나 인터셉터를 이용해서 필요한 기능을 일일히 구현하긴 할 수 있겠지만 튼튼하게 잘 나와있는 스프링 시큐리티를 좀 익혀서 쓰는 것이 좋아보인다.

다양한 방식의 인증을 구현할 수 있도록 되어있다.

아이디 패스워드 인증방식뿐 아니라 아래 방식까지 다 커버한다고 한다.
SSO/ Okta/ LDAP
App level Authorization
Intra App Authentication like OAuth
Microservice security(tokens, JWT)
Method level security

스프링 시큐리티의 5가지 컨셉

스프링 시큐리티 프레임워크에서 쓰이는 용어 중 제일 중요한 5가지는 아래와 같다.

Authentication

Who are you? 요청의 주체에 대한 검증이다. 어떤 방식으로든 HTTP request가 우리에게 요청할 자격이 있는가를 판단하는 것이다.

Authorization

Can this user do this? 요청의 주체에 대한 authentication으로 요청의 자격을 검증했다면 이제 어디까지 권한을 가지고 있는지를 파악하고 부여된 권한 만큼만 접근할 수 있도록 한다.

Principal

Currently logged in user; 현재 요청 쓰레드에서(Security Context) 유저의 identity와 상세 정보를 알 수 있도록 해준다. 

Granted Authority

A concept of permission; 읽기 권한, 쓰기 권한 등을 의미한다.

Roles

Group of authorities; 권한을 여러개 묶어서 하나의 role로 만들어서 부여할 수 있게 해준다.

매커니즘

스프링 시큐리티는 스프링 영역 앞에서 Filter로 거의 모든 것을 다 조진다. HTTP 요청이 스프링 어플리케이션으로 오면 스프링 시큐리티에서 가로채서 지지고 볶은 뒤에 어플리케이션으로 돌려주는 식이다. 그 과정에 사용되는 필터들을 어떻게 쓰냐에 따라서 다양한 인증 매커니즘을 구현할 수 있다.

단순히 유저의 패스워드와 아이디로 인증하는 것 부터 OAuth2 방식까지 구현하기 나름인 것 같다.

아무튼 스프링 시큐리티가 제공하는 다양한 클래스와 인터페이스들을 이용해서 필터들을 체이닝하여 쓰면 된다.

https://ducmanhphan.github.io/2019-02-09-The-mechanism-of-spring-security/

기본적인 사용법

스프링 부트 프로젝트에 스프링 시큐리티 디펜던시(https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security)를 추가하면 디폴트로 모든 엔드포인트에 대한 인증이 적용된다. application.properties에

 spring.security.user.name=abcd
 spring.security.user.password=1234

이렇게 설정해 주면 기본 제공해주는 로그인 폼으로 인증을 할 수 있다. 하지만 실제 서비스에서 이렇게 사용할 수는 없고 목적에 맞게 설정하고 구현해서 쓸 줄 알아야 한다. 스프링 시큐리티 프레임워크 하나만 가지고도 책 한 권을 쓸 정도로 내용이 많아서 이걸 전부 알고 쓰기는 어렵다. 그래서 어떤 방식의 인증, 인가를 구현할 것인지를 생각해 두고 레퍼런스(https://docs.spring.io/spring-security/site/docs/5.0.7.RELEASE/reference/html/index.html)를 찾아가면서 그 때 그 때 구현하는 방법이 일반적인 것 같다.

기본적으로는 WebSecurityConfigurerAdapter를 상속하는 클래스에 인증과 인가에 관한 configure 메소드를 오버라이드 해서 사용하게 된다. 보통 아래 코드 같이 두 가지 메소드를 오버라이드 하게 된다.

public class SecurityConfig extends WebSecurityConfigurerAdapter {
	
    //Authentication configure
    @Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		....
	}
    
    //Authorization configure
    @Override
	protected void configure(HttpSecurity httpSecurity) throws Exception {
		...
	}

}

스프링 시큐리티의 여러가지 필터들을 체이닝하는 식으로 구현하면 된다.

나는 이번에 Stateless + OAuth2 + Cookie + 이메일 / 패스워드 인증 + JWT발급 을 하는 시큐리티 앱을 구현해 보았는데 위 두 메소드를 이런 식으로 썼다.

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .cors()
                    .and()
                .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                .csrf()
                    .disable()
                .formLogin()
                    .disable()
                .httpBasic()
                    .disable()
                .exceptionHandling()
                    .authenticationEntryPoint(new RestAuthenticationEntryPoint())
                    .and()
                .authorizeRequests()
                    .antMatchers("/","/auth/**","/oauth2/**")
                    .permitAll()
                .anyRequest()
                    .authenticated()
                .and()
                .oauth2Login()
                    .authorizationEndpoint()
                        .baseUri("/oauth2/authorize")
                        .authorizationRequestRepository(httpCookieOAuth2AuthorizationRequestRepository)
                        .and()
                .userInfoEndpoint()
                    .userService(customOAuth2UserService)
                    .and()
                .successHandler(oAuth2AuthenticationSuccessHandler)
                .failureHandler(oAuth2AuthenticationFailureHandler);
        http.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

 이런식으로 왕창 체이닝 하는 식으로 쭉쭉쭉 붙여가면서 각 클래스들에대한 레퍼런스를 찾아 보면서 만들면 되는 것 같다.

원모 깃허브 : https://github.com/yonmoyonmo/wcs-authentication

마치는 말

스프링 시큐리티 프레임워크가 조금 복잡해 보이기는 하지만 익혀두면 아주 든든한 것 같다. 스프링 백엔드 매니아들은 필수적으로 공부해야할 요소인 것 같다. 나도 앞으로 계속 사용하면서 공부해야겠다.

댓글