From f45b2c8659c0cb075bcd7f5fee9f2ed8b28cb31c Mon Sep 17 00:00:00 2001 From: Adrian Bob Date: Fri, 9 Dec 2022 20:35:09 +0200 Subject: [PATCH] BAEL-5325: Add Spring Security login/logout API with Springdoc (#13027) * BAEL-5325: Add Spring Security login/logout API with Springdoc * Updated Spring version and using annotations for Basic Auth config * Added tests * Removed unused dependencies * Removed dependencies which were accidentally added. * Removed plugins which were accidentally added. * Removed property which was accidentally added. --- spring-security-modules/pom.xml | 1 + .../spring-security-web-springdoc/README.md | 15 +++++ .../spring-security-web-springdoc/pom.xml | 54 ++++++++++++++++ .../main/java/com/baeldung/basicauth/Foo.java | 60 +++++++++++++++++ .../com/baeldung/basicauth/FooController.java | 39 +++++++++++ .../SpringBootSpringdocBasicAuth.java | 13 ++++ .../config/PasswordEncoderConfiguration.java | 15 +++++ .../config/SecurityConfiguration.java | 38 +++++++++++ .../basicauth/config/SpringdocConfig.java | 12 ++++ .../SpringBootSpringdocFormLogin.java | 13 ++++ .../config/PasswordEncoderConfiguration.java | 15 +++++ .../config/SecurityConfiguration.java | 40 ++++++++++++ .../formlogin/controller/FooController.java | 37 +++++++++++ .../controller/LogoutController.java | 17 +++++ .../com/baeldung/formlogin/model/Foo.java | 62 ++++++++++++++++++ .../src/main/resources/application.properties | 1 + .../basicauth/OpenAPIIntegrationTest.java | 64 +++++++++++++++++++ .../formlogin/OpenAPIIntegrationTest.java | 52 +++++++++++++++ 18 files changed, 548 insertions(+) create mode 100644 spring-security-modules/spring-security-web-springdoc/README.md create mode 100644 spring-security-modules/spring-security-web-springdoc/pom.xml create mode 100644 spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/Foo.java create mode 100644 spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/FooController.java create mode 100644 spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/SpringBootSpringdocBasicAuth.java create mode 100644 spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/config/PasswordEncoderConfiguration.java create mode 100644 spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/config/SecurityConfiguration.java create mode 100644 spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/config/SpringdocConfig.java create mode 100644 spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/SpringBootSpringdocFormLogin.java create mode 100644 spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/config/PasswordEncoderConfiguration.java create mode 100644 spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/config/SecurityConfiguration.java create mode 100644 spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/controller/FooController.java create mode 100644 spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/controller/LogoutController.java create mode 100644 spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/model/Foo.java create mode 100644 spring-security-modules/spring-security-web-springdoc/src/main/resources/application.properties create mode 100644 spring-security-modules/spring-security-web-springdoc/src/test/java/com/baeldung/basicauth/OpenAPIIntegrationTest.java create mode 100644 spring-security-modules/spring-security-web-springdoc/src/test/java/com/baeldung/formlogin/OpenAPIIntegrationTest.java diff --git a/spring-security-modules/pom.xml b/spring-security-modules/pom.xml index 1e0ae825e1..d6e30e8ab8 100644 --- a/spring-security-modules/pom.xml +++ b/spring-security-modules/pom.xml @@ -43,6 +43,7 @@ spring-security-web-rest-basic-auth spring-security-web-rest-custom spring-security-web-rest + spring-security-web-springdoc spring-security-web-sockets spring-security-web-thymeleaf spring-security-web-x509 diff --git a/spring-security-modules/spring-security-web-springdoc/README.md b/spring-security-modules/spring-security-web-springdoc/README.md new file mode 100644 index 0000000000..90a82d1681 --- /dev/null +++ b/spring-security-modules/spring-security-web-springdoc/README.md @@ -0,0 +1,15 @@ +## Spring Security Web Springdoc + +This module contains articles about Springdoc with Spring Security + +### Relevant Articles: + +- [Documenting a Spring REST API Using OpenAPI 3.0](https://www.baeldung.com/spring-rest-openapi-documentation) +- [Configure JWT Authentication for OpenAPI](https://www.baeldung.com/openapi-jwt-authentication) + +### Running This Project: + +To run the projects use the commands: +- `mvn spring-boot:run -Dstart-class=com.baeldung.basicauth.SpringBootSpringdocBasicAuth` +- `mvn spring-boot:run -Dstart-class=com.baeldung.formlogin.SpringBootSpringdocFormLogin` + diff --git a/spring-security-modules/spring-security-web-springdoc/pom.xml b/spring-security-modules/spring-security-web-springdoc/pom.xml new file mode 100644 index 0000000000..03e938f1c8 --- /dev/null +++ b/spring-security-modules/spring-security-web-springdoc/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + spring-security-web-springdoc + 0.1-SNAPSHOT + spring-security-web-springdoc + jar + Spring Security with Springdoc tutorial + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../../parent-boot-2 + + + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-web + + + org.springdoc + springdoc-openapi-ui + ${springdoc.version} + + + org.springdoc + springdoc-openapi-security + ${springdoc.version} + + + com.google.guava + guava + ${guava.version} + + + org.springframework.boot + spring-boot-starter-test + test + + + + + 1.6.13 + + + \ No newline at end of file diff --git a/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/Foo.java b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/Foo.java new file mode 100644 index 0000000000..ab0f010e2c --- /dev/null +++ b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/Foo.java @@ -0,0 +1,60 @@ +package com.baeldung.basicauth; + +import java.io.Serializable; + +public class Foo implements Serializable { + + private static final long serialVersionUID = -5422285893276747592L; + + private long id; + private String name; + + public Foo(final String name) { + super(); + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(final long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final Foo other = (Foo) obj; + if (name == null) { + return other.name == null; + } else return name.equals(other.name); + } + + @Override + public String toString() { + return "Foo [name=" + name + "]"; + } + +} diff --git a/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/FooController.java b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/FooController.java new file mode 100644 index 0000000000..9ca2f9a6cc --- /dev/null +++ b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/FooController.java @@ -0,0 +1,39 @@ +package com.baeldung.basicauth; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; + +import com.google.common.collect.Lists; + +@RestController +@OpenAPIDefinition(info = @Info(title = "Foos API", version = "v1")) +@SecurityRequirement(name = "basicAuth") +@RequestMapping(value = "foos", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) +public class FooController { + + private static final int STRING_LENGTH = 6; + + @GetMapping(value = "/{id}") + public Foo findById(@PathVariable("id") final Long id) { + return new Foo(randomAlphabetic(STRING_LENGTH)); + } + + @GetMapping + public List findAll() { + return Lists.newArrayList(new Foo(randomAlphabetic(STRING_LENGTH)), new Foo(randomAlphabetic(STRING_LENGTH)), new Foo(randomAlphabetic(STRING_LENGTH))); + } + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public Foo create(@RequestBody final Foo foo) { + return foo; + } +} diff --git a/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/SpringBootSpringdocBasicAuth.java b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/SpringBootSpringdocBasicAuth.java new file mode 100644 index 0000000000..0875c5d176 --- /dev/null +++ b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/SpringBootSpringdocBasicAuth.java @@ -0,0 +1,13 @@ +package com.baeldung.basicauth; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBootSpringdocBasicAuth { + + public static void main(String[] args) { + SpringApplication.run(SpringBootSpringdocBasicAuth.class, args); + } + +} diff --git a/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/config/PasswordEncoderConfiguration.java b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/config/PasswordEncoderConfiguration.java new file mode 100644 index 0000000000..4da6b5438b --- /dev/null +++ b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/config/PasswordEncoderConfiguration.java @@ -0,0 +1,15 @@ +package com.baeldung.basicauth.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +public class PasswordEncoderConfiguration { + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/config/SecurityConfiguration.java b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/config/SecurityConfiguration.java new file mode 100644 index 0000000000..a419162828 --- /dev/null +++ b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/config/SecurityConfiguration.java @@ -0,0 +1,38 @@ +package com.baeldung.basicauth.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +public class SecurityConfiguration { + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .csrf().disable() + .authorizeRequests() + .antMatchers("/v3/api-docs/**", + "/swagger-ui/**", + "/swagger-ui.html").permitAll() + .anyRequest().authenticated() + .and() + .httpBasic(); + return http.build(); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth, PasswordEncoder passwordEncoder) throws Exception { + auth.inMemoryAuthentication() + .withUser("user") + .password(passwordEncoder.encode("password")) + .roles("USER"); + } + +} diff --git a/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/config/SpringdocConfig.java b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/config/SpringdocConfig.java new file mode 100644 index 0000000000..57d3d8bd02 --- /dev/null +++ b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/basicauth/config/SpringdocConfig.java @@ -0,0 +1,12 @@ +package com.baeldung.basicauth.config; + +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.security.SecurityScheme; +import org.springframework.context.annotation.Configuration; + +@Configuration +@SecurityScheme( + type = SecuritySchemeType.HTTP, + name = "basicAuth", + scheme = "basic") +public class SpringdocConfig {} diff --git a/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/SpringBootSpringdocFormLogin.java b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/SpringBootSpringdocFormLogin.java new file mode 100644 index 0000000000..5c4c262fa7 --- /dev/null +++ b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/SpringBootSpringdocFormLogin.java @@ -0,0 +1,13 @@ +package com.baeldung.formlogin; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBootSpringdocFormLogin { + + public static void main(String[] args) { + SpringApplication.run(SpringBootSpringdocFormLogin.class, args); + } + +} diff --git a/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/config/PasswordEncoderConfiguration.java b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/config/PasswordEncoderConfiguration.java new file mode 100644 index 0000000000..e3f9e71bd6 --- /dev/null +++ b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/config/PasswordEncoderConfiguration.java @@ -0,0 +1,15 @@ +package com.baeldung.formlogin.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +public class PasswordEncoderConfiguration { + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/config/SecurityConfiguration.java b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/config/SecurityConfiguration.java new file mode 100644 index 0000000000..2b849031ce --- /dev/null +++ b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/config/SecurityConfiguration.java @@ -0,0 +1,40 @@ +package com.baeldung.formlogin.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +public class SecurityConfiguration { + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .csrf().disable() + .authorizeRequests() + .antMatchers("/v3/api-docs/**", + "/swagger-ui/**", + "/swagger-ui.html").permitAll() + .anyRequest().authenticated() + .and() + .formLogin() + .defaultSuccessUrl("/foos"); + return http.build(); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth, PasswordEncoder passwordEncoder) throws Exception { + auth.inMemoryAuthentication() + .withUser("user") + .password(passwordEncoder.encode("password")) + .roles("USER"); + } + +} diff --git a/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/controller/FooController.java b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/controller/FooController.java new file mode 100644 index 0000000000..1d09789067 --- /dev/null +++ b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/controller/FooController.java @@ -0,0 +1,37 @@ +package com.baeldung.formlogin.controller; + +import com.baeldung.formlogin.model.Foo; +import com.google.common.collect.Lists; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.info.Info; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; + +@RestController +@RequestMapping(value = "foos", produces = MediaType.APPLICATION_JSON_VALUE) +@OpenAPIDefinition(info = @Info(title = "Foos API", version = "v1")) +public class FooController { + + private static final int STRING_LENGTH = 6; + + @GetMapping(value = "/{id}") + public Foo findById(@PathVariable("id") final Long id) { + return new Foo(randomAlphabetic(STRING_LENGTH)); + } + + @GetMapping + public List findAll() { + return Lists.newArrayList(new Foo(randomAlphabetic(STRING_LENGTH)), new Foo(randomAlphabetic(STRING_LENGTH)), new Foo(randomAlphabetic(STRING_LENGTH))); + } + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public Foo create(@RequestBody final Foo foo) { + return foo; + } +} diff --git a/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/controller/LogoutController.java b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/controller/LogoutController.java new file mode 100644 index 0000000000..ab1c635de0 --- /dev/null +++ b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/controller/LogoutController.java @@ -0,0 +1,17 @@ +package com.baeldung.formlogin.controller; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.info.Info; +import org.springframework.web.bind.annotation.*; + +@RestController +@OpenAPIDefinition(info = @Info(title = "logout-endpoint")) +public class LogoutController { + + @PostMapping("logout") + @Operation(description = "End authenticated user session") + public void logout() { + throw new UnsupportedOperationException(); + } +} diff --git a/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/model/Foo.java b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/model/Foo.java new file mode 100644 index 0000000000..5133c215b3 --- /dev/null +++ b/spring-security-modules/spring-security-web-springdoc/src/main/java/com/baeldung/formlogin/model/Foo.java @@ -0,0 +1,62 @@ +package com.baeldung.formlogin.model; + +import java.io.Serializable; + +public class Foo implements Serializable { + + private static final long serialVersionUID = -5422285893276747592L; + + private long id; + private String name; + + public Foo(final String name) { + this.name = name; + } + + public Foo() { + } + + public long getId() { + return id; + } + + public void setId(final long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final Foo other = (Foo) obj; + if (name == null) { + return other.name == null; + } else return name.equals(other.name); + } + + @Override + public String toString() { + return "Foo [name=" + name + "]"; + } + +} diff --git a/spring-security-modules/spring-security-web-springdoc/src/main/resources/application.properties b/spring-security-modules/spring-security-web-springdoc/src/main/resources/application.properties new file mode 100644 index 0000000000..7157c0f5b9 --- /dev/null +++ b/spring-security-modules/spring-security-web-springdoc/src/main/resources/application.properties @@ -0,0 +1 @@ +springdoc.show-login-endpoint=true diff --git a/spring-security-modules/spring-security-web-springdoc/src/test/java/com/baeldung/basicauth/OpenAPIIntegrationTest.java b/spring-security-modules/spring-security-web-springdoc/src/test/java/com/baeldung/basicauth/OpenAPIIntegrationTest.java new file mode 100644 index 0000000000..3e622059c1 --- /dev/null +++ b/spring-security-modules/spring-security-web-springdoc/src/test/java/com/baeldung/basicauth/OpenAPIIntegrationTest.java @@ -0,0 +1,64 @@ +package com.baeldung.basicauth; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class OpenAPIIntegrationTest { + + @LocalServerPort + private int port; + + @Autowired + private TestRestTemplate restTemplate; + + @Test + void whenInvokeSwagger_thenRenderIndexPage() { + String response = this.restTemplate.getForObject("http://localhost:" + port + "/swagger-ui/index.html", String.class); + + assertNotNull(response); + assertTrue(response.contains("Swagger UI")); + assertTrue(response.contains("
")); + } + + @Test + void whenInvokeOpenApi_thenCheckHeaders() { + ResponseEntity response = this.restTemplate.getForEntity("http://localhost:" + port + "/v3/api-docs", String.class); + + assertNotNull(response); + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertNotNull(response.getHeaders().get("Content-Type")); + assertEquals(1, response.getHeaders().get("Content-Type").size()); + assertEquals("application/json", response.getHeaders().get("Content-Type").get(0)); + } + + @Test + void whenInvokeOpenApi_thenVerifyOpenApiDoc() { + ResponseEntity response = this.restTemplate.getForEntity("http://localhost:" + port + "/v3/api-docs", String.class); + + assertNotNull(response); + assertNotNull(response.getBody()); + assertTrue(response.getBody().contains("\"openapi\":")); + assertTrue(response.getBody().contains("Foos API")); + assertTrue(response.getBody().contains("\"post\"")); + } + + @Test + void whenInvokeOpenApi_thenCheckSecurityConfig() { + ResponseEntity response = this.restTemplate.getForEntity("http://localhost:" + port + "/v3/api-docs", String.class); + + assertNotNull(response); + assertNotNull(response.getBody()); + assertTrue(response.getBody().contains("\"securitySchemes\"")); + assertTrue(response.getBody().contains("\"type\":\"http\"")); + assertTrue(response.getBody().contains("\"scheme\":\"basic\"")); + } +} diff --git a/spring-security-modules/spring-security-web-springdoc/src/test/java/com/baeldung/formlogin/OpenAPIIntegrationTest.java b/spring-security-modules/spring-security-web-springdoc/src/test/java/com/baeldung/formlogin/OpenAPIIntegrationTest.java new file mode 100644 index 0000000000..5d942f2126 --- /dev/null +++ b/spring-security-modules/spring-security-web-springdoc/src/test/java/com/baeldung/formlogin/OpenAPIIntegrationTest.java @@ -0,0 +1,52 @@ +package com.baeldung.formlogin; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class OpenAPIIntegrationTest { + + @LocalServerPort + private int port; + + @Autowired + private TestRestTemplate restTemplate; + + @Test + void whenInvokeSwagger_thenRenderIndexPage() { + String response = this.restTemplate.getForObject("http://localhost:" + port + "/swagger-ui/index.html", String.class); + + assertNotNull(response); + assertTrue(response.contains("Swagger UI")); + assertTrue(response.contains("
")); + } + + @Test + void whenInvokeOpenApi_thenCheckHeaders() { + ResponseEntity response = this.restTemplate.getForEntity("http://localhost:" + port + "/v3/api-docs", String.class); + + assertNotNull(response); + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertNotNull(response.getHeaders().get("Content-Type")); + assertEquals(1, response.getHeaders().get("Content-Type").size()); + assertEquals("application/json", response.getHeaders().get("Content-Type").get(0)); + } + + @Test + void whenInvokeOpenApi_thenVerifyOpenApiDoc() { + ResponseEntity response = this.restTemplate.getForEntity("http://localhost:" + port + "/v3/api-docs", String.class); + + assertNotNull(response); + assertNotNull(response.getBody()); + assertTrue(response.getBody().contains("\"openapi\":")); + assertTrue(response.getBody().contains("Foos API")); + assertTrue(response.getBody().contains("\"post\"")); + } +}