[SpringBoot] Swagger를 통한 REST 요청에 전역 jwt 인증 설정 하기

Livenow·2020년 9월 15일
4

안녕하세요,

이 포스트는 Jwt 설정, Swagger설정이 이미 완료된 서비스에서 활용할 수 있는 방법을 소개합니다.

문제점

그림과 같이 Swagger를 사용하면서 각 API마다 @ApiImplicitParam 을 통해 JWT를 받아왔었는데요,

서비스가 확대되고, jwt 토큰 인증이 필요한 API가 많아 질 수록 코드의 양과 가독성이 나빠 졌습니다.

또한 스웨거를 통한 테스트 진행시 각 API마다 jwt를 입력해야 한다는 번거로움 또한 있었습니다.

  • 그림과 같이 Authentication을 받는 모든 곳에 jwt를 입력해야 했음.

해결

Swagger version 2.9.2부터 이렇게 번거로운 일을 쉽게 바뀔 수 있는 기술이 들어갔다고 합니다.

소개에 앞서 결과부터 확인 해보겠습니다.

원래는 없었던 Autorize라는 버튼이 생겼습니다.

이를 클릭하면

이렇게 jwt token값을 입력받는 창이 뜹니다.

token값을 입력하고, Authorize를 클릭합니다.

API부분에선 jwt token값을 받던 부분이 사라지고, Excute시 토큰에 맞는 값을 가져올 수 있습니다.

어떻게 적용하는지 순서대로 알아보겠습니다.

첫 번째,

build.grdle에 swagger 의존성을 추가해줍니다.

dependencies {
implementation group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.9.2'
compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.9.2'
}

두 번째,

SwaggerConfig 클래스를 작성하고, 속성들을 추가해 줍니다.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.Arrays;
import java.util.List;

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .useDefaultResponseMessages(false)
                .select()
                .apis(RequestHandlerSelectors.basePackage("org.greenbyme.angelhack"))
                .paths(PathSelectors.ant("/api/**"))
                .build()
                .apiInfo(metaData())
                .securityContexts(Arrays.asList(securityContext()))
                .securitySchemes(Arrays.asList(apiKey()));

    }

    private ApiInfo metaData() {
        return new ApiInfoBuilder()
                .title("GreenByMe REST API")
                .description("Green by me, Green by earth(us)")
                .version("0.4.0")
                .termsOfServiceUrl("Terms of service")
                .contact(new Contact("Tae Jeong, Da hun", "https://github.com/GreenByMe/GreenByMe_Server", "xowjd41@naver.com"))
                .license("Apache License Version 2.0")
                .licenseUrl("https://www.apache.org/licenses/LICENSE-2.0")
                .build();
    }

    private ApiKey apiKey() {
        return new ApiKey("JWT", "jwt", "header");
    }

    private SecurityContext securityContext() {
        return springfox
                .documentation
                .spi.service
                .contexts
                .SecurityContext
                .builder()
                .securityReferences(defaultAuth()).forPaths(PathSelectors.any()).build();
    }

    List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        return Arrays.asList(new SecurityReference("JWT", authorizationScopes));
    }
}

여기서 중요한 것은

private ApiKey apiKey() {
        return new ApiKey("JWT", "jwt", "header");
}

부분인데, 가운데의 "jwt"를 유의하셔야합니다.

@Slf4j
@Component
public class AuthInterceptor implements HandlerInterceptor {

    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws UserException {
        final String token = request.getHeader("jwt");
        log.info("preHandle: " + token);
        if (!jwtTokenProvider.validateToken(token)) {
            throw new UserException("권한이 없습니다", ErrorCode.INVALID_ACCESS);
        }
        request.setAttribute("userID", jwtTokenProvider.getId(token));
        return true;
    }
}

저는 API에서 요청한 Authentication으로 받은 헤더값 token을 "jwt"라는 이름으로 지정을 했고(SwaggerConfig에서 지정),

이를 AuthInterceptor로 헤더의 "jwt"라는 값을 받아 처리하였기 때문에 이와 같이 동작을 할 수 있는 것 입니다.

세 번째

@ApiImplicitParams 부분을 주석처리 합니다.

결론

설정은 다 끝났습니다. 저장 후 빌드를 하시면, 위에서 확인했던 사진과 같이 동작함을 확인 할 수 있습니다.

profile
경험의 연장선

1개의 댓글

comment-user-thumbnail
2022년 8월 27일
return springfox
                .documentation
                .spi.service
                .contexts
                .SecurityContext

이부분 import springfox.documentation.spi.service.contexts.SecurityContext 로 깔끔하게 만들면 더 좋은 코드가 될 것 같아요!
좋은 글 감사합니다 😀

답글 달기