Spring Security

박윤택·2022년 10월 11일
0

Spring

목록 보기
13/18

Spring Security 개요

Spring Security?

"Spring MVC 기반 어플리케이션의 인증, 인가를 지원하는 보안 프레임 워크"
InterceptorServlet Filter를 이용하여 보안 기능을 직접 구현가능하지만 Spring Security에서 안정적인 기능, 검증된 로직이기 때문에 Spring Security를 이용하는 것이 안전한 선택


용어 정리

  • Principal(주체) : 어플리케이션에서 작업을 수행할 수 있는 사용자, 디바이스 등이 될 수 있고 일반적으로 프로세스가 성공적으로 수행된 사용자의 계정정보를 의미한다.
  • Authentication(인증) : 어플리케이션에서 사용하는 사용자가 본인이 맞음을 증명하는 절차
  • Credential(신원 정보) : Authentication을 정상적으로 수행하기 위해 식별하는 정보
  • Authorization(인가) : Authentication이 정상적으로 수행된 사용자에게 하나 이상의 권한을 부여하여 특정 리소스에 접근할 수 있게 허가하는 과정을 의미

Spring Security 적용

  • build.gradle 설정
dependencies {
	...
	implementation 'org.springframework.boot:spring-boot-starter-security'   
    ...
}

  • Configuration
@Configuration
public class SecurityConfiguration {
     @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // HttpSecurity를 통해 HTTP 요청에 대한 보안 설정을 구성
        ...
        ...
    }
}

Spring Security 5.7 이전 버전에서는 WebSecurityConfigurerAdapter를 상속받아 사용하였지만 5.7 버전에서 Deprecated되어 SecurityFilterChain을 Bean으로 등록하여 HTTP 보안 설정을 구성해야 한다.

🤔 주의

    .authorizeHttpRequests(authorize -> authorize                  
                    .antMatchers("/orders/**").hasRole("ADMIN")        
                    .antMatchers("/members/my-page").hasRole("USER")   
                    .antMatchers("/**").permitAll()                   
            );

antMatchers()를 이용한 접근 권한 부여 시 permitAll()이 제일 앞에 위치하면 Role에 상관없이 모든 request URL에 대한 접근을 허용한다. 항상 더 구체적인 URL 경로부터 접근 권한을 부여한 다음 덜 구체적인 URL 경로에 대한 접근 권한을 부여해야 한다.


Spring Security 웹 요청 처리 흐름

웹 요청에서의 서블릿 필터와 필터체인의 역할

  • 서블릿 필터

어플리케이션의 엔드포인트에 요청이 도달하기 전에 중간에서 요청을 가로챈 후 어떤 처리를 할 수 있는 적절한 포인트를 제공, 요청의 전처리 뿐만 아니라 응답을 보내주기 전에 처리작업도 할 수 있다.

  • 필터 체인
    하나 이상의 필터들을 연결한 구조

    Sevelet FilterChain은 요청 URI를 기반으로 HttpServletRequet를 처리한다. 클라이언트가 서버 측으로 요청을 하면 서블릿 컨테이너는 해당 URI의 경로를 기반으로 어떤 필터와 어떤 Servlet을 매핑할지 결정한다.

  • DelegatingFilterProxy
    서블릿 컨테이너 영역의 필터와 ApplicationContext에 Bean으로 등록된 필터들을 연결해주는 브릿지 역할

  • FilterChainProxy
    Spring Security에서 보안을 위한 작업을 처리하는 필터들을 모아둔 것이며, Spring Security의 Filter를 사용하기 위한 진입점
    FilterChainProxy로부터 Spring Security에서 제공하는 보안 필터들이 필요한 작업을 수행한다.

Filter와 FilterChain 구현

  • Filter interface 구현
import javax.servlet.*;
import java.io.IOException;

public class FirstFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
        System.out.println("FirstFilter 생성됨");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("========First 필터 시작========");
        chain.doFilter(request, response);
        System.out.println("========First 필터 종료========");
    }

    @Override
    public void destroy() {
        System.out.println("FirstFilter Destory");
        Filter.super.destroy();
    }
}
  • Filter Configuration
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterConfiguration {

    @Bean
    public FilterRegistrationBean<FirstFilter> firstFilterRegister()  {
        FilterRegistrationBean<FirstFilter> registrationBean = new FilterRegistrationBean<>(new FirstFilter());
        // registrationBean.setOrder(1);
        return registrationBean;
    }
}

FilterConfiguration에 여러 필터를 넣는다면 registrationBean.setOrder() 메서드를 이용하여 Filter가 적용되는 순서를 지정할 수 있다. 또는 Filter에 @Order어노테이션을 이용하여 Filter의 순서를 지정할 수 있다.


Spring Security 인증

