Spring REST API로 Swagger 2 설정

보안 상단

방금 Spring Security 5의 새로운 OAuth2 스택에 초점을 맞춘 전체 자료를 포함하여 새로운 Learn Spring Security 과정을 발표했습니다.

>> 코스 레스트 Top을 확인하세요

방금 Spring 5 및 Spring Boot 2의 기본 사항에 초점을 맞춘 새로운 Learn Spring 과정을 발표했습니다 .

>> 과정 확인

1. 개요

요즘에는 프런트 엔드 및 백 엔드 구성 요소가 종종 웹 응용 프로그램을 분리합니다. 일반적으로 API를 프런트 엔드 구성 요소 또는 타사 앱 통합을위한 백 엔드 구성 요소로 노출합니다.

이러한 시나리오에서는 백엔드 API에 대한 적절한 사양이 있어야합니다. 동시에 API 문서는 유익하고 읽기 쉬우 며 따르기 쉬워야합니다.

또한 참조 문서는 API의 모든 변경 사항을 동시에 설명해야합니다. 이를 수동으로 수행하는 것은 지루한 작업이므로 프로세스 자동화가 불가피했습니다.

이 튜토리얼에서는 Swagger 2 사양의 Springfox 구현을 사용하여 Spring REST 웹 서비스 용 Swagger 2를 살펴 봅니다 .

Swagger에 익숙하지 않은 경우이 자습서를 계속하기 전에 웹 페이지를 방문하여 자세히 알아보십시오.

2. 대상 프로젝트

우리가 사용할 REST 서비스의 생성은이 기사의 범위에 포함되지 않습니다. 이미 적합한 프로젝트가 있으면 사용하십시오. 그렇지 않은 경우 다음 링크를 시작하는 것이 좋습니다.

  • Spring 4 및 Java Config 기사로 REST API 빌드
  • RESTful 웹 서비스 구축

3. Maven 종속성 추가

위에서 언급했듯이 Swagger 사양의 Springfox 구현을 사용할 것입니다. 최신 버전은 Maven Central에서 찾을 수 있습니다.

Maven 프로젝트에 추가하려면 pom.xml 파일에 종속성이 필요 합니다.

 io.springfox springfox-swagger2 2.9.2 

3.1. 스프링 부트 종속성

Spring Boot 기반 프로젝트 의 경우 단일 springfox-boot-starter 종속성 을 추가하는 것으로 충분합니다 .

 io.springfox springfox-boot-starter 3.0.0 

4. Swagger 2를 프로젝트에 통합

4.1. 자바 구성

Swagger의 구성은 주로 Docket Bean을 중심으로합니다 .

@Configuration public class SpringFoxConfig { @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.any()) .build(); } }

Docket 빈을 정의한 후 select () 메서드는 ApiSelectorBuilder 인스턴스를 반환하여 Swagger가 노출하는 엔드 포인트를 제어하는 ​​방법을 제공합니다.

우리는 선택을위한 조건을 구성 할 수 있습니다 RequestHandler를 의 도움으로들 RequestHandlerSelectorsPathSelectors . 둘 다에 대해 any () 를 사용하면 Swagger를 통해 전체 API에 대한 문서를 사용할 수 있습니다.

4.2. Spring Boot없는 구성

일반 Spring 프로젝트에서는 Swagger 2를 명시 적으로 활성화해야합니다. 이렇게하려면 구성 클래스 에서 @ EnableSwagger2WebMvc 를 사용해야합니다 .

@Configuration @EnableSwagger2WebMvc public class SpringFoxConfig { }

또한 Spring Boot가 없으면 리소스 핸들러를 자동 구성 할 수 없습니다.

Swagger UI는 WebMvcConfigurerAdapter 를 확장하고 @EnableWebMvc 로 주석이 달린 클래스의 일부로 구성해야하는 리소스 집합을 추가합니다 .

@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("swagger-ui.html") .addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/"); }

4.3. 확인

Springfox가 작동하는지 확인하기 위해 브라우저에서 다음 URL을 방문 할 수 있습니다.

// localhost : 8080 / spring-security-rest / api / v2 / api-docs

