Spring Security Authentication 편의 클래스

텐저린티·2023년 9월 18일
0

Spring

목록 보기
6/6
post-thumbnail
post-custom-banner

무엇을 위한 것?

Spring Security 를 팀플에 적용하면서 필요해졌다.

SecurityContext 를 이용해서 인증 정보를 받아, HTTP 요청에서 사용자에 대한 추가 정보를 얻지 않도록 하고 싶었다.

결론으로 Long userId 이거를 로직에서 지워버리고 싶었다.

하지만, JWT 필터를 통해 SecurityContext 를 만들어준 다음에는 Authentication 객체로 받게 된다.

이렇게 되면, 다른 팀원들이 인증 정보에서 무언가를 꺼내서 쓰려면 매번 파싱해주고, 형변환해주고 그래야 한다.

중복코드가 엄청 발생할 것.

그래서 편하게 인증정보를 꺼내쓸 수 있는 클래스를 만들어서 주고 싶었다.

팀원들이 Security 프레임워크를 모르더라도 사용할 수 있도록 하려고 JwtSimpleAuthentication 이라는 클래스를 만들었다.

인증 정보 코드

  • 이게 만든 코드
  • 해당 클래스를 빈으로 주입받아서 사용하면 된다.
  • 필요한 정보를 getter 메소드로 받아서 사용한다.
@Component
public class JwtSimpleAuthentication {

    public Email getEmail() {
        JwtAuthenticationPrincipal principal = (JwtAuthenticationPrincipal) SecurityContextHolder.getContext()
                .getAuthentication()
                .getPrincipal();
        return new Email(principal.email());
    }

    public String getEmailAddress() {
        JwtAuthenticationPrincipal principal = (JwtAuthenticationPrincipal) SecurityContextHolder.getContext()
                .getAuthentication()
                .getPrincipal();
        return principal.email();
    }

    public Role getRole() {
        List<GrantedAuthority> authorities = (List<GrantedAuthority>) SecurityContextHolder.getContext()
                .getAuthentication()
                .getAuthorities();
        return Role.valueOf(authorities.get(0)
                .getAuthority());
    }
}
  • 이거는 의존성이 생긴 Principal 커스텀 클래스
  • 이 코드 때문에 SecurityContext 에 들어있는 Authentication 의 Principal 에는 email 정보만 들어있다.
  • 나머지 credentials 나 authorities 는 제자리에 두었다.
public record JwtAuthenticationPrincipal(String email) {

    public JwtAuthenticationPrincipal {
        Assert.isTrue(isNotEmpty(email), "email must be provided.");
    }
}

사용 코드

  • 실제 로직에서는 스프링 빈으로 주입받아서 사용하면 됨
  • 컨트롤러, 서비스 둘 다 사용 가능
@Component
public class Service {

		private final JwtSimpleAuthentication authentication;

		public Service(JwtSimpleAuthentication authentication) {
				this.authentication = authentication;
		}

    public String test() {
        Email email = authentication.getEmail();
        Role role = authentication.getRole();
        return authentication.getEmailAddress();
    }

}

테스트 코드

  • JwtSimpleAuthentication 객체를 사용한 메소드 테스트할 때가 문제였습니다.
  • @WithMockCustomUser 을 사용하면 이제 처리됩니다.
  • 다만, 테스트 데이터가 아래 어노테이션 default 값으로 고정됩니다.
    • 좀 더 확장성 있는 방법을 찾아볼게요
@SpringBootTest
@Transactional
class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    @WithMockCustomUser
    void test() {
        String test = userService.test();
        System.out.println(test);
    }
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@WithSecurityContext(factory = WithMockCustomUserSecurityContextFactory.class)
public @interface WithMockCustomUser {

    String email() default "danaka@naver.com";

    String role() default "USER";

}

유의점

  • @WithMockCustomUser 어노테이션 사용 시 이메일과 역할이 고정된다.
  • 필요하다면 기본값을 바꿔서 테스트하면 된다.
profile
개발하고 말테야
post-custom-banner

0개의 댓글