데브코스 백엔드 3기 62일차

월화수! 벌써 10주차?
드디어 소문 무성하던 Spring Security 시작!
Spring Security에 있는 기능들과 필터들, Security Context, 인증처리과정 등을 배웠다


Spring Security

Spring Web 기반 서비스에서 보안관련 기능 제공 - 인증 + 인가


  • 인증 Authentication : 사용자 신원확인 - 로그인
  • 인가 Authorize : 권한을 확인, 권한있는 사용자만 접근하도록
  • Credential 보안 : 민감정보(비밀번호, 결제정보 등) 암호화해서 저장하기
  • 전송레이어 보안 : https protocol을 적용했는가 = SSL 보호를 적용했는가


  • 참고) ascii art 검색해서 Text to ASCII Art Generator로 그림 만들어서 banner.txt에 복붙하면 프로젝트 시작 때 콘솔에 이대로 모양 찍힘(신기해!)


Spring Security 설정

dependency

  • spring-boot-starter-security : 스프링시큐리티 모듈
  • spring-security-test : 스프링시큐리티 테스트 모듈
  • thymeleaf-extras-springsecurity5 : 타임리프, 스프링 시큐리티 확장모듈
    • 타임리프에서 Spring Security 인증정보객체 접근 가능


@Configuration
@EnableWebSecurity
public class WebSecurityConfigure extends WebSecurityConfigurerAdapter {

  @Override
  public void configure(WebSecurity web) {
    web.ignoring().antMatchers("/assets/**");
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
      .authorizeRequests()
        .antMatchers("/me").hasAnyRole("USER", "ADMIN")
        .anyRequest().permitAll()
        .and()
      .formLogin()
        .defaultSuccessUrl("/")
        .permitAll()
        .and()
    ;
  }
}
  • @EnableWebSecurity : Spring Security 관련 대부분의 설정 자동으로
  • WebSecurity : 필터체인 관련 전역설정 처리
    • ignoring() : Spring Security 필터체인 적용 안하고 싶은 리소스 설정
    • 일반적으로 정적리소스들
    • 불필요한 서버자원 낭비방지
      • 요청 들어오면 FilterChainProxy가 해당요청 처리할 Spring Security 필터체인 구성해서 실행,
        이 때 필터체인에 포함되는 필터 꽤많음
        매 요청마다 필터체인 구성하고 실행하는건 비효율적
  • HttpSecurity : 세부적인 웹보안 기능 설정처리
    • authorizeRequests()
    • formLogin() : 자동으로 Spring Security가 로그인페이지 생성
      • defaultSuccessUrl("/"): 로그인 성공시 갈 url


사용자 계정 추가하기

@Configuration
@EnableWebSecurity
public class WebSecurityConfigure extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
      .withUser("user").password("{noop}user123").roles("USER")
      .and()
      .withUser("admin").password("{noop}admin123").roles("ADMIN")
    ;
}
  • .password("{noop}user123")에서의 {noop} : prefix, 암호화 되지않음을 의미
    • Spring Security5에서 기본적으로 PasswordEncoder로 DelegatingPasswordEncoder 사용
      -> 패스워드를 해시알고리즘으로 암호화하는데 해시알고리즘별로 PasswordEncoder를 각각 제공
    • 최초 로그인 1회 성공시, {noop} 타입에서 {bcrypt} 타입으로 PasswordEncoder 변경됨 = 패스워드가 업그레이드 됨


로그아웃

LogoutFilter가 담당