그 결과 많은 수의 키-값 쌍이 포함 된 JSON 응답이 생성되어 사람이 읽을 수 없습니다. 다행히 Swagger는 이러한 목적으로 Swagger UI 를 제공합니다 .

5. Swagger UI

Swagger UI는 Swagger에서 생성 한 API 문서와의 사용자 상호 작용을 훨씬 쉽게 만들어주는 기본 제공 솔루션입니다.

5.1. Springfox의 Swagger UI 활성화

Swagger UI를 사용하려면 Maven 종속성을 추가해야합니다.

 io.springfox springfox-swagger-ui 2.9.2 

이제 다음을 방문하여 브라우저에서 테스트 할 수 있습니다.

// localhost : 8080 / your-app-root / swagger-ui /

그런데 우리의 경우 정확한 URL은 다음과 같습니다.

// localhost : 8080 / spring-security-rest / api / swagger-ui /

결과는 다음과 같아야합니다.

5.2. Swagger 문서 탐색

Within Swagger’s response is a list of all controllers defined in our application. Clicking on any of them will list the valid HTTP methods (DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT).

Expanding each method provides additional useful data, such as response status, content-type, and a list of parameters. It is also possible to try each method using the UI.

Swagger’s ability to be synchronized with our code base is crucial. To demonstrate this, we can add a new controller to our application:

@RestController public class CustomController { @RequestMapping(value = "/custom", method = RequestMethod.POST) public String custom() { return "custom"; } }

Now if we refresh the Swagger documentation, we see custom-controller in the list of controllers. As we know, there is only one method (POST) shown in Swagger’s response.

6. Spring Data REST

Springfox provides support for Spring Data REST through its springfox-data-rest library.

Spring Boot will take care of the auto-configuration if it discovers the spring-boot-starter-data-rest on the classpath.

Now let's create an entity named User:

@Entity public class User { @Id private Long id; private String firstName; private int age; private String email; // getters and setters }

Then we'll create the UserRepository to add CRUD operations on the User entity:

@Repository public interface UserRepository extends CrudRepository { }

Last, we'll import the SpringDataRestConfiguration class to the SpringFoxConfig class:

@EnableSwagger2WebMvc @Import(SpringDataRestConfiguration.class) public class SpringFoxConfig { //... }

Note: We've used the @EnableSwagger2WebMvc annotation to enable Swagger, as it has replaced the @EnableSwagger2 annotation in version 3 of the libraries.

Let's restart the application to generate the specifications for the Spring Data REST APIs:

We can see that Springfox has generated the specifications for the User entity with HTTP methods like GET, POST, PUT, PATCH, and DELETE.

7. Bean Validations

Springfox also supports the bean validation annotations through its springfox-bean-validators library.

First, we'll add the Maven dependency to our pom.xml:

