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

목금!
Spring Sercurity에서 인증 시 session 처리와
인가처리, 이벤트, 그리고 그 밖의 필터들에 대해 배웠다


Spring Security Session 처리

SecurityContextPersistenceFilter

인증관련 필터 중 최상단

  • Session에서 Security Context를 가져와서
    Security Context Holder 통해서 Thread Local 변수에
    인증된 Authentication 객체를 set하는 일이 일어남
  • 사실 이거하는 애는, SecurityContextRepository(Security Context를 session에 load하고 save 함)


SessionManagementFilter

session-fixation protection 세션고정보호 라는 기능

  • session-fixation attack
    • 다양한 방법이 있음
    • ex) 악성유저가 정상유저에 sessionId를 주입하고, 그걸 모르는 정상유저는 그 sessionId를 가지고 로그인하게되면 악성유저는 정상유저가 로그인한 상태의 세션 사용함
    • ex) 악성유저가 로그인 전의 정상유저의 sessionId 탈취해 정상유저가 로그인 후 그 세션을 악성유저가 사용함

=> 해결 : 인증전 사용자 세션을 인증 후에 사용 못하게 하면 됨!
= 인증 전-후 세션 달라지면 됨


=> Spring Sercurity에서는 세션고정보호를 위해
SessionAuthenticationStrategy 통해 세션의 생성 방식을 4가지 옵션으로 제공

.sessionManagement()
  .sessionFixation().changeSessionId()
  .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
  • sessionFixation()은 세션의 생성 방식 정함
    • none : 아무것도안함, 인증 전-후 세션 그대로 => session-fixation attack오면 그대로 받음
    • newSession : 인증 후 새로운 세션을 만들고 + 기존 데이터 복제 안함
    • migrateSession : 인증 후 새로운 세션을 만들고 + 기존 데이터 복제
    • changeSession : 인증 후 새로운 세션을 만들지 않지만, 공격 방어함(Servlet 3.1 이상에서 지원)
      • 인증할때마다 새로운 세션아이디 발급 + 다른 모든 세션 속성은 유지


  • sessionCreationPolicy()는 세션의 생성 정책 정함
    • ALWAYS : 항상 세션 사용
    • IF_REQUIRED : default, 필요 시 생성
    • NEVER : Spring Security가 세션생성은 안함, 그러나 이미 있으면 사용
    • STATELESS : 세션 완전 사용 안함



Spring Security에서의 인가

1. FilterSecurityInterceptor의 attemptAuthorization()에서 시작


2. getAccessDecisionManager() 통해 부르면 AccessDecisionManager(정확히는 이를 구현한 구현체의 Voter들을 통해)가 권한비교

resource 접근에 필요한 권한들 vs 유저가 보유한 권한들 비교

  • resource 접근에 필요한 권한들은 SecurityMetadataSource 통해서 ConfigAttributes 타입으로 가져옴
  • 유저가 보유한 권한들은 Security Context Holder의 인증완료된 Authentication 객체의 권한을 가져옴(익명유저도 인증완료된 객체)


AccessDecisionManager의 구현체

  • AffirmativeBased : Voter들 중 하나라도 승인하면 승인
  • ConsensusBased : 다수
  • UnanimousBased : 만장일치


  • 이 구현체들은 List<AccessDecisionVoter>를 가지고 있음, 각각의 Voter들이 투표 후 집계하는 느낌(정확히는 AccessDecisionVoter의 구현체들이)
    • AccessDecisionVoter의 구현체 WebExpressionVoter는 SpEL을 사용하는 Voter
    • 이 때의 표현식은 WebSecurityExpressionRoot에서 제공받음


  • url 접근권한 검사를 커스텀 할 수 있음
    • SpEL 표현식 커스텀(WebSecurityExpressionRoot 상속받아서) + 핸들러 커스텀해서 구현할 수도 있고
    • 아예 Voter 자체를 커스텀해서 만들고(빈등록) + 기존 핸들러 사용해도됨
    @Bean
      public AccessDecisionManager accessDecisionManager() {
        List<AccessDecisionVoter<?>> decisionVoters = new ArrayList<>();
        decisionVoters.add(new WebExpressionVoter());
        //커스텀한 voter(OddAdminVoter)를 AccessDecisionVoter 리스트에 추가해주기
        decisionVoters.add(new OddAdminVoter(new AntPathRequestMatcher("/admin"))); 
        //default(AffirmativeBased)가 아닌 UnanimousBased(만장일치)로 커스텀해보기  
        return new UnanimousBased(decisionVoters); 
      }
    


3. 권한 부족하면 AccessDeniedException