@Configuration
@EnableWebSecurity
public class WebSecurityConfigure extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
      //생략 위와 같음  
      //로그아웃 설정
      .logout()
        .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
        .logoutSuccessUrl("/")
        .invalidateHttpSession(true)
        .clearAuthentication(true)
    ;
  }
}
  • logoutRequestMatcher(new AntPathRequestMatcher("/logout")) : /logout url 시 로그아웃하겠다
    • LogoutFilter 생성자 안에서 setFilterProcessesUrl(“/logout”) 있음 default가 /logout
  • logoutSuccessUrl("/") : 로그아웃 성공시 갈 url
  • invalidateHttpSession(true) : 로그아웃 후 해당유저 세션 invalidate 시킴
  • clearAuthentication(true) : 로그아웃 후 해당유저 Security Context를 null로 초기화
    • invalidateHttpSession(), clearAuthentication()는 SecurityContextLogoutHandler.java 에서 default true라 굳이 안해도 되는 설정


사용자 요청 어떻게 FilterChainProxy에 전달될까?

  • Servlet Filter는 Servlet 스펙이라 스프링에서 정의된 빈 사용못함
    -> Servlet Filter와 Spring의 filter 연결필요
    -> DelegatingFilterProxy가 연결
    (Servlet Filter인데, 요청 받으면 Spring Bean에게 요청 위임 = Spring 기술도 사용하면서 Filter 역할도 하는)
    • DelegatingFilterProxy는 FilterChainProxy에게 요청 위임
    • FilterChainProxy : 필터목록들, 실제 보안 처리를 하는 필터
      • 사용자의 요청은 필터체인프록시로 들어옴
      • 프록시에 있는 필터들을 차례대로 통과(모두 동작하는건x) -> 응답반환 -> 응답은 필터에 대해 역순으로 -> 사용자에게
      • Spring IoC Container에 등록된 bean 이름 : SpringSecurityFilterChain



Spring Security 구성하는 FilterChain들

ChannelProcessingFlter

  • 가장 위의 필터
  • 전송레이어 보안적용관련 - 웹요청이 어떤 protocol(http or https)로 전달되어야 하는지 처리


  • 참고) key tool 이용해서 local상에서 SSL 인증서 생성해보기(테스트용)

1

2


ExceptionTranslationFilter

필터체인 실행스택에서 자기아래오는 필터들(ex) FilterSecurityInterceptor)에서 발생하는 예외들만 처리

  • FilterSecurityInterceptor -> AccessDecisionManager(인가처리, 적절한 권한을 가지고있는가 체크)-> (거부되어 예외 발생)

  • 예외가 발생하면 ExceptionTranslationFilter에서 catch

    • 접근거부 예외 발생시킨 요청 캐시처리 후
    • AuthenticationException (인증관련)이라면 로그인페이지로 리다이렉션
    • AccessDeniedException (로그인 되어있는데도 접근거부 된거라면) -> AccessDeniedHandler로 인해 접근거부페이지로 리다이렉션 or 다른조치(커스텀 가능)


RequestCacheAwareFilter

캐시에 저장된, 인증요청에 의해 가로채진 원래 요청으로 이동


AnnonymousAuthenticationFilter

사용자 인증 안했을 때, null 대신 Anonymous 인증 타입으로 될 수 있게


DefaultLoginPageGeneratingFilter

Get 요청 시, 로그인 페이지 생성해주는 필터



SecurityContext

참고) Tread Per Request

SpringMVC가 사용하는 병렬처리(동시처리)기법

  • WAS가 Thread Pool 생성(Tomcat 기본값 200)
    = Thread Pool 수 = WAS의 최대 동시처리 HTTP 요청수
  • Thread Pool의 Thread 개수 늘리면???
    • 동시처리 수는 증가
    • but, 스레드 자체도 서버의 메모리, CPU 연산 소모하는 리소스 => 스레드 관리하는 오버헤드가 증가

    => 성능이 Thread 개수에 비례하는건 아님!


제목 없음

  • 클라이언트 요청들을 차례대로 Queue에 적재해두고 하나씩 thread pool의 thread 한개가 요청을 끝까지 담당 -> 끝나면 다시 thread pool로


  • 참고) 나름 최신의 Spring WebFlux 같은 기술은 Spring MVC와 다른 동시처리 아키텍쳐 가짐(지향하는 바가 다름)
    => 최대한 적은 Thread로 최대한 많은 수의 요청을 처리하자
    = 상황에 따라 하나의 요청을 하나 or 다수의 Thread가 처리할 수 도 있음을 의미