 io.springfox springfox-bean-validators 2.9.2 

Again, if we use Spring Boot, we don't have to provide the above dependency explicitly.

Next, let's add a few validation annotations like @NotNull and @Min to the User entity:

@Entity public class User { //... @NotNull(message = "First Name cannot be null") private String firstName; @Min(value = 15, message = "Age should not be less than 15") @Max(value = 65, message = "Age should not be greater than 65") private int age; }

Finally, we'll import the BeanValidatorPluginsConfiguration class to the SpringFoxConfig class:

@EnableSwagger2 @Import(BeanValidatorPluginsConfiguration.class) public class SpringFoxConfig { //... }

Let's take a look at the changes in the API specifications:

Here, we can observe that the User model has * required on the firstName. Also, the minimum and maximum values are defined for the age.

8. Plugin

In order to add specific features to the API specifications, we can create a Springfox plugin. A plugin can offer various features, from enriching the models and properties to the custom API listings and defaults.

Springfox supports the plugin creation through its spi module. The spi module provides a few interfaces like the ModelBuilderPlugin, ModelPropertyBuilderPlugin, and ApiListingBuilderPlugin that act as an extensibility hook to implement a custom plugin.

To demonstrate the capabilities, let's create a plugin to enrich the email property of the User model. We'll use the ModelPropertyBuilderPlugin interface and set the values of the pattern and example.

First, let's create the EmailAnnotationPlugin class and override the supports method to allow any documentation type, such as Swagger 1.2 and Swagger 2:

@Component @Order(Validators.BEAN_VALIDATOR_PLUGIN_ORDER) public class EmailAnnotationPlugin implements ModelPropertyBuilderPlugin { @Override public boolean supports(DocumentationType delimiter) { return true; } }

Then we'll override the apply method of the ModelPropertyBuilderPlugin to set the values of the builder properties:

@Override public void apply(ModelPropertyContext context) { Optional email = annotationFromBean(context, Email.class); if (email.isPresent()) { context.getSpecificationBuilder().facetBuilder(StringElementFacetBuilder.class) .pattern(email.get().regexp()); context.getSpecificationBuilder().example("[email protected]"); } }

So, the API specifications will show the pattern and example values of the property annotated with the @Email annotation.

Next, we'll add the @Email annotation to the User entity:

@Entity public class User { //... @Email(regexp=".*@.*\\..*", message = "Email should be valid") private String email; }

Last, we'll enable the EmailAnnotationPlugin in the SpringFoxConfig class by registering as a bean:

@Import({BeanValidatorPluginsConfiguration.class}) public class SpringFoxConfig { //... @Bean public EmailAnnotationPlugin emailPlugin() { return new EmailAnnotationPlugin(); } }

Let's check out the EmailAnnotationPlugin in action:

We can see the value of the pattern is the same regex (.*@.*\\..*) from the email property of the User entity.

Similarly, the value of the example ([email protected]) is the same, as defined in the apply method of the EmailAnnotationPlugin.

9. Advanced Configuration

The Docket bean of our application can be configured to give us more control over the API documentation generation process.

9.1. Filtering API for Swagger’s Response

It is not always desirable to expose the documentation for the entire API. We can restrict Swagger’s response by passing parameters to the apis() and paths() methods of the Docket class.

As seen above, RequestHandlerSelectors allows using the any or none predicates but can also be used to filter the API according to the base package, class annotation, and method annotations.

PathSelectors provides additional filtering with predicates, which scan the request paths of our application. We can use any(), none(), regex(), or ant().

In the example below, we will instruct Swagger to include only controllers from a particular package, with specific paths, using the ant() predicate:

@Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.basePackage("com.baeldung.web.controller")) .paths(PathSelectors.ant("/foos/*")) .build(); }

9.2. Custom Information

Swagger also provides some default values in its response, which we can customize, such as “Api Documentation”, “Created by Contact Email”, and “Apache 2.0”.

To change these values, we can use the apiInfo(ApiInfo apiInfo) method — the ApiInfo class that contains custom information about the API:

@Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.basePackage("com.example.controller")) .paths(PathSelectors.ant("/foos/*")) .build() .apiInfo(apiInfo()); } private ApiInfo apiInfo() { return new ApiInfo( "My REST API", "Some custom description of API.", "API TOS", "Terms of service", new Contact("John Doe", "www.example.com", "[email protected]"), "License of API", "API license URL", Collections.emptyList()); }

9.3. Custom Methods Response Messages

Swagger allows globally overriding response messages of HTTP methods through Docket’s globalResponseMessage()method.

First, we need to instruct Swagger not to use default response messages. Suppose we want to override 500 and 403 response messages for all GET methods.

To achieve this, some code must be added to the Docket’s initialization block (original code is excluded for clarity):

.useDefaultResponseMessages(false) .globalResponseMessage(RequestMethod.GET, newArrayList(new ResponseMessageBuilder() .code(500) .message("500 message") .responseModel(new ModelRef("Error")) .build(), new ResponseMessageBuilder() .code(403) .message("Forbidden!") .build()));

10. Swagger UI With an OAuth-Secured API

The Swagger UI provides a number of very useful features that we've covered well so far here. But we can't really use most of these if our API is secured and not accessible.

Let's see how we can allow Swagger to access an OAuth-secured API using the Authorization Code grant type in this example.

We'll configure Swagger to access our secured API using the SecurityScheme and SecurityContext support:

@Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2).select() .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.any()) .build() .securitySchemes(Arrays.asList(securityScheme())) .securityContexts(Arrays.asList(securityContext())); }

