Skip to content

Commit

Permalink
Merge pull request #23 from Genc/develop
Browse files Browse the repository at this point in the history
Upgrading to Java 17 and Spring Boot 3.3.1 version.
Genc authored Jul 22, 2024
2 parents 7ccc554 + 761db3e commit b0d99f4
Showing 17 changed files with 135 additions and 103 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -12,10 +12,10 @@ jobs:

steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '11'
java-version: '17'
distribution: 'temurin'

- name: Build with Maven
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM openjdk:11
FROM eclipse-temurin:17
WORKDIR app
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} spring-boot-boilerplate.jar
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
*Spring Boot Boilerplate* is a **starter kit**. This project is a very simple and useful.

## Technologies
- Spring Boot (v2.7.10)
- Spring Boot (v3.3.1)
- Spring Data JPA
- Spring Validation
- Spring Security + JWT Token
18 changes: 14 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: '3.8'

services:

db:
@@ -10,16 +8,28 @@ services:
- "5432:5432"
environment:
POSTGRES_PASSWORD: example
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5

app:
container_name: app
image: omerfarukgenc34/spring-boot-boilerplate:2.2.0
image: omerfarukgenc34/spring-boot-boilerplate:3.3.1
ports:
- "8080:8080"
environment:
- "POSTGRES_DB_SERVER_ADDRESS=db"
- "POSTGRES_DB_SERVER_PORT=5432"
- "POSTGRES_USER=postgres"
- "POSTGRES_PASSWORD=example"
healthcheck:
test: "curl --fail --silent localhost:8080/actuator/health/readiness | grep UP || exit 1"
interval: 2s
timeout: 3s
retries: 5
start_period: 2s
depends_on:
- db
db:
condition: service_healthy
19 changes: 14 additions & 5 deletions local-docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: '3.8'

services:

db:
@@ -10,17 +8,28 @@ services:
- "5432:5432"
environment:
POSTGRES_PASSWORD: example
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5

app:
container_name: app
build:
context: .
build: .
ports:
- "8080:8080"
environment:
- "POSTGRES_DB_SERVER_ADDRESS=db"
- "POSTGRES_DB_SERVER_PORT=5432"
- "POSTGRES_USER=postgres"
- "POSTGRES_PASSWORD=example"
healthcheck:
test: "curl --fail --silent localhost:8080/actuator/health/readiness | grep UP || exit 1"
interval: 2s
timeout: 3s
retries: 5
start_period: 2s
depends_on:
- db
db:
condition: service_healthy
36 changes: 13 additions & 23 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -6,17 +6,17 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.10</version>
<version>3.3.1</version>
</parent>

<groupId>com.farukgenc</groupId>
<artifactId>springboot-boilerplate</artifactId>
<version>2.2.0</version>
<version>3.3.1</version>

<name>spring-boot-boilerplate</name>
<description>
Spring Boot Boilerplate is a starter kit. This project includes : Spring Boot(2.7.4), Spring Data JPA, Spring Validation, Spring Security +
JWT Token, PostgreSQL, Mapstruct, Lombok, Swagger
Spring Boot Boilerplate is a starter kit.
This project includes : Spring Boot(3.3.1), Spring Data JPA, Spring Validation, Spring Security + JWT Token, PostgreSQL, Mapstruct, Lombok, Swagger (Open API)
</description>

<developers>
@@ -28,12 +28,15 @@
</developers>

<properties>
<java.version>11</java.version>
<jwt.version>4.3.0</jwt.version>
<apache-commons.version>3.12.0</apache-commons.version>
<openapi-swagger.version>1.6.15</openapi-swagger.version>
<mapstruct.version>1.5.3.Final</mapstruct.version>

<java.version>17</java.version>
<jwt.version>4.4.0</jwt.version>

<openapi-swagger.version>2.6.0</openapi-swagger.version>

<mapstruct.version>1.5.5.Final</mapstruct.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>

</properties>

<dependencies>
@@ -99,29 +102,16 @@
<version>${lombok-mapstruct-binding.version}</version>
</dependency>


<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${apache-commons.version}</version>
</dependency>