참고) Thread Local

동일 Thread 내에서 언제든 읽고 쓸 수 있는 변수

  • Tread Per Request와 Thread Local 같이 쓸 때 주의할점!
    • Thread 쓰고 Thread Pool에 반환 시 Thread Local clear해주기
    • 그렇지 않으면, 해당 Thread가 다른 요청처리 할 때도 Thread Local 변수값 계속 참조하게 됨


SecurityContextHolder

SecurityContext 담고있는

  • SecurityContextHolderStrategy : Thread Local 기반으로 하고있음
    => Thread 내의 어디서든 SecurityContextHolder에서 SecurityContext 조회 가능
  • Thread 반환 전, Thread Local clear 해주는 부분?
    • FilterChainProxy.java 에서 SecurityContextHolder.clearContext()


SecurityContext

Authentication 객체 담고있는 = 인증 전과 후의 모든 사용자


Authentication

  • 구현체들
    • AnonymousAuthenticationToken : 인증되지 않은 사용자를 나타내는 객체타입
    • UsernamePasswordAuthenticationToken : 아이디, 패스워드를 통해 인증된 사용자 객체타입
    • RememberMeAuthenticationToken


  • Principal : return type이 Object, 인증 전과 후의 return type이 달라짐
    • 인증 전 : String(로그인 ID)
    • 인증 후 : UserDetails의 User 객체타입으로 표현됨
  • Credentials : 비밀번호 같은 값들
  • Authorities : 권한 목록들



Spring Security의 인증처리 과정

2

  • AuthenticationProcessingFilter를 extends 한 UsernamePasswordAuthenticationFilter에서 UsernamePasswordAuthenticationToken 생성
    • attemptAuthentication()에서 obtainUsername(), obtainPassword()로 로그인폼에서 username과 password 가져와서
    • new UsernamePasswordAuthenticationToken(username, password)
    • 이 때의 생성자에서는 authorities - null / authentication - false / principal - String type의 로그인Id
    • 결론, 인증 전의 Authentication 객체!
  • 인증 전의 Authentication 객체를 AuthenticationManager에게 전달
    • AuthenticationManager를 구현한 ProviderManager는 AuthenticationProvider를 List로 들고있음
    • List<AuthenticationProvider> 중 하나(DaoAuthenticationProvider)가 실제로 인증처리
  • DaoAuthenticationProvider에서 실제 DB에서 User 가져오기 위해 UserDetailService(loadUserByUsername()) 사용
    • additionalAuthentiationChecks()에서 ID, Password 체크해서 UsernamePasswordAuthenticationToken 반환
    • 이 때의 생성자에서는 authorities - 매핑되어 있고 / authentication - true / principal - UserDetails의 User 객체타입
    • 결론, 이제는 인증 후의 Authentication 객체!



일기(회고)

  • 자바 동시성이슈에 대해 공부하다가 process, thread 등
    운영체제에 대해 너무 무지하다는 생각이 들어서
    전공자인 우리 훈팀 팀원들에게 SOS를 쳤다!
    아이들이 학교수업 때 들었던 강의, 유튜브, 자료들을 공유해주었다!
    따수워..!
    전공과목 중에도 운영체제가 제일 빡세었다고 하니 혼자 하는 나는 더 열심히 해보겠어


  • 요즘 평소보다 한시간반씩 빨리 일어나서
    CS 공부를 한다
    자기 전에는 알고리즘 한문제를 풀고 있고 BFS, DFS 위주로
    뭔가 뿌듯해서 적어본다ㅎㅎ
    계속 이렇게 부지런하게 지내자


  • 애들이랑 여유 생기면 해보자! 했던 카트를 처음 해보았다
    카트라이더보단 아니지만 재밌다
    간만에 힐링햇G

    1214-1


업데이트: