개요
@MockMember
어노테이션의 경우- 테스트 환경에서 시큐리티에서 인증된 사용자의 모의(Mock) 하기 위해서 사용된다.
- 일반적인 경우에는
@WithMockUser
등의 사용자 모킹을 하면 되겠지만..
- 나는
@WithMockUser
가 사용을 해도 일단 인증을 받아 올 수가 없었다.
- 그래서 검색하던 중에 발견한 것이 별도의 커스텀 어노테이션을 통하여
SecurityContext
안의 보안 객체를 커스텀하게 테스트코드에서만 채우는 방법이 있다는 것을 발견했다.
어노테이션 코드
@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = MockMemberSecurityContextFactory.class)
public @interface MockMember {
String email() default "mockMember3410@gmail.com";
Member.Role role() default Member.Role.ROLE_CUSTOMER;
Member.Status status() default Member.Status.ACTIVE;
}
Retention
- 런타임으로 설정
- 어노테이션이 런타임단계에서도 유효해야 테스트가 가능하기에 설정
WithSecurityContext
- 별도의 커스텀된 SecurityContextFactory 를 탈 수 있도록 설정이 가능한 어노테이션이다.
- 테스트 중에 Security Context 를 쉽게 모의할 수 있도록 도와주는 어노테이션이다.
@Target({ ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface WithSecurityContext { /** * The {@link WithUserDetailsSecurityContextFactory} to use to create the * {@link SecurityContext}. It can contain {@link Autowired} and other Spring * annotations. * @return */ Class<? extends WithSecurityContextFactory<? extends Annotation>> factory(); /** * Determines when the {@link SecurityContext} is setup. The default is before * {@link TestExecutionEvent#TEST_METHOD} which occurs during * {@link org.springframework.test.context.TestExecutionListener#beforeTestMethod(TestContext)} * @return the {@link TestExecutionEvent} to initialize before * @since 5.1 */ TestExecutionEvent setupBefore() default TestExecutionEvent.TEST_METHOD; }
- 기본적으로 테스트메서드에 초점이 맞춰져있는 어노테이션임을 알 수 있다.
MockMemberSecurityContextFactory
public class MockMemberSecurityContextFactory implements WithSecurityContextFactory<MockMember> {
@Override
public SecurityContext createSecurityContext(MockMember annotation) {
Jwt jwt = Jwt.withTokenValue("token")
.header("alg", "hs-256")
.claim("sub", annotation.email())
.build();
JwtAuthenticationToken authentication = new JwtAuthenticationToken(jwt, null, null);
SecurityContext context = org.springframework.security.core.context.SecurityContextHolder.createEmptyContext();
context.setAuthentication(authentication);
return context;
}
}
팩토리 메서드상의 상속의 근거
@Target({ ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface WithSecurityContext {
/**
* The {@link WithUserDetailsSecurityContextFactory} to use to create the
* {@link SecurityContext}. It can contain {@link Autowired} and other Spring
* annotations.
* @return
*/
Class<? extends WithSecurityContextFactory<? extends Annotation>> factory();
- 위 WithSecurityContext 상에서 Class 의 타입은 WithSecurityContextFactory 의 서브 클래스이여 함을 알 수 있다. ⇒
? extends WithSecurityContextFactory
- 위 정보를 토대로
public class MockMemberSecurityContextFactory implements WithSecurityContextFactory<MockMember> {
- 를 설정할 수 있는 근거가 발생한다.
WithSecurityContextFactory 의 < 내 커스텀 어노테이션 extends Annotation > 이 성립
JWT AUTHENTICATION TOKEN 의 테스트 모킹
- 기본적으로 내 프로젝트에서 oauth2 resource server 라이브러리를 임포트중이다.
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
- JWT 토큰을 기본 전략으로 삼았기 때문에,
- 전략의 근거
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.cors(); // cors 허용 ... .oauth2ResourceServer( OAuth2ResourceServerConfigurer::jwt) // oauth2ResourceServer 를 JWT 방식으로 설정 ... }
- jwt 방식으로 OAuth2ResourceServer 를 설정함
- 다른 프로젝트의 일반적인 예시들과 조금 다름.
- UsernamePasswordAuthenticationToken 말고
- 항상 JwtAuthenticationToken 을 기본 보안 객체로 받게됨.
- 전략의 근거
- JWT 토큰을 기본 전략으로 삼았기 때문에,
- 그래서..
MockMemberSecurityContextFactory
@Override
public SecurityContext createSecurityContext(MockMember annotation) {
Jwt jwt = Jwt.withTokenValue("token")
.header("alg", "hs-256")
.claim("sub", annotation.email())
.build();
JwtAuthenticationToken authentication = new JwtAuthenticationToken(jwt, null, null);
SecurityContext context = org.springframework.security.core.context.SecurityContextHolder.createEmptyContext();
context.setAuthentication(authentication);
return context;
}
- 이런식으로 모의 JWT 객체를 만들고
JwtAuthenticationToken
을 만들어 보안 컨텍스트에 채워넣어줘야
- 정상적인 JWT 엑세스 토큰을 통한 인증 → 로직 → 반환 의 일련이 흐름이 가능하다.
Uploaded by N2T
'자바 > 리팩토링' 카테고리의 다른 글
[프로젝트] 모킹 클래스의 private 필드에 테스트 데이터 삽입 방법 - `ReflectionTestUtils` (0) | 2024.02.04 |
---|---|
[프로젝트] 시큐리티 권한 체크 `@PreAuthorize 또는 @Secured` (0) | 2024.02.02 |
[리팩토링] properties 파일을.. yaml 로 변경하면서 생긴, gradle 문제 해결 (0) | 2024.01.19 |
[리팩토링] Swagger UI + Spring RestDocs 적용기 (0) | 2024.01.05 |
스웨거와 RestDocs 의 장점을 뽑아내기 (0) | 2024.01.03 |