Spring Security에서의 인증관련 이벤트객체

AbstractAuthenticationEvent : Spring Security의 최상단 이벤트 객체


Spring Security의 이벤트 종류

  • 인증성공 : AuthenticationSuccessEvent
  • 인증실패 : AbstractAuthenticationFailure(인증 실패 원인에 따라 이벤트(클래스)가 달라질 수 있음)
    • AuthenticationFailureBadCredentialsEvent : 로그인 ID, pwd 잘못 되었을 때
    • AuthenticationFailureExpiredEvent : 로그인 계정 만료되었을 때


Spring Security의 이벤트가 발행되는 곳

  • AuthenticationEventPublisher
    • publishAuthenticationSuccess()
    • publishAuthenticationFailure()
    • 이 메서드들을 ProviderManager에서 호출


이벤트객체(모델)을 사용하는 이유

모듈간의 결합도를 느슨하게 하는데 사용

  • ex) 이벤트를 추가해야하는 경우 -> 이벤트리스너만 추가하면 됨
  • 이벤트리스너 추가하기
    • @EventListener 붙인 메서드 만들어주면 됨
    • 주의!
      • 이벤트를 발행시키는 부분(Spring Security 내부)와 이벤트를 구독해서 처리해주는 부분(리스너 로직)이 동기적으로 동일 스레드 내에서 실행이 됨
        = 리스너에서 처리 늦어지면 이벤트 발생부분까지 처리가 지연됨
        => 리스너에서 오래걸리는 작업을 해야한다면 스레드 분리할 수 있음
        @Async붙여주기(WebMVC configuration class에도 @EnableAsync)
//@Async
@Component
public class CustomAuthenticationEventHandler {

  @EventListener
  public void handleAuthenticationSuccessEvent(AuthenticationSuccessEvent event) {
    Authentication authentication = event.getAuthentication();
  }



그 밖의 필터들

CsrfFilter

TokenRepository통해서 CSRF Token 만들고
요청오면(요청마다 CSRF Token 같이보냄) 요청의 token과 TokenRepository에서 token 가져와서(세션의 token) 비교

  • CSRF(Cross-site Request Forgery) : 사용자가 자신의 의지와 무관하게 공격자가 의도한 행위를 웹사이트에 요청하게 하는 공격


WebAsyncManagerIntegrationFilter

  • Spring Web MVC Async Request : Spring Web MVC에서 Http요청처리를 별도의 다른 스레드로 분리해 처리하는 기능
    => 그렇게 되면 다른 Thread
    • Thread Pool을 사용하면서 Security Context 참조를 다른 thread로 전파하려면?
      • DelegatingSecurityContextAsyncTaskExecutor 설정하기
//Task를 처리할 Task Executor 를 만들기
@Bean
  @Qualifier("myAsyncTaskExecutor")  //기본 Task Executor도 autowired 되어있어서 명시해주기
  public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(3);
    executor.setMaxPoolSize(5);
    executor.setThreadNamePrefix("my-executor-");
    return executor;
  }

  @Bean
  public DelegatingSecurityContextAsyncTaskExecutor taskExecutor(
    @Qualifier("myAsyncTaskExecutor") ThreadPoolTaskExecutor delegate
  ) {
    return new DelegatingSecurityContextAsyncTaskExecutor(delegate);
  }



이번주 테코톡

12/12 월, 아마찌의 ORM vs SQL Mapper vs JDBC

  • JDBC만 이용 : 중복코드, 자원반납 필수
    => 불편함들 -> Framework(SQL Mapper, ORM) - 내부적으로 JDBC API는 사용
  • SQL Mapper : SQL문과 객체의 필드를 매핑하여 데이터를 객체화 + SQL문 직접 작성(DB에 완벽하게 독립은 x)
    • 여전히, OOP와 RDB의 패러다임 불일치 문제(지향하는 바가 다름)
      • OOP : 객체지향(상속, 추상화)
      • RDB : 데이터
  • ORM : DB-Object 매핑, SQL문이 아닌 직관적인 코드(메소드)로 데이터 조작


12/13 화, 코카콜라의 HTTP 메서드와 상태코드

  • PUT : 완전히 대체, 해당 리소스가 없으면 생성
  • ETag 사용해 효율성 높임 : 작은 토큰(일반적으로 파일 내용의 해시)을 서버로 보내 파일이 변경되었는지 확인
    서버가 동일한 토큰을 반환하면 파일이 동일하므로 다시 다운로드할 필요가 없음


