From a4691406f2b391651ca0826ae5afb2f4501280da Mon Sep 17 00:00:00 2001 From: victorsempere Date: Mon, 25 Jul 2022 05:51:00 +0200 Subject: [PATCH] BAEL-5328 (#12457) * BAEL-5328 First draft of the code to the article: https://drafts.baeldung.com/wp-admin/post.php?post=136328&action=edit * BAEL-5328 Fixed comment. Used entry point instead of endpoint * BAEL-5328 Added SecurityFilterChain configuration to allow requests to: * "/api/auth/**", "/swagger-ui-custom.html" ,"/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**", "/webjars/**", "/swagger-ui/index.html","/api-docs/**" As the spring-boot-starter-security has been started we need to add it to keep this article, simple. Fixed description of the login() method for the 200 httpstatus response * BAEL-5328 Added required attribute in @RequestBody of the login() method * BAEL-5328 Code formatting --- ...efaultGlobalSecuritySchemeApplication.java | 32 ++++++ ...GlobalSecuritySchemeOpenApiController.java | 61 +++++++++++ .../dto/ApplicationExceptionDto.java | 26 +++++ .../dto/LoginDto.java | 103 ++++++++++++++++++ .../dto/PingResponseDto.java | 84 ++++++++++++++ .../dto/TokenDto.java | 57 ++++++++++ 6 files changed, 363 insertions(+) create mode 100644 spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/DefaultGlobalSecuritySchemeApplication.java create mode 100644 spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/controller/DefaultGlobalSecuritySchemeOpenApiController.java create mode 100644 spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/dto/ApplicationExceptionDto.java create mode 100644 spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/dto/LoginDto.java create mode 100644 spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/dto/PingResponseDto.java create mode 100644 spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/dto/TokenDto.java diff --git a/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/DefaultGlobalSecuritySchemeApplication.java b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/DefaultGlobalSecuritySchemeApplication.java new file mode 100644 index 0000000000..1ce81a1e83 --- /dev/null +++ b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/DefaultGlobalSecuritySchemeApplication.java @@ -0,0 +1,32 @@ +package com.baeldung.defaultglobalsecurityscheme; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.SecurityFilterChain; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.security.SecurityScheme; + +@SpringBootApplication +@OpenAPIDefinition(info = @Info(title = "Apply Default Global SecurityScheme in springdoc-openapi", version = "1.0.0"), security = { @SecurityRequirement(name = "api_key") }) +@SecurityScheme(type = SecuritySchemeType.APIKEY, name = "api_key", in = SecuritySchemeIn.HEADER) +public class DefaultGlobalSecuritySchemeApplication { + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + return http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.antMatchers("/api/auth/**", "/swagger-ui-custom.html", "/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**", "/webjars/**", "/swagger-ui/index.html", "/api-docs/**") + .permitAll() + .anyRequest() + .authenticated()) + .build(); + } + + public static void main(String[] args) { + SpringApplication.run(DefaultGlobalSecuritySchemeApplication.class, args); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/controller/DefaultGlobalSecuritySchemeOpenApiController.java b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/controller/DefaultGlobalSecuritySchemeOpenApiController.java new file mode 100644 index 0000000000..4ad7a2a2c3 --- /dev/null +++ b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/controller/DefaultGlobalSecuritySchemeOpenApiController.java @@ -0,0 +1,61 @@ +package com.baeldung.defaultglobalsecurityscheme.controller; + +import java.time.OffsetDateTime; +import java.time.ZoneOffset; + +import javax.validation.Valid; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import com.baeldung.defaultglobalsecurityscheme.dto.LoginDto; +import com.baeldung.defaultglobalsecurityscheme.dto.ApplicationExceptionDto; +import com.baeldung.defaultglobalsecurityscheme.dto.PingResponseDto; +import com.baeldung.defaultglobalsecurityscheme.dto.TokenDto; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityRequirements; + +@RestController +@RequestMapping("/") +public class DefaultGlobalSecuritySchemeOpenApiController { + @RequestMapping(method = RequestMethod.POST, value = "/login", produces = { "application/json" }, consumes = { "application/json" }) + @Operation(operationId = "login", responses = { + @ApiResponse(responseCode = "200", description = "api_key to be used in the secured-ping entry point", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = TokenDto.class)) }), + @ApiResponse(responseCode = "401", description = "Unauthorized request", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = ApplicationExceptionDto.class)) }) }) + @SecurityRequirements() + public ResponseEntity login(@Parameter(name = "LoginDto", description = "Login") @Valid @RequestBody(required = true) LoginDto loginDto) { + TokenDto token = new TokenDto(); + token.setRaw("Generated Token"); + return ResponseEntity.ok(token); + } + + @Operation(operationId = "ping", responses = { + @ApiResponse(responseCode = "200", description = "Ping that needs an api_key attribute in the header", content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = PingResponseDto.class), examples = { @ExampleObject(value = "{ pong: '2022-06-17T18:30:33.465+02:00' }") }) }), + @ApiResponse(responseCode = "401", description = "Unauthorized request", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = ApplicationExceptionDto.class)) }), + @ApiResponse(responseCode = "403", description = "Forbidden request", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = ApplicationExceptionDto.class)) }) }) + @RequestMapping(method = RequestMethod.GET, value = "/ping", produces = { "application/json" }) + public ResponseEntity ping(@RequestHeader(name = "api_key", required = false) String api_key) { + int year = 2000; + int month = 1; + int dayOfMonth = 1; + int hour = 0; + int minute = 0; + int second = 0; + int nanoSeccond = 0; + ZoneOffset offset = ZoneOffset.UTC; + PingResponseDto response = new PingResponseDto(); + response.setPong(OffsetDateTime.of(year, month, dayOfMonth, hour, minute, second, nanoSeccond, offset)); + return ResponseEntity.ok(response); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/dto/ApplicationExceptionDto.java b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/dto/ApplicationExceptionDto.java new file mode 100644 index 0000000000..5fb63793c4 --- /dev/null +++ b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/dto/ApplicationExceptionDto.java @@ -0,0 +1,26 @@ +package com.baeldung.defaultglobalsecurityscheme.dto; + +public class ApplicationExceptionDto { + private long errorCode; + private String description; + + public ApplicationExceptionDto() { + super(); + } + + public long getErrorCode() { + return errorCode; + } + + public void setErrorCode(long errorCode) { + this.errorCode = errorCode; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} diff --git a/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/dto/LoginDto.java b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/dto/LoginDto.java new file mode 100644 index 0000000000..cf88cc4d98 --- /dev/null +++ b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/dto/LoginDto.java @@ -0,0 +1,103 @@ +package com.baeldung.defaultglobalsecurityscheme.dto; + +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * LoginDto + */ + +@JsonTypeName("Login") +public class LoginDto { + + @JsonProperty("user") + private String user; + + @JsonProperty("pass") + private String pass; + + public LoginDto user(String user) { + this.user = user; + return this; + } + + /** + * Get user + * @return user + */ + + @Schema(name = "user", required = true) + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public LoginDto pass(String pass) { + this.pass = pass; + return this; + } + + /** + * Get pass + * @return pass + */ + + @Schema(name = "pass", required = true) + public String getPass() { + return pass; + } + + public void setPass(String pass) { + this.pass = pass; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + LoginDto login = (LoginDto) o; + return Objects.equals(this.user, login.user) && Objects.equals(this.pass, login.pass); + } + + @Override + public int hashCode() { + return Objects.hash(user, pass); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class LoginDto {\n"); + sb.append(" user: ") + .append(toIndentedString(user)) + .append("\n"); + sb.append(" pass: ") + .append(toIndentedString(pass)) + .append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString() + .replace("\n", "\n "); + } +} diff --git a/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/dto/PingResponseDto.java b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/dto/PingResponseDto.java new file mode 100644 index 0000000000..0d367785d8 --- /dev/null +++ b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/dto/PingResponseDto.java @@ -0,0 +1,84 @@ +package com.baeldung.defaultglobalsecurityscheme.dto; + +import java.time.OffsetDateTime; +import java.util.Objects; + +import javax.validation.Valid; + +import org.springframework.format.annotation.DateTimeFormat; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * SecuredPingResponseDto + */ + +@JsonTypeName("PingResponse") +public class PingResponseDto { + + @JsonProperty("pong") + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private OffsetDateTime pong; + + public PingResponseDto pong(OffsetDateTime pong) { + this.pong = pong; + return this; + } + + /** + * Get pong + * @return pong + */ + @Valid + @Schema(name = "pong", required = false) + public OffsetDateTime getPong() { + return pong; + } + + public void setPong(OffsetDateTime pong) { + this.pong = pong; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PingResponseDto securedPingResponse = (PingResponseDto) o; + return Objects.equals(this.pong, securedPingResponse.pong); + } + + @Override + public int hashCode() { + return Objects.hash(pong); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class PingResponseDto {\n"); + sb.append(" pong: ") + .append(toIndentedString(pong)) + .append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString() + .replace("\n", "\n "); + } +} diff --git a/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/dto/TokenDto.java b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/dto/TokenDto.java new file mode 100644 index 0000000000..d8f7daa09c --- /dev/null +++ b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/defaultglobalsecurityscheme/dto/TokenDto.java @@ -0,0 +1,57 @@ +package com.baeldung.defaultglobalsecurityscheme.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * LoginDto + */ + +@JsonTypeName("Token") +public class TokenDto { + + @JsonProperty("raw") + private String raw; + + @Schema(name = "raw", example = "app token") + public String getRaw() { + return raw; + } + + public void setRaw(String raw) { + this.raw = raw; + } + + @Override + public String toString() { + return "TokenDto [raw=" + raw + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((raw == null) ? 0 : raw.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + TokenDto other = (TokenDto) obj; + if (raw == null) { + if (other.raw != null) + return false; + } else if (!raw.equals(other.raw)) + return false; + return true; + } + +}