데브코스 백엔드 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
)
- 이벤트를 발행시키는 부분(Spring Security 내부)와 이벤트를 구독해서 처리해주는 부분(리스너 로직)이 동기적으로 동일 스레드 내에서 실행이 됨
//@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
설정하기
- Thread Pool을 사용하면서 Security Context 참조를 다른 thread로 전파하려면?
//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 : 데이터
- 여전히, 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시간에 한주 강의내용을 같이 정리하는데
필터가 너무 많으니까 어질어질했던 우리팀 대화 ㅋㅋㅋ(너무 웃겼음)
- ㄷㅎ님과 1:1 코드리뷰
일대일은 처음이기에 설렜다
코드리뷰도 하고 요즘 살짝 드는 고민들에 대해 꺼냈다
중간에 ㄱㅎ님도 오셨는데 명언제조기 두분 ㅎㅎ
결론은 시간가는줄 모를 정도로(3시간함 ㅎㅎ) 너무너무 좋았던 시간이었다! 힐링!- 곡선을 보여주면 된다
- 신박했던 대화 : 한글로 코딩을 하게된다면 어떨까