포스트

2022-03-30-TIL

Today I Learned

Spring RestTemplate Logging

RestTemplate으로 외부와 통신하는 부분에서 request body나 response body를 직접 로깅하고 싶다면 인터셉터를 추가해서 잡아서 보아야 한다.

logging.level.org.springframework.web.client.RestTemplate=DEBUG만 켜도 요청 URL과 응답 상태 정도는 볼 수 있지만, 바디까지 보려면 ClientHttpRequestInterceptor를 구현해 RestTemplate에 등록한다. 인터셉터에서 요청 바디는 byte[]로 바로 받을 수 있지만, 응답 바디는 스트림이라 한 번 읽으면 이후 메시지 컨버터가 읽을 수 없다. 그래서 BufferingClientHttpRequestFactory로 감싸 응답을 메모리에 버퍼링한 뒤 읽는다. 큰 응답에서는 메모리를 쓰므로 운영에서는 로그 레벨로 켜고 끌 수 있게 해 둔다.

1
2
3
RestTemplate restTemplate = new RestTemplate(
        new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
restTemplate.getInterceptors().add(new LoggingInterceptor());
  • https://www.baeldung.com/spring-resttemplate-logging
  • https://wildeveloperetrain.tistory.com/102

Maven

인텔리제이에서 maven 탭을 열어서 package 우클릭 > Profile … 선택 후, Run Configurations에서 우클릭 > Profile …

1
clean package jib:build -DskipTests -f pom.xml

IntelliJ의 Maven Run Configuration에 위 명령(골)을 저장해 두면 매번 터미널에 입력하지 않고 테스트를 건너뛴 패키징과 Jib 이미지 빌드, 푸시를 한 번에 실행할 수 있다. -f pom.xml은 사용할 POM 파일을 지정하는 옵션이고, 프로파일을 함께 지정하려면 -P<profile>을 붙인다.


LDAP

Lightweight Directory Access Protocol은 DAP의 무거운 기능을 최소화하고 필수 기능에 집중한 경량화된 DAP이다. 이는 일반적인 DB보다 유연성 있는 쿼리를 제공함으로써 특정 검색에 대한 성능이 빠르다.

DAP는 X.500 디렉터리 표준의 접근 프로토콜이고, LDAP은 이를 TCP/IP 위에서 가볍게 쓸 수 있게 만든 것으로 현재 버전은 RFC 4511에 정의된 LDAPv3다. 데이터는 트리 구조(DIT)로 저장되고, 각 항목은 cn=kim,ou=dev,dc=example,dc=com 같은 DN(Distinguished Name)으로 식별된다. 읽기와 검색이 대부분이고 쓰기는 드문 사용자, 조직, 권한 정보에 최적화되어 있어서, 사내 계정을 한곳에서 관리하고 여러 시스템이 같은 계정으로 로그인하게 하는 통합 인증에 많이 쓰인다. Active Directory도 LDAP을 지원한다. 기본 포트는 389이고, TLS를 쓰는 LDAPS는 636이다.

  • https://hec-ker.tistory.com/319
  • https://ldap.or.kr/ldap-%EC%9D%B4%EB%9E%80/
  • https://always-kimkim.tistory.com/entry/X500%EA%B3%BC-LDAP-%EC%A0%95%EB%A6%AC
  • https://velog.io/@kante/LDAP-%EC%9D%B4%EB%9E%80
  • https://yongho1037.tistory.com/796
  • https://catsbi.oopy.io/c0a4f395-24b2-44e5-8eeb-275d19e2a536

Spring Container

링크된 글은 스프링이 의존성 주입을 리플렉션으로 처리하는 과정을 다룬다. 컨테이너는 컴포넌트 스캔이나 설정 클래스로 빈 정의를 모은 뒤, 클래스의 생성자나 @Autowired가 붙은 필드, 메서드를 리플렉션으로 찾아 의존 빈을 넣어 준다. private 필드에도 주입할 수 있는 것은 리플렉션으로 접근 제한을 우회하기 때문이다.

  • https://taes-k.github.io/2021/05/23/spring-di-reflection/

생성자 주입 vs @Autowired

필드에 @Autowired를 붙이는 필드 주입보다 생성자 주입이 권장되는 이유는 다음과 같다.

  • 의존성을 final로 선언할 수 있어 객체가 생성된 뒤 바뀌지 않는다.
  • 필수 의존성이 빠지면 객체를 만들 수 없으므로 누락을 바로 알 수 있다.
  • 스프링 없이도 new로 생성하면서 테스트용 객체를 넣을 수 있다.
  • 생성자 파라미터가 많아지면 클래스의 책임이 너무 많다는 신호가 바로 보인다.

Spring 4.3부터 생성자가 하나뿐이면 @Autowired를 생략할 수 있어서, Lombok의 @RequiredArgsConstructor와 함께 쓰면 코드도 짧다.

  • https://madplay.github.io/post/why-constructor-injection-is-better-than-field-injection

서블릿 필터

서블릿 필터는 서블릿 컨테이너 수준에서 DispatcherServlet에 요청이 도달하기 전과 응답이 나간 후에 동작하는 컴포넌트다. doFilter에서 chain.doFilter(request, response)를 호출해야 다음 필터나 서블릿으로 넘어간다. 인코딩 설정, 로깅, 인증처럼 모든 요청에 공통으로 적용할 처리에 쓴다. 스프링의 HandlerInterceptor는 DispatcherServlet 안에서 컨트롤러 호출 전후에 동작해 핸들러 정보에 접근할 수 있다는 점이 다르다. Spring Security도 필터 체인으로 구현되어 있다.

  • https://12teamtoday.tistory.com/141

Spring Security

Spring Security는 인증(누구인지)과 인가(무엇을 할 수 있는지)를 처리하는 프레임워크다. 서블릿 필터 체인(SecurityFilterChain)에 여러 보안 필터를 배치해 요청이 컨트롤러에 도달하기 전에 인증 정보를 확인하고, 인증된 사용자 정보는 SecurityContextHolder에 저장한다. 세션 기반 로그인 외에 JWT 같은 토큰 인증을 쓰려면 토큰을 검증하는 필터를 추가하거나 OAuth2 Resource Server 기능을 쓴다. 메서드 단위 권한 검사는 아래의 메서드 보안 애너테이션으로 한다.

  • https://docs.spring.io/spring-security/reference/getting-spring-security.html
  • https://devuna.tistory.com/59
  • https://www.baeldung.com/spring-security-method-security
  • https://spring.io/guides/gs/securing-web/
  • https://imbf.github.io/spring/2020/06/29/Spring-Security-with-JWT.html

JWT Classes

org.springframework.security.oauth2.jwt.Jwt는 Spring Security OAuth2 Resource Server에서 디코딩과 검증을 마친 JWT를 나타내는 클래스다. 헤더, 클레임, 원본 토큰 값, 발급 시각과 만료 시각을 담고 있고, getSubject(), getClaim("name")처럼 클레임을 꺼낼 수 있다. JwtDecoder가 토큰 문자열을 서명 검증 후 이 객체로 바꾼다.

  • https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/oauth2/jwt/Jwt.html

@Configuration

@Configuration은 @Bean 메서드로 빈을 정의하는 설정 클래스에 붙인다. Javadoc에 따르면 설정 클래스는 실행 중에 CGLIB 하위 클래스로 확장되어, 사용자 코드에서 @Bean 메서드를 직접 호출해도 같은 싱글톤 인스턴스를 돌려준다. 이 동작은 proxyBeanMethods 속성(5.2부터, 기본값 true)으로 제어하며, false로 두면 프록시를 만들지 않는 “lite mode”가 되어 @Configuration을 뗀 것과 같아진다. 하위 클래스를 만들어야 하므로 설정 클래스는 final일 수 없다.

  • https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Configuration.html
  • https://castleone.tistory.com/2
  • https://mangkyu.tistory.com/75

@EnableWebSecurity

@EnableWebSecurity는 @Configuration 클래스에 붙여 Spring Security의 웹 보안 설정을 활성화한다. 보통 WebSecurityConfigurerAdapter를 상속해 configure(HttpSecurity)를 재정의하는 방식으로 설정해 왔지만, Spring 블로그에 따르면 Spring Security 5.7.0-M2에서 이 클래스가 deprecated되었고, SecurityFilterChain 빈을 직접 등록하는 컴포넌트 기반 설정이 권장된다. Spring Boot는 Spring Security가 클래스패스에 있으면 이 설정을 자동으로 적용한다.

  • https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/config/annotation/web/configuration/EnableWebSecurity.html

@EnableGlobalMethodSecurity

@EnableGlobalMethodSecurity는 메서드 단위 보안을 켜는 애너테이션으로, prePostEnabled = true이면 @PreAuthorize/@PostAuthorize, securedEnabled = true이면 @Secured, jsr250Enabled = true이면 @RolesAllowed를 쓸 수 있다. 현재 Spring Security 문서는 이 애너테이션을 deprecated로 표시하고 @EnableMethodSecurity로 옮기라고 권한다. 새 애너테이션은 @PreAuthorize 계열을 기본으로 켜고, @Secured는 securedEnabled = true로 따로 켠다.

  • https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/annotation/method/configuration/EnableGlobalMethodSecurity.html

@Secured

@Secured("ROLE_ADMIN")은 메서드를 호출하려면 지정한 권한 중 하나가 있어야 한다는 선언이다. 역할 문자열만 지정할 수 있고 SpEL 식은 쓸 수 없으므로, “본인 글만 수정 가능”처럼 파라미터를 참조하는 조건이 필요하면 @PreAuthorize("hasRole('ADMIN') or #id == principal.id")를 쓴다. 메서드 보안은 AOP 프록시로 동작하므로 같은 클래스 안에서의 자기 호출에는 적용되지 않는다.

  • https://docs.spring.io/spring-security/site/docs/3.2.8.RELEASE/apidocs/org/springframework/security/access/annotation/Secured.html
  • https://copycoding.tistory.com/278

@CSRF

CSRF(Cross-Site Request Forgery)는 사용자가 로그인한 상태에서 공격자의 페이지가 사용자 브라우저로 하여금 의도하지 않은 요청을 보내게 하는 공격이다. 브라우저가 쿠키를 자동으로 붙이기 때문에 세션 쿠키 기반 인증에서 문제가 된다. Spring Security는 기본으로 CSRF 보호를 켜고, 상태를 바꾸는 요청(POST, PUT, DELETE 등)에 서버가 발급한 CSRF 토큰이 함께 와야 처리한다. 쿠키를 쓰지 않고 Authorization 헤더로 토큰을 보내는 무상태 REST API는 이 공격의 전제가 성립하지 않아 csrf().disable()을 하는 경우가 많다. 참고로 @CSRF라는 애너테이션은 없고, 설정은 HttpSecurity의 csrf()로 한다.

  • https://velog.io/@woohobi/Spring-security-csrf%EB%9E%80

@Order

@Order는 같은 타입의 빈이 여러 개일 때 순서를 정한다. 값이 작을수록 우선순위가 높고, Ordered.HIGHEST_PRECEDENCE가 가장 먼저다. List<Filter>처럼 컬렉션으로 주입받을 때의 순서, 여러 @ControllerAdvice나 AOP Aspect의 적용 순서, Spring Security에서 여러 SecurityFilterChain을 둘 때 어느 체인이 먼저 매칭될지를 정하는 데 쓰인다. 빈 생성 순서를 정하는 것은 아니라는 점에 주의해야 한다.

  • https://www.baeldung.com/spring-order
  • https://unhosted.tistory.com/79
참고한 자료외부 출처 4

외부 출처

이 글은 저작권자의 CC BY 4.0 라이선스를 따릅니다.

댓글

아직 댓글이 없습니다