10.1. The Security Configuration

We'll define a SecurityConfiguration bean in our Swagger configuration and set some defaults:

@Bean public SecurityConfiguration security() { return SecurityConfigurationBuilder.builder() .clientId(CLIENT_ID) .clientSecret(CLIENT_SECRET) .scopeSeparator(" ") .useBasicAuthenticationWithAccessCodeGrant(true) .build(); }

10.2. SecurityScheme

Next, we'll define our SecurityScheme; this is used to describe how our API is secured (Basic Authentication, OAuth2, …).

In our case here, we'll define an OAuth scheme used to secure our Resource Server:

private SecurityScheme securityScheme() { GrantType grantType = new AuthorizationCodeGrantBuilder() .tokenEndpoint(new TokenEndpoint(AUTH_SERVER + "/token", "oauthtoken")) .tokenRequestEndpoint( new TokenRequestEndpoint(AUTH_SERVER + "/authorize", CLIENT_ID, CLIENT_SECRET)) .build(); SecurityScheme oauth = new OAuthBuilder().name("spring_oauth") .grantTypes(Arrays.asList(grantType)) .scopes(Arrays.asList(scopes())) .build(); return oauth; }

Note that we used the Authorization Code grant type, for which we need to provide a token endpoint and the authorization URL of our OAuth2 Authorization Server.

And here are the scopes we need to have defined:

private AuthorizationScope[] scopes() { AuthorizationScope[] scopes = { new AuthorizationScope("read", "for read operations"), new AuthorizationScope("write", "for write operations"), new AuthorizationScope("foo", "Access foo API") }; return scopes; }

These sync up with the scopes we actually have defined in our application, for the /foos API.

10.3. SecurityContext

Finally, we need to define a SecurityContext for our example API:

private SecurityContext securityContext() { return SecurityContext.builder() .securityReferences( Arrays.asList(new SecurityReference("spring_oauth", scopes()))) .forPaths(PathSelectors.regex("/foos.*")) .build(); }

Note how the name we used here in the reference — spring_oauth — syncs up with the name we used previously in the SecurityScheme.

10.4. Test

Now that we have everything set up and ready to go, let's take a look at our Swagger UI and try access the Foo API.

We can access the Swagger UI locally:

//localhost:8082/spring-security-oauth-resource/swagger-ui.html

As we can see, a new Authorize button now exists due to our security configurations:

When we click the Authorize button, we can see the following pop-up to authorize our Swagger UI to access the secured API:

Note that:

  • We can already see the CLIENT_ID and CLIENT_SECRET, as we've pre-configured them earlier (but we can still change them).
  • We can now select the scopes we need.

Here's how the secured API is marked:

And now, finally, we can hit our API!

물론이 보안 구성이 활성화되었으므로 Swagger UI를 외부에 노출하는 방법에주의해야한다는 것은 말할 필요도 없습니다.

11. 결론

이 기사에서는 Swagger 2를 설정하여 Spring REST API에 대한 문서를 생성합니다. 또한 Swagger의 출력을 시각화하고 사용자 정의하는 방법을 탐색했습니다. 마지막으로 Swagger에 대한 간단한 OAuth 구성을 살펴 보았습니다.

이 튜토리얼 의 전체 구현 은 GitHub 프로젝트에서 찾을 수 있습니다. Boot 프로젝트의 설정을 보려면이 GitHub 모듈을 확인하세요.

OAuth 섹션의 경우 spring-security-oauth 저장소에서 코드를 사용할 수 있습니다.

REST With Spring의 학생 인 경우 모듈 7의 단원 1로 이동하여 Spring 및 Spring Boot로 Swagger를 설정하는 방법에 대해 자세히 알아보세요.

보안 바닥

방금 Spring Security 5의 새로운 OAuth2 스택에 초점을 맞춘 전체 자료를 포함하여 새로운 Learn Spring Security 과정을 발표했습니다.

>> 과정 나머지 하단을 확인하십시오

방금 Spring 5 및 Spring Boot 2의 기본 사항에 초점을 맞춘 새로운 Learn Spring 과정을 발표했습니다 .

>> 과정 확인