<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${openapi-swagger.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>

</dependencies>
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package com.farukgenc.boilerplate.springboot.configuration;

import com.farukgenc.boilerplate.springboot.security.jwt.JwtAuthenticationFilter;
import com.farukgenc.boilerplate.springboot.security.jwt.JwtAuthenticationEntryPoint;
import com.farukgenc.boilerplate.springboot.security.jwt.JwtAuthenticationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.*;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@@ -20,9 +20,7 @@
* @author Faruk
*/
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {

private final JwtAuthenticationFilter jwtAuthenticationFilter;
@@ -39,17 +37,24 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti

//@formatter:off

return http.cors().and().csrf().disable()
return http
.csrf(CsrfConfigurer::disable)
.cors(CorsConfigurer::disable)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/register", "/login","/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html", "/actuator/**").permitAll()
.anyRequest().authenticated().and()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().build();
.authorizeHttpRequests(request -> request.requestMatchers("/register",
"/login",
"/v3/api-docs/**",
"/swagger-ui/**",
"/swagger-ui.html",
"/actuator/**")
.permitAll()
.anyRequest()
.authenticated())
.sessionManagement(manager -> manager.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.exceptionHandling(handler -> handler.authenticationEntryPoint(unauthorizedHandler))
.build();

//@formatter:on
}


}
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@
import io.swagger.v3.oas.models.security.SecurityScheme;
import lombok.Getter;
import lombok.Setter;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -49,7 +50,7 @@ public OpenAPI openAPI() {
final Components components = new Components();

final String schemeName = "bearerAuth";
components.addSecuritySchemes(schemeName, new SecurityScheme().name(schemeName).type(HTTP).scheme("bearer").bearerFormat("JWT"));
components.addSecuritySchemes(schemeName, new SecurityScheme().name(schemeName).type(HTTP).scheme("Bearer").bearerFormat("JWT"));

final OpenAPI openAPI = new OpenAPI();
openAPI.setInfo(apiInformation);
@@ -70,7 +71,6 @@ private Info getApiInformation() {
contact.setUrl(contactUrl);
contact.setEmail(contactMail);


final Info info = new Info();
info.setTitle(appName);
info.setVersion(appVersion);
@@ -81,4 +81,14 @@ private Info getApiInformation() {
return info;
}

@Bean
GroupedOpenApi managementApi() {
return GroupedOpenApi.builder().pathsToMatch("/actuator/**").group("Management Api").build();
}

@Bean
GroupedOpenApi defaultApi() {
return GroupedOpenApi.builder().pathsToExclude("/actuator/**").group("Default Api").build();
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.farukgenc.boilerplate.springboot.controller;

import io.swagger.v3.oas.annotations.Operation;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -13,6 +14,7 @@
public class HelloController {

@GetMapping("/hello")
@Operation(tags = "Hello Service", description = "When you send token information in the header it just says Hello")
public ResponseEntity<String> sayHello() {

return ResponseEntity.ok("Hello Spring Boot Boilerplate");
Original file line number Diff line number Diff line change
@@ -3,18 +3,17 @@
import com.farukgenc.boilerplate.springboot.security.dto.LoginRequest;
import com.farukgenc.boilerplate.springboot.security.dto.LoginResponse;
import com.farukgenc.boilerplate.springboot.security.jwt.JwtTokenService;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

/**
* Created on Ağustos, 2020
*
* @author Faruk
*/
@CrossOrigin
@RestController
@RequiredArgsConstructor
@RequestMapping("/login")
@@ -23,6 +22,7 @@ public class LoginController {
private final JwtTokenService jwtTokenService;

@PostMapping
@Operation(tags = "Login Service", description = "You must log in with the correct information to successfully obtain the token information.")
public ResponseEntity<LoginResponse> loginRequest(@Valid @RequestBody LoginRequest loginRequest) {

final LoginResponse loginResponse = jwtTokenService.getLoginResponse(loginRequest);
Original file line number Diff line number Diff line change
@@ -3,19 +3,19 @@
import com.farukgenc.boilerplate.springboot.security.dto.RegistrationRequest;
import com.farukgenc.boilerplate.springboot.security.dto.RegistrationResponse;
import com.farukgenc.boilerplate.springboot.security.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

/**
* Created on Ağustos, 2020
*
* @author Faruk
*/
@CrossOrigin
@RestController
@RequiredArgsConstructor
@RequestMapping("/register")
@@ -24,6 +24,7 @@ public class RegistrationController {
private final UserService userService;

@PostMapping
@Operation(tags = "Register Service", description = "You can register to the system by sending information in the appropriate format.")
public ResponseEntity<RegistrationResponse> registrationRequest(@Valid @RequestBody RegistrationRequest registrationRequest) {

final RegistrationResponse registrationResponse = userService.registration(registrationRequest);
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.farukgenc.boilerplate.springboot.model;

import jakarta.persistence.*;
import lombok.*;

import javax.persistence.*;

/**
* Created on Ağustos, 2020
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.farukgenc.boilerplate.springboot.security.dto;

import jakarta.validation.constraints.NotEmpty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.validation.constraints.NotEmpty;

/**
* Created on Ağustos, 2020
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package com.farukgenc.boilerplate.springboot.security.dto;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotEmpty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;

/**
* Created on Ağustos, 2020
*
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.farukgenc.boilerplate.springboot.security.jwt;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
Original file line number Diff line number Diff line change
@@ -2,9 +2,13 @@

import com.farukgenc.boilerplate.springboot.security.service.UserDetailsServiceImpl;
import com.farukgenc.boilerplate.springboot.security.utils.SecurityConstants;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
@@ -13,10 +17,6 @@
import org.springframework.stereotype.Service;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

@@ -35,44 +35,50 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final UserDetailsServiceImpl userDetailsService;

@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {

final String requestURI = req.getRequestURI();
final String header = request.getHeader(SecurityConstants.HEADER_STRING);

if (requestURI.contains(SecurityConstants.LOGIN_REQUEST_URI) || requestURI.contains(SecurityConstants.REGISTRATION_REQUEST_URI)) {
chain.doFilter(req, res);
return;
}

final String header = req.getHeader(SecurityConstants.HEADER_STRING);
String username = null;
String authToken = null;
if (Objects.nonNull(header) && header.startsWith(SecurityConstants.TOKEN_PREFIX)) {

authToken = header.replace(SecurityConstants.TOKEN_PREFIX, StringUtils.EMPTY);
authToken = header.replace(SecurityConstants.TOKEN_PREFIX, Strings.EMPTY);

try {
username = jwtTokenManager.getUsernameFromToken(authToken);
}
catch (Exception e) {
log.error("Authentication Exception : {}", e.getMessage());
chain.doFilter(request, response);
return;
}
}

final SecurityContext securityContext = SecurityContextHolder.getContext();

if (Objects.nonNull(username) && Objects.isNull(securityContext.getAuthentication())) {
final boolean canBeStartTokenValidation = Objects.nonNull(username) && Objects.isNull(securityContext.getAuthentication());

final UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (!canBeStartTokenValidation) {
chain.doFilter(request, response);
return;
}

if (jwtTokenManager.validateToken(authToken, userDetails.getUsername())) {
final UserDetails user = userDetailsService.loadUserByUsername(username);
final boolean validToken = jwtTokenManager.validateToken(authToken, user.getUsername());

final UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(req));
log.info("Authentication successful. Logged in username : {} ", username);
securityContext.setAuthentication(authentication);
}
if (!validToken) {
chain.doFilter(request, response);
return;
}

chain.doFilter(req, res);
final UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
securityContext.setAuthentication(authentication);

log.info("Authentication successful. Logged in username : {} ", username);

chain.doFilter(request, response);
}
}
34 changes: 17 additions & 17 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
server:
http2:
enabled: true

spring:
datasource:
url: jdbc:postgresql://${POSTGRES_DB_SERVER_ADDRESS:localhost}:${POSTGRES_DB_SERVER_PORT:5432}/
username: ${POSTGRES_USER:postgres}
password: ${POSTGRES_PASSWORD:example}

hikari:
pool-name: SpringBootBoilerplateHikariPool
jpa:
database-platform: org.hibernate.dialect.PostgreSQLDialect
hibernate:
ddl-auto: create
show-sql: true
format-sql: true
type: info
use-sql-comments: true
properties:
hibernate.use_sql_comments: true
hibernate.format_sql: true
open-in-view: false
jta:
enabled: false

springdoc:
show-actuator: true
paths-to-match: /**
packages-to-scan: com.farukgenc.boilerplate.springboot

management:
endpoint:
health:
show-details: ALWAYS
endpoints:
web:
exposure:
include: "*"
probes:
enabled: true
endpoints:
web:
exposure:
include: "*"

logging:
level:
@@ -47,7 +47,7 @@ swagger:
contact-mail: omer@farukgenc.com
contact-url: https://farukgenc.com
app-name: Spring Boot Boilerplate Project
app-description: "Spring Boot Boilerplate is a starter kit. This project includes : Spring Boot(2.7.4), Spring Data JPA, Spring Validation, Spring Security + JWT Token, PostgreSQL, Mapstruct, Lombok, Swagger"
app-version: 2.0.0
app-description: "Spring Boot Boilerplate is a starter kit. This project includes : Spring Boot(3.3.1), Spring Data JPA, Spring Validation, Spring Security + JWT Token, PostgreSQL, Mapstruct, Lombok, Swagger (Open API)"
app-version: 3.3.1
app-license-url: https://www.apache.org/licenses/LICENSE-2.0.html
app-license: Apache 2.0

0 comments on commit b0d99f4

Please sign in to comment.