12/14 수, 코다의 Process vs Thread

  • 동시성 : 한순간에 여러가지 일x
    • 한 프로세스가 CPU 점유 ⇒ 다른 프로세스 실행 못함
    • 짧은 텀으로 전환(Context switching), 여러가지 일이 동시에 일어나는 것처럼 보이는 것


  • 프로그램 → (실행) → 프로세스
    • 프로그램이 실행되는데에 필요한 것들이 메모리에 올라가야함(코드, 데이터, heap, stack 영역)
    • PCB블럭(프로세스 정보 담은) 만들어짐 - 포인터, process state, PID, 프로그램 카운터


  • Thread
    • 경량화된 프로세스 버전
    • 하나의 프로세스 안에 여러개의 스레드
    • 공유되는 자원이 있음(stack 빼고 메모리 영역)
      → 컨텍스트 스위칭 시 캐싱적중률이 높아짐! (다 넣었다가 다 뺏다가 하지 않아도 되니까)


Multi-Process vs Multi-Thread
한 어플리케이션에서 두개이상의 작업을 동시에 처리하는 방식이 다를뿐

  • Multi-Process
    • 부모 프로세스가 fork()해서 자식 프로세스 여러개 ⇒ 별개의 메모리 영역
    • 독립적
      • 동기화 불필요
      • 자원소모(개별 메모리 차지) / IPC 통해 통신 / Context switching 비용 증가
  • Multi-Thread
    • 자원공유
      • 메모리 효율적 사용 / Context switching 비용 감소
      • 동기화 필요 / 한 스레드에 문제 생기면 전체 프로세스에 영향 미칠 수 있음


Multi-core
하드웨어 측면 = 물리적으로 여러코어 사용

  • 병렬처리 (하나의 코어는 동시성, 멀티코어는 병렬)
  • 실제로 여러 프로세서 ⇒ 다수의 실행단위를 한순간에 처리


12/15 목, 인비의 DTO vs VO

  • DTO : 데이터 전달용
  • VO : 값 표현용


12/16 금, 크리스,로마의 Stream vs for

  • for문 : 자바 초반부터 있었음
    • 외부반복
    • 코드블록으로 표현
    • 반복문을 return, break, continue 등 제어가능
  • 향상된 for문 : IndexOutOfBoundsException로부터 안정적, 가독성
  • Stream : stream 생성 - 중간연산 - 최종연산
    • 내부반복, 지연연산
    • 파이프라인 함수객체(람다식, 메서드참고)로 표현
      • 표현 간결
      • but final 변수만 읽을 수 있음, 외부변수 수정 못함
  • 스트림을 적용하기 좋은 조건(이펙티브 자바 45)
    • 원소들의 sequence를 일관되게 변환한다
    • 필터링한다
    • 하나의 연산을 사용해 결합한다
    • 컬렉션에 모은다
    • 특정 조건을 만족하는 원소를 찾는다
  • 각각의 장단점을 고려해서 사용하자
    • 가장 좋은 성능은 for문 + int 배열
    • 오늘날 하드웨어는 충분히 빠르기 때문에,
      소프트웨어 입장에서 성능보다 다른점(가독성, 유지보수 등)을 고려해서 결정하는 추세



일기(회고)

  • 요즘 공부를 하면서 드는 생각은
    공부를 하면 할수록, 시야가 넓어질수록
    궁금한게 공부할게 두배세배씩 더 생긴다는게 신기하다?
    점점 더 깊게 공부해서 좋긴하다


  • Spring Security는 일단 대략적으로 알고있어야 할 필터들과
    각 필터들의 굵직한 특징, 인증과 인가처리 과정 정도만 정리하였다
    깊게 공부하는 것은 실제로 사용할 때 해도 될것 같다는 다수의 멘토님들 말씀
    다만 다음주에 배우는 JWT는 집중해서 공부하기


  • 이번주 Spring Security는 정말 쉽지 않았다
    필터부자!
    RBF시간에 한주 강의내용을 같이 정리하는데
    필터가 너무 많으니까 어질어질했던 우리팀 대화 ㅋㅋㅋ(너무 웃겼음)

    1216


  • ㄷㅎ님과 1:1 코드리뷰
    일대일은 처음이기에 설렜다
    코드리뷰도 하고 요즘 살짝 드는 고민들에 대해 꺼냈다
    중간에 ㄱㅎ님도 오셨는데 명언제조기 두분 ㅎㅎ
    결론은 시간가는줄 모를 정도로(3시간함 ㅎㅎ) 너무너무 좋았던 시간이었다! 힐링!
    • 곡선을 보여주면 된다
    • 신박했던 대화 : 한글로 코딩을 하게된다면 어떨까

    1216-1


업데이트: