본문 바로가기
Dev/Spring

Swagger를 통한 REST 요청에 jwt 인증키 포함시키기

by 돈코츠라멘 2020. 1. 22.
@Configuration
class WebSecurityConfig : WebMvcConfigurer {

    override fun addInterceptors(registry: InterceptorRegistry) {
        registry.addInterceptor(JwtInterceptor())
            .addPathPatterns("/v1/api/*")
            .excludePathPatterns("/v1/api/signin")
    }

}

Spring Boot 프로젝트에 위와같이 jwt 인증을 추가한 후 Swagger를 통해 api들을 테스트하려는데, 아래와 같이 jwt 인증에러가 발생한다.

2020-01-21 10:24:03,184 [https-jsse-nio-19932-exec-7] ERROR org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[dispatcherServlet]: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: request.getHeader(JwtTokenProvider.HEADER_AUTH) must not be null] with root cause
java.lang.IllegalStateException: request.getHeader(JwtTokenProvider.HEADER_AUTH) must not be null

Swagger를 통해 한 호출에 jwt 인증키가 포함되어 있지 않아서 발생하는 에러였다. 로그인(/v1/api/signin)을 제외한 모든 REST 요청에 jwt 인증키를 확인하는 인터셉터를 추가하였으니 이 에러가 발생하는것이 당연하다.

하지만 api를 테스트할 때 마다 매번 토큰을 발급받아서 포함시킬수도 없는 노릇! 그래서 Swagger를 통한 api 요청의 헤더에 임시로 인증키를 만들어서 포함시키는 방식으로 해결하였다. securitySchemessecurityContexts를 설정하여 해결하는 방법도 있었지만, 어차피 배포버전에는 swagger를 포함시키지 않을 예정이라 심플하게 헤더에 인증키를 넣어버렸다.

JwtTokenProvider

토큰을 발급하고 검증하는 로직은 기존에 구현했던 코드를 그대로 사용한다. 참고로 내가 사용한 코드는 아래와 같다.

class JwtTokenProvider {

    companion object {

        private val secretKey: String = "auth"
        private val expiredMin: Int = 10

        val HEADER_AUTH = "authorization"

        fun createToken(id: String): String = Jwts.builder()
            .setId(id)
            .setIssuedAt(Date())
            .setNotBefore(Date())
            .setExpiration(Date(System.currentTimeMillis() + expiredMin.toInt() * 60 * 1000))
            .signWith(SignatureAlgorithm.HS256, secretKey)
            .compact()

        fun verifyToken(strToken: String): Boolean {
            return try {
                Jwts.parser().setSigningKey(secretKey).parseClaimsJws(strToken).body
                true
            } catch (e: Exception) {
                false
            }
        }

        fun getClaims(strToken: String): Claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(strToken).body
    }

}

SwaggerConfig

Header Parameter를 만들고 globalOperationParameters로 추가한다. 이 Parameter의 value에 새로운 jwt 토큰을 발급해서 추가한다.

@Configuration
@EnableSwagger2
class SwaggerConfig {

    @Bean
    fun api(): Docket = Docket(DocumentationType.SWAGGER_2)
        .useDefaultResponseMessages(false)
        .apiInfo(apiInfo())
        .select()
        .apis(RequestHandlerSelectors.withClassAnnotation(RestController::class.java))
        .paths(PathSelectors.any())
        .build()
        .globalOperationParameters(authParam()) // 추가


    fun apiInfo(): ApiInfo = ApiInfoBuilder() // 생략
        .build()

    private fun authParam(): List<Parameter> {
        val parameter = ParameterBuilder()
            .name(JwtTokenProvider.HEADER_AUTH)
            .modelRef(ModelRef("string"))
            .parameterType("header")
            .defaultValue(JwtTokenProvider.createToken("swagger"))
            .required(true)
            .build()
        return listOf(parameter)
    }
}

Test!

Application을 재기동한 후 api를 테스트하려 할 때 이전과는 달리 header parameter로 이미 jwt 토큰이 포함되어 있다.

댓글