인증 처리 흐름

  1. Username(ID)와 password를 Spring Security의 Filter Chain중 UsernamePasswordAuthenticationFilter가 해당 요청을 받는다.

  2. UsernamePasswordAuthenticationToken을 생성한다. 이때 아직 인증이 되지 않는다.

  3. 인증되지 않은 Authentication을 AuthenticationManager에게 전달한다. AuthenticationManager는 인증처리를 총괄하는 매니저 역할의 인터페이스이고 ProviderManager가 구현 클래스이다.

  4. ProviderManager로부터 Authentication을 전달받은 AuthenticationProvider는 ProviderManager의 인증 처리를 대신한다.

  5. UserDetailsService를 통해 UserDetails를 조회한다.

  6. DB에 저장되어있는 사용자의 Credential(신원 정보)를 조회한다.

  7. UserDetails를 생성한다.

  8. AuthenticationProvider에게 전달한다.

  9. AuthenticationProvider는 UserDetails를 검증하기 위해 PasswordEncoder를 이용하여 암호화된 Password의 일치여부를 검증한다.
    검증에 성공하면 UserDetails를 이용하여 Authentication을 생성하고 그렇지 않으면 Exception을 발생시키고 인증처리를 중단한다.

  10. ProviderManager에게 Authentication을 전달한다. 2번에서는 Authentication은 인증들 위해 필요한 사용자의 로그인 정보를 가지고 있지만 전달한 Authentication에는 Principal, Credential, GrantedAuthorities를 가지고 있다.

  11. 인증된 Authentication을 UsernamePasswordAuthenticationFilter에게 전달한다.

  12. 인증된 Authentication을 UsernamePasswordAuthenticationFilter에서 SecurityContext에 저장한다.


Spring Security 인가

인가 처리 흐름


로그인을 성공한 이후에 인증된 사용자에게 권한을 부여하는지에 대한 흐름이다.

Spring Security Filter Chain에서 URL을 통해 사용자의 엑세스를 제한하는 권한 부여 Filter는 AuthorizationFilter이다.

  1. 인증을 무사히 마쳤다면 SecurityContext에 Authentication이 저장되어 있을 것이다. 이를 가져온다.

  2. 획득한 Authentication과 HttpServletRequest를 AuthorizationManager에게 전달한다.

  3. AuthorizationManager는 RequestMatcherDelegatingAuthorizationManager에게 권한 부여를 위임한다. RequestMatcherDelegatingAuthorizationManager는 RequestMathcer 평가식을 기반으로 권한을 체크한다.

  4. 적절한 권한이라면 다음 요청 프로세스를 이어간다.

  5. 적절한 권한이 아니라면 AccessDeniedException이 throw 된다.


접근 제어 표현식

표현식설명
hasRole(Stirng role)- 현재 보안 주체(principal)가 지정된 역할을 갖고 있는지 여부를 확인하고 가지고 있다면 true를 리턴한다.
- hasRole(’admin’)처럼 파라미터로 넘긴 role이 ROLE_ 로 시작하지 않으면 기본적으로 추가한다.
(DefaultWebSecurityExpressionHandler의 defaultRolePrefix를 수정하면 커스텀할 수 있다.)
hasAnyRole(String… roles)- 현재 보안 주체가 지정한 역할 중 1개라도 가지고 있으면 true를 리턴한다.
(문자열 리스트를 콤마로 구분해서 전달한다.)
- ex) hasAnyRole(’admin’, ‘user’)
hasAuthority(String authority)- 현재 보안 주체가 지정한 권한을 갖고 있는지 여부를 확인하고 가지고 있다면 true를 리턴한다.
- ex) hasAuthority(’read’)
hasAnyAuthority(String… authorities)- 현재 보안 주체가 지정한 권한 중 하나라도 있으면 true를 리턴한다.
- ex) hasAnyAuthority(’read’, ‘write’)
principal- 현재 사용자를 나타내는 principal 객체에 직접 접근할 수 있다.
authentication- SecurityContext로 조회할 수 있는 현재 Authentication 객체에 직접 접근할 수 있다.
permitAll- 항상 true로 평가한다.
denyAll- 항상 false로 평가한다.
isAnonymous()- 현재 보안 주체가 익명 사용자면 true를 리턴한다.
isRememberMe()- 현재 보안 주체가 remember-me 사용자면 true를 리턴한다.
isAuthenticated()- 사용자가 익명이 아닌 경우 true를 리턴한다.
isFullyAuthenticated()- 사용자가 익명 사용자나 remember-me 사용자가 아니면 true를 리턴한다.
hasPermission(Object target, Object permission)- 사용자가 target에 해당 permission 권한이 있으면 true를 리턴한다.
ex) hasPermission(domainObject, ‘read’)
hasPermission(Object targetId, String targetType, Object permission)- 사용자가 target에 해당 permission 권한이 있으면 true를 리턴한다.
ex) hasPermission(1, ‘com.example.domain.Message’, ‘read’)

0개의 댓글