경희대학교 컴퓨터공학부 하계 리턴 백엔드(스프링부트) 스터디 7주차 - 트랙장 최현영
들어가기 앞서
인증[Authentication]
인가[Authorization]
주체[Principle]
JWT[Json Web Token]
xxxxx.yyyyyy.zzzz 형식의 String 타입
xxxxx : Header[헤더]
{
"alg": "HS256",//해싱 알고리즘
"typ": "JWT" //토큰 타입
}
yyyyy : Payload[내용]
iss
: 토큰 발급자 주체 (issuer)sub
: 토큰 제목 (subject)aud
: 토큰을 발급받을 대상자 (audience)exp
: 토큰의 만료시간 (expiraton) → 시간으로 설정된다.nbf
: Not Before 를 의미하는데, exp와 마찬가지로 시간으로 설정되는데, 설정한 날짜가 지나기 전까지는 토큰이 유효하지 않는다. → 사용 불가 상태에 만든다는 것iat
: 토큰이 발급된 시간 (issued at)jti
: JWT의 고유 식별자[Unique ID]로서, 중복을 방지하고자 고유한 토큰임을 증명하기 위해 사용됨⇒ 여기서 어떤 것을 Payload에 담을지는 Optional이다.
zzzzz : Signature[서명]
스프링 시큐리티
서블릿 필터[Filter]
앞전에 1주차에서 살펴봤던 서블릿의 내용을 상기시켜 보자
다시말해, 서블릿은 클라이언트의 요청을 처리하고 결과를 반환하는 응답을 생성하는 Java 클래스이다.
서블릿 컨테이너에서 서블릿[서블릿 객체]을 관리하며, 스프링부트에 내장되어있는 톰캣이 서블릿 컨테이너가 내장되어 있는 WAS라고 배웠다.
그렇다면 필터는 뭐냐?
필터는 서블릿에 도달하기 전에 HTTP 요청에 대한 필터링을 수행하는, 서블릿에서 정의된 인터페이스
즉, 서블릿 컨테이너가 생성한 HttpServletRequest에는 클라이언트가 요청한 내용이 담겨져 있는데, 서블릿에 전달하기 전 우선적으로 검사하여 필터링[여과] 하겠다는 것이다.
개발자가 직접 필터를 생성하여 요청에 대한 필터링을 수행할 수 있는데, 일반적으로 Filter 인터페이스를 상속받아 구현한다.
public class CustomFilter implements Filter {
}
Filter 인터페이스는 3가지 메소드를 지니고 있다.
실습
package com.rebe.returnstudy.Configuration;
import jakarta.servlet.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Slf4j
@Component
public class CustomFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
log.info("Return Study Custom 필터 생성");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("현재, Custom Filter 통과중");
}
@Override
public void destroy() {
Filter.super.destroy();
log.info("Return Study Custom 필터 소멸");
}
}
여기서 ServletRequest/Reponse 객체를 입력 파라미터로 받는데, 이는 HttpServletRequest가 상속을 받고 있기 때문이다. ⇒ 다형성을 활용한 것.(polymorphism)
필터를 사용하기 위해서는 빈으로 등록해야한다. 필터를 등록하기 위해서는 FilterRegistrationBean으로 설정해 주어야한다.
package com.rebe.returnstudy.Configuration;
import jakarta.servlet.Filter;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@RequiredArgsConstructor
@Configuration
public class Config {
private final CustomFilter customFilter;
@Bean
public FilterRegistrationBean<Filter> firstFilterRegister() {
FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(customFilter); //필터 등록
return registrationBean;
}
}
실행 하였다면, 빌드 시점에 “Return Study Custom 필터 생성” 이라는 log문이 찍히는 것을 볼 수 있다. 즉, 필터는 스프링 컨테이너가 관리하는 빈으로 등록되었고, WAS가 실행[빌드]되었을 때, init()이라는 메서드가 호출되며 필터가 Dispatcher Servlet 앞에 놓여진 것이다.
어떤 요청을 보내도, 필터를 반드시 통과하게 되어, “현재 Custom Filter를 통과중”이라는 로그문이 출력된 모습을 볼 수 있다. → 즉, 필터를 통과할 때, doFilter() 메서드가 호출된 것이다.
스프링 빌드를 종료할 때[8080 포트의 PID를 죽일때] “Return Study Custom 필터 소멸이라는 로그문이 출력되며 동적으로 생성한 필터가 소멸되었다. → 종료될 때, destory() 메서드가 호출되었다.
필터 체인[Filter Chain]
ApplicationFilterChain
**은 필터 체인을 구성하고 필터들 사이에서 요청과 응답을 전달하는 역할을 담당한다.
DelegatingFilterProxy
앞에서 우리가 만든 CustomFilter 역시, DelegatingFilter Proxy 덕분에 스프링 컨테이너에서 빈으로 등록되어 서블릿 컨테이너가 관리하는 필터를 스프링에서 관리할 수 있게 된 것.
DelegatingFilterProxy 내부에는 스프링 컨테이너가 관리하는 빈 객체의 필터인 ‘**필터체인 프록시’[FilterChain Proxy]**를 지니고 있는데, 스프링 시큐리티에서 제공하는 Security 필터 체인(Security Filter Chain)을 요청 URI에 따라 다르게 사용할 수 있도록 해준다. 즉, 여러개의 필터 체인을 구성할 수 있다.
SecurityFilterChain
**은 스프링 시큐리티(Spring Security)에서 제공하는 필터들을 개발자가 선택하고, 구성하기 위한 인터페이스이다.⇒ 정리해 보자면, 스프링 시큐리티가 제공하는 필터들을 바탕으로 SecurityFilter Chain으로 구성한 뒤, Deligating Proxy 내부에 존재하는 FilterChainProxy가 이 Security Filter Chain을 ‘스프링 빈’으로 등록해서 스프링 컨테이너가 사용할 수 있도록 해줌
⇒ 즉, SecurityContext를 가져오거나 저장하는 역할을 한다.