-
[Spring] RequestCacheAwareFilter, ChannelProcessingFilter 사용하기Backend/Spring 2022. 9. 2. 17:49728x90
RequestCacheAwareFilter
FilterChainProxy를 구성하는 주요 Security Filter 중 하나로 로그인 성공 이후 가로채어진 사용자의 원래 요청으로 이동하기 위해 사용된다.
익명의 사용자(로그인 안한 사용자)가 보호받는 리소스 페이지에 접근하는 경우
- 접근 권한이 없기 때문에 AccessDecisionManager에서 접근 거부 예외가 발생함
- ExceptionTranslationFilter 접근 거부 예외를 처리함
- 현재 사용자가 익명 사용자라면, 보호받는 리소스로의 접근을 캐시 처리하고, 로그인 페이지로 이동시킴
- 로그인이 완료되면 원래 접근하려 했던 캐싱 페이지로 접근할 수 있게 함
private void handleAccessDeniedException(HttpServletRequest request, HttpServletResponse response, FilterChain chain, AccessDeniedException exception) throws ServletException, IOException { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); boolean isAnonymous = this.authenticationTrustResolver.isAnonymous(authentication); if (isAnonymous || this.authenticationTrustResolver.isRememberMe(authentication)) { if (logger.isTraceEnabled()) { logger.trace(LogMessage.format("Sending %s to authentication entry point since access is denied", authentication), exception); } sendStartAuthentication(request, response, chain, new InsufficientAuthenticationException( this.messages.getMessage("ExceptionTranslationFilter.insufficientAuthentication", "Full authentication is required to access this resource"))); } else { if (logger.isTraceEnabled()) { logger.trace( LogMessage.format("Sending %s to access denied handler since access is denied", authentication), exception); } this.accessDeniedHandler.handle(request, response, exception); } } protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, AuthenticationException reason) throws ServletException, IOException { // SEC-112: Clear the SecurityContextHolder's Authentication, as the // existing Authentication is no longer considered valid SecurityContextHolder.getContext().setAuthentication(null); this.requestCache.saveRequest(request, response); this.authenticationEntryPoint.commence(request, response, reason); }
- RequestCacheAwareFilter를 통해 위에서 살펴본 캐시된 요청을 처리할 수 있음
- 캐시된 요청이 있다면 캐시된 요청을 처리하고, 캐시된 요청이 없다면 현재 요청을 처리함
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest wrappedSavedRequest = this.requestCache.getMatchingRequest((HttpServletRequest) request, (HttpServletResponse) response); chain.doFilter((wrappedSavedRequest != null) ? wrappedSavedRequest : request, response); }
ChannelProcessingFilter
전송 레이어 보안을 위해 SSL 인증서를 생성하고, 이를 Spring Boot 웹 어플리케이션에 적용한다. 이를 통해 웹 어플리케이션은 HTTPS 프로토콜을 통해 서비스 된다.
HTTP와 HTTPS
HTTP(Hyper Text Transfer Protocol)
- 인터넷 상에서 데이터를 주고 받기 위한 프로토콜
- 클라이언트와 서버가 주고 받는 데이터는 암호화되어 있지 않음
- 따라서 악의적인 데이터 감청, 데이터 변조의 가능성이 있음
HTTPS(Hyper Text Transfer Protocol Secure)
- HTTP 프로토콜의 암호화 버전
- 클라이언트와 서버가 주고받는 모든 데이터는 암호화되어 있음
- 데이터 암호화를 위해 SSL(Secure Sockets Layer)을 사용
- SSL은 Netscape가 개발했으며 SSL 3.0부터 TLS라는 이름으로 변경되었다. 일반적으로 SSL, TLS은 같은 의미를 지닌다. 그러나 SSL이란 용어가 더 많이 사용된다.
- SSL 암호화를 위해 SSL 인증서가 필요함
- 서버는 SSL인증서를 클라이언트에 전달함
- 클라이언트는 서버가 전달한 SSL인증서를 검증하고, 신뢰할 수 있는 서버인지 확인함
- 신뢰할 수 있는 서버라면 SSL 인증서의 공개키를 이용해 실제 데이터 암호화에 사용될 암호화키를 암호화하여 서버에 전달함
- 실제 데이터 암복호화하는 대칭키 방식
- 서버와 클라이언트 사이의 대칭키 공유를 위해 RSA암호화를 사용함
SSL 인증서 생성
keytool 도구를 이용해 임의로 SSL인증서를 생성할 수 있음 (keytool은 Java 설치 경로 bin 디렉토리 아래에 위치함)
물론 실제 서비스에는 사용할 수 없으며, 어디까지나 로컬 테스트 용도로만 활용해야함
keystore 만들기
keytool -genkey -alias [keystore 별칭] -keyalg RSA -storetype PKCS12 -keystore [keystore 파일]
$ keytool -genkey -alias prgrms_keystore -keyalg RSA -storetype PKCS12 -keystore prgrms_keystore.p12 키 저장소 비밀번호 입력: 새 비밀번호 다시 입력: 이름과 성을 입력하십시오. [Unknown]: localhost 조직 단위 이름을 입력하십시오. [Unknown]: prgrms 조직 이름을 입력하십시오. [Unknown]: prgrms 구/군/시 이름을 입력하십시오? [Unknown]: Seoul 시/도 이름을 입력하십시오. [Unknown]: Seoul 이 조직의 두 자리 국가 코드를 입력하십시오. [Unknown]: KR CN=localhost, OU=prgrms, O=prgrms, L=Seoul, ST=Seoul, C=KR이(가) 맞습니까? [아니오]: y
keystore에서 인증서 추출하기
keytool -export -alias [keystore 별칭] -keystore [keystore 파일] -rfc -file [인증서 파일]
$ keytool -export -alias prgrms_keystore -keystore prgrms_keystore.p12 -rfc -file prgrms.cer 키 저장소 비밀번호 입력: 인증서가 <prgrms.cer> 파일에 저장되었습니다.
trust-store 만들기
keytool -import -alias [trust keystore 별칭] -file [인증서 파일] -keystore [trust keystore 파일]
$ keytool -import -alias prgrms_truststore -file prgrms.cer -keystore prgrms_truststore.p12 키 저장소 비밀번호 입력: 새 비밀번호 다시 입력: 소유자: CN=localhost, OU=prgrms, O=prgrms, L=Seoul, ST=Seoul, C=KR 발행자: CN=localhost, OU=prgrms, O=prgrms, L=Seoul, ST=Seoul, C=KR 일련 번호: 37c3cb60 적합한 시작 날짜: Sat Sep 03 13:23:01 KST 2022 종료 날짜: Fri Dec 02 13:23:01 KST 2022 인증서 지문: SHA1: 72:85:A2:F5:C6:A3:F7:96:E0:2B:69:C6:44:7A:0F:6B:3A:0B:19:6E SHA256: A1:47:86:97:DE:53:93:80:53:99:9C:01:D4:4B:3D:A1:8C:A8:00:17:02:84:4A:80:66:40:1D:41:82:F9:75:30 서명 알고리즘 이름: SHA256withRSA 주체 공용 키 알고리즘: 2048비트 RSA 키 버전: 3 확장: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 29 E6 C4 87 D9 A6 B5 CA 59 89 A3 DC 91 34 9E 0D ).......Y....4.. 0010: 0F 33 2E 5B .3.[ ] ] 이 인증서를 신뢰합니까? [아니오]: y 인증서가 키 저장소에 추가되었습니다.
SSL 인증서 적용
prgrms_keystore.p12, prgrms_truststore.p12 2개 파일을 resources 디렉토리로 복사 후 application.xml 파일에 설정 추가
- port를 443으로 변경 (https 프로토콜의 디폴트 포트가 443)
- server.ssl 설정 추가
server: port: 443 ssl: enabled: true key-alias: prgrms_keystore key-store: classpath:prgrms_keystore.p12 key-store-password: prgrms123 key-password: prgrms123 trust-store: classpath:prgrms_truststore.p12 trust-store-password: prgrms123
웹 어플리케이션을 시작하면 443(https) 포트를 통해 서비스가 기동하는 것을 로그로 확인할 수 있음
웹브라우저 주소에 https://localhost를 입력
- 정상적으로 페이지 접근을 확인할 수 있음
- 로그인 로그아웃도 정상적으로 수행가능
- SSL 인증서가 유효하지 않기 때문에 경고가 뜸 (유효한 인증서라면 경고가 뜨지 않음)
- 브라우저마다 경고가 다를 수 있음 (크롬의 경우 HTTPS 연결이 사용되지 않았다는 경고메시지 발생)
Spring Security 설정하기
ChannelProcessingFilter 설정을 통해 HTTPS 채널을 통해 처리해야 하는 웹 요청을 정의할 수 있음
- FilterInvocationSecurityMetadataSource 클래스에 HTTPS 프로토콜로 처리해야 URL 정보가 담김
- 실제적인 처리를 ChannelDecisionManager 클래스로 위임함
HttpSecurity 클래스를 통해 ChannelProcessingFilter 세부 설정을 할 수 있음
@Override protected void configure(HttpSecurity http) throws Exception { http /** * HTTP 요청을 HTTPS 요청으로 리다이렉트 */ .requiresChannel() .anyRequest().requiresSecure() ; }
728x90'Backend > Spring' 카테고리의 다른 글