본문 바로가기
Study/SpringBoot

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

by 검프 2021. 1. 25.

안녕하세요,

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

문제점

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

https://user-images.githubusercontent.com/48986787/93177085-328fc400-f76d-11ea-8c1b-d94bfb8eff24.png

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

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

https://user-images.githubusercontent.com/48986787/93177317-99ad7880-f76d-11ea-8f7d-f1cb7f7c6f75.png

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

해결

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

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

https://user-images.githubusercontent.com/48986787/93177874-661f1e00-f76e-11ea-8679-cec8ba8f2b73.png

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

이를 클릭하면

https://user-images.githubusercontent.com/48986787/93177985-91a20880-f76e-11ea-8e1f-65772d001822.png

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

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

https://user-images.githubusercontent.com/48986787/93178123-bf874d00-f76e-11ea-9fe6-6c7375a5e891.png

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"라는 값을 받아 처리하였기 때문에 이와 같이 동작을 할 수 있는 것 입니다.

세 번째

https://user-images.githubusercontent.com/48986787/93179326-79cb8400-f770-11ea-9867-21056de0f76b.png

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

결론

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

댓글