├── imgs ├── swagger-1.jpg ├── swagger-2.jpg └── springboot-boilerplate.jpg ├── src └── main │ ├── resources │ ├── messages │ │ ├── general │ │ │ ├── GeneralMessages.properties │ │ │ └── GeneralMessages_tr.properties │ │ ├── exception │ │ │ ├── ExceptionMessages.properties │ │ │ └── ExceptionMessages_tr.properties │ │ └── validation │ │ │ ├── ValidationMessages.properties │ │ │ └── ValidationMessages_tr.properties │ ├── banner.txt │ └── application.yml │ └── java │ └── com │ └── rimmelasghar │ └── boilerplate │ └── springboot │ ├── model │ ├── UserRole.java │ ├── User.java │ └── Book.java │ ├── repository │ ├── BookRepository.java │ └── UserRepository.java │ ├── security │ ├── dto │ │ ├── LoginResponse.java │ │ ├── RegistrationResponse.java │ │ ├── AuthenticatedUserDto.java │ │ ├── LoginRequest.java │ │ └── RegistrationRequest.java │ ├── jwt │ │ ├── JwtProperties.java │ │ ├── JwtAuthenticationEntryPoint.java │ │ ├── JwtTokenService.java │ │ ├── JwtTokenManager.java │ │ └── JwtAuthenticationFilter.java │ ├── service │ │ ├── UserService.java │ │ ├── UserDetailsServiceImpl.java │ │ └── UserServiceImpl.java │ ├── mapper │ │ └── UserMapper.java │ └── utils │ │ └── SecurityConstants.java │ ├── exceptions │ ├── RegistrationException.java │ ├── ApiExceptionResponse.java │ ├── ValidationErrorResponse.java │ ├── RegistrationControllerAdvice.java │ ├── LoginControllerAdvice.java │ └── ValidationAdvice.java │ ├── service │ ├── BookService.java │ ├── Impl │ │ └── BookServiceImpl.java │ └── UserValidationService.java │ ├── utils │ ├── ProjectConstants.java │ ├── GeneralMessageAccessor.java │ └── ExceptionMessageAccessor.java │ ├── configuration │ ├── PasswordEncoderConfiguration.java │ ├── MessageConfiguration.java │ ├── SecurityConfiguration.java │ └── SwaggerConfiguration.java │ ├── controller │ ├── HealthController.java │ ├── LoginController.java │ ├── RegistrationController.java │ └── BookController.java │ └── SpringBootBoilerplateApplication.java ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── Dockerfile ├── .dockerignore ├── .gitignore ├── docker-compose.yml ├── local-docker-compose.yml ├── README.md ├── pom.xml └── LICENSE /imgs/swagger-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rimmelasghar/SpringBoot-boilerPlate/HEAD/imgs/swagger-1.jpg -------------------------------------------------------------------------------- /imgs/swagger-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rimmelasghar/SpringBoot-boilerPlate/HEAD/imgs/swagger-2.jpg -------------------------------------------------------------------------------- /src/main/resources/messages/general/GeneralMessages.properties: -------------------------------------------------------------------------------- 1 | registration_successful = {0} registered successfully! -------------------------------------------------------------------------------- /src/main/resources/messages/general/GeneralMessages_tr.properties: -------------------------------------------------------------------------------- 1 | registration_successful = {0} kullanıcısı başarıyla kaydedildi. -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rimmelasghar/SpringBoot-boilerPlate/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /imgs/springboot-boilerplate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rimmelasghar/SpringBoot-boilerPlate/HEAD/imgs/springboot-boilerplate.jpg -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:17 2 | WORKDIR app 3 | ARG JAR_FILE=target/*.jar 4 | COPY ${JAR_FILE} spring-boot-boilerplate.jar 5 | EXPOSE 8080 6 | ENTRYPOINT ["java","-jar","spring-boot-boilerplate.jar"] -------------------------------------------------------------------------------- /src/main/resources/messages/exception/ExceptionMessages.properties: -------------------------------------------------------------------------------- 1 | username_already_exists = This username is already being used! 2 | email_already_exists = This email address is already being used! 3 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/model/UserRole.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.model; 2 | 3 | // rimmel asghar 4 | public enum UserRole { 5 | 6 | USER, ADMIN 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/resources/messages/exception/ExceptionMessages_tr.properties: -------------------------------------------------------------------------------- 1 | email_already_exists = Bu e-posta adresi zaten kullan\u0131l\u0131yor! 2 | username_already_exists = Bu kullan\u0131c\u0131 ad\u0131 zaten kullan\u0131l\u0131yor! 3 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.3/apache-maven-3.9.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar 3 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/repository/BookRepository.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.repository; 2 | 3 | import com.rimmelasghar.boilerplate.springboot.model.Book; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface BookRepository extends JpaRepository { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/security/dto/LoginResponse.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.security.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | // rimmel asghar 8 | @Getter 9 | @Setter 10 | @AllArgsConstructor 11 | public class LoginResponse { 12 | 13 | private String token; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/exceptions/RegistrationException.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.exceptions; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | 6 | // rimmel asghar 7 | @Getter 8 | @RequiredArgsConstructor 9 | public class RegistrationException extends RuntimeException { 10 | 11 | private final String errorMessage; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/security/dto/RegistrationResponse.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.security.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | // rimmel asghar 9 | @Getter 10 | @Setter 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | public class RegistrationResponse { 14 | 15 | private String message; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/service/BookService.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.service; 2 | import com.rimmelasghar.boilerplate.springboot.model.Book; 3 | import java.util.List; 4 | import java.util.Optional; 5 | 6 | public interface BookService { 7 | 8 | List getAllBooks(); 9 | 10 | Optional getBookById(Long id); 11 | 12 | Book saveBook(Book book); 13 | 14 | void deleteBook(Long id); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/resources/messages/validation/ValidationMessages.properties: -------------------------------------------------------------------------------- 1 | # REGISTRATION VALIDATION 2 | registration_name_not_empty=Name can not be null! 3 | registration_email_not_empty=Email can not be null! 4 | registration_username_not_empty=Username can not be null! 5 | registration_password_not_empty=Password can not be null! 6 | registration_email_is_not_valid=Please enter a valid email! 7 | # LOGIN VALIDATION 8 | login_username_not_empty=Username can not be null! 9 | login_password_not_empty=Password can not be null! 10 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/utils/ProjectConstants.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.utils; 2 | 3 | import java.util.Locale; 4 | 5 | 6 | public final class ProjectConstants { 7 | 8 | 9 | 10 | public static final String DEFAULT_ENCODING = "UTF-8"; 11 | 12 | public static final Locale PAKISTAN_LOCALE = new Locale.Builder().setLanguage("en").setRegion("PK").build(); 13 | 14 | private ProjectConstants() { 15 | 16 | throw new UnsupportedOperationException(); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.repository; 2 | 3 | import com.rimmelasghar.boilerplate.springboot.model.User; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | // rimmel asghar 7 | public interface UserRepository extends JpaRepository { 8 | 9 | User findByUsername(String username); 10 | 11 | boolean existsByEmail(String email); 12 | 13 | boolean existsByUsername(String username); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | _______ _____ ______ _____ __ _ ______ ______ _____ _____ _______ ______ _____ _____ _______ ______ _____ _______ _______ _______ 2 | |______ |_____] |_____/ | | \ | | ____ |_____] | | | | | |_____] | | | | |______ |_____/ |_____] | |_____| | |______ 3 | ______| | | \_ __|__ | \_| |_____| |_____] |_____| |_____| | |_____] |_____| __|__ |_____ |______ | \_ | |_____ | | | |______ 4 | 5 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/configuration/PasswordEncoderConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.configuration; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 6 | 7 | 8 | @Configuration 9 | public class PasswordEncoderConfiguration { 10 | 11 | @Bean 12 | public BCryptPasswordEncoder encoder() { 13 | return new BCryptPasswordEncoder(); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/resources/messages/validation/ValidationMessages_tr.properties: -------------------------------------------------------------------------------- 1 | # REGISTRATION VALIDATION 2 | registration_name_not_empty=\u0130sim bo\u015F olamaz! 3 | registration_email_not_empty=Email bo\u015F olamaz! 4 | registration_username_not_empty=Kullan\u0131c\u0131 ad\u0131 bo\u00FE olamaz! 5 | registration_password_not_empty=Parola bo\u015F olamaz! 6 | registration_email_is_not_valid=L\u00FCtfen ge\u00E7erli bir email giriniz! 7 | # LOGIN VALIDATION 8 | login.username_not_empty=Kullan\u0131c\u0131 ad\u0131 bo\u00FE olamaz! 9 | login.password_not_empty=\u015Eifre bo\u015F olamaz! 10 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/security/dto/AuthenticatedUserDto.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.security.dto; 2 | 3 | import com.rimmelasghar.boilerplate.springboot.model.UserRole; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | // rimmel asghar 9 | @Getter 10 | @Setter 11 | @NoArgsConstructor 12 | public class AuthenticatedUserDto { 13 | 14 | private String name; 15 | 16 | private String username; 17 | 18 | private String password; 19 | 20 | private UserRole userRole; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/security/dto/LoginRequest.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.security.dto; 2 | 3 | import lombok.Getter; 4 | import lombok.NoArgsConstructor; 5 | import lombok.Setter; 6 | 7 | import javax.validation.constraints.NotEmpty; 8 | 9 | // rimmel asghar 10 | @Getter 11 | @Setter 12 | @NoArgsConstructor 13 | public class LoginRequest { 14 | 15 | @NotEmpty(message = "{login_username_not_empty}") 16 | private String username; 17 | 18 | @NotEmpty(message = "{login_password_not_empty}") 19 | private String password; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/controller/HealthController.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.controller; 2 | 3 | import org.springframework.http.ResponseEntity; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | // rimmel asghar 8 | @RestController 9 | public class HealthController { 10 | 11 | @GetMapping("/health") 12 | public ResponseEntity sayHello() { 13 | 14 | return ResponseEntity.ok("Working Hot as Chili Sauce - developed by Rimmel"); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/exceptions/ApiExceptionResponse.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.exceptions; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import org.springframework.http.HttpStatus; 8 | 9 | import java.time.LocalDateTime; 10 | 11 | // rimmel asghar 12 | @Getter 13 | @Setter 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class ApiExceptionResponse { 17 | 18 | private String message; 19 | 20 | private HttpStatus status; 21 | 22 | private LocalDateTime time; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/exceptions/ValidationErrorResponse.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.exceptions; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import org.springframework.http.HttpStatus; 8 | 9 | import java.time.LocalDateTime; 10 | import java.util.List; 11 | 12 | // rimmel asghar 13 | @Getter 14 | @Setter 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | public class ValidationErrorResponse { 18 | 19 | private HttpStatus status; 20 | 21 | private LocalDateTime time; 22 | 23 | private List message; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/security/jwt/JwtProperties.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.security.jwt; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | /** 9 | * Created on October, 2022 10 | * 11 | * @author Faruk 12 | */ 13 | 14 | @Getter 15 | @Setter 16 | @Configuration 17 | @ConfigurationProperties(prefix = "jwt") 18 | public class JwtProperties { 19 | 20 | private String issuer; 21 | 22 | private String secretKey; 23 | 24 | private long expirationMinute; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Exclude development and version control directories 2 | .dockerignore 3 | .git 4 | .gitignore 5 | 6 | # Exclude build and IDE directories 7 | target/ 8 | out/ 9 | build/ 10 | .gradle/ 11 | .idea/ 12 | .vscode/ 13 | 14 | # Exclude project-specific temporary files and logs 15 | *.log 16 | *.log.* 17 | *.tmp 18 | 19 | # Exclude Maven-related files 20 | mvnw 21 | mvnw.cmd 22 | HELP.md 23 | target/ 24 | mvn-*.xml 25 | 26 | # Exclude Gradle-related files 27 | gradle/ 28 | build/ 29 | 30 | # Exclude test reports and generated documentation 31 | test-reports/ 32 | javadoc/ 33 | jacoco/ 34 | 35 | # Exclude Docker-related files (if applicable) 36 | Dockerfile 37 | docker-compose.yml 38 | .dockerignore 39 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/model/User.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.model; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.*; 6 | 7 | // rimmel asghar 8 | @Getter 9 | @Setter 10 | @Entity 11 | @Builder 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | @Table(name = "USERS") 15 | public class User { 16 | 17 | @Id 18 | @GeneratedValue(strategy = GenerationType.IDENTITY) 19 | private Long id; 20 | 21 | private String name; 22 | 23 | @Column(unique = true) 24 | private String username; 25 | 26 | private String password; 27 | 28 | private String email; 29 | 30 | @Enumerated(EnumType.STRING) 31 | private UserRole userRole; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/SpringBootBoilerplateApplication.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.EnableAspectJAutoProxy; 6 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 7 | 8 | @SpringBootApplication 9 | @EnableJpaRepositories 10 | @EnableAspectJAutoProxy 11 | public class SpringBootBoilerplateApplication { 12 | 13 | public static void main(String[] args) { 14 | 15 | SpringApplication.run(SpringBootBoilerplateApplication.class, args); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/security/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.security.service; 2 | 3 | import com.rimmelasghar.boilerplate.springboot.model.User; 4 | import com.rimmelasghar.boilerplate.springboot.security.dto.AuthenticatedUserDto; 5 | import com.rimmelasghar.boilerplate.springboot.security.dto.RegistrationRequest; 6 | import com.rimmelasghar.boilerplate.springboot.security.dto.RegistrationResponse; 7 | 8 | // rimmel asghar 9 | public interface UserService { 10 | 11 | User findByUsername(String username); 12 | 13 | RegistrationResponse registration(RegistrationRequest registrationRequest); 14 | 15 | AuthenticatedUserDto findAuthenticatedUserByUsername(String username); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/security/jwt/JwtAuthenticationEntryPoint.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.security.jwt; 2 | 3 | import org.springframework.security.core.AuthenticationException; 4 | import org.springframework.security.web.AuthenticationEntryPoint; 5 | import org.springframework.stereotype.Component; 6 | 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | import java.io.IOException; 10 | 11 | // rimmel asghar 12 | @Component 13 | public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { 14 | 15 | @Override 16 | public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { 17 | 18 | response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage()); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/security/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.security.mapper; 2 | 3 | import com.rimmelasghar.boilerplate.springboot.model.User; 4 | import com.rimmelasghar.boilerplate.springboot.security.dto.AuthenticatedUserDto; 5 | import com.rimmelasghar.boilerplate.springboot.security.dto.RegistrationRequest; 6 | import org.mapstruct.Mapper; 7 | import org.mapstruct.ReportingPolicy; 8 | import org.mapstruct.factory.Mappers; 9 | 10 | // rimmel asghar 11 | @Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE) 12 | public interface UserMapper { 13 | 14 | UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); 15 | 16 | User convertToUser(RegistrationRequest registrationRequest); 17 | 18 | AuthenticatedUserDto convertToAuthenticatedUserDto(User user); 19 | 20 | User convertToUser(AuthenticatedUserDto authenticatedUserDto); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/security/dto/RegistrationRequest.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.security.dto; 2 | 3 | import lombok.Getter; 4 | import lombok.NoArgsConstructor; 5 | import lombok.Setter; 6 | import lombok.ToString; 7 | 8 | import javax.validation.constraints.Email; 9 | import javax.validation.constraints.NotEmpty; 10 | 11 | // rimmel asghar 12 | @Getter 13 | @Setter 14 | @ToString 15 | @NoArgsConstructor 16 | public class RegistrationRequest { 17 | 18 | @NotEmpty(message = "{registration_name_not_empty}") 19 | private String name; 20 | 21 | @Email(message = "{registration_email_is_not_valid}") 22 | @NotEmpty(message = "{registration_email_not_empty}") 23 | private String email; 24 | 25 | @NotEmpty(message = "{registration_username_not_empty}") 26 | private String username; 27 | 28 | @NotEmpty(message = "{registration_password_not_empty}") 29 | private String password; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | replay_pid* 25 | 26 | HELP.md 27 | target/ 28 | !.mvn/wrapper/maven-wrapper.jar 29 | !**/src/main/**/target/ 30 | !**/src/test/**/target/ 31 | 32 | ### STS ### 33 | .apt_generated 34 | .classpath 35 | .factorypath 36 | .project 37 | .settings 38 | .springBeans 39 | .sts4-cache 40 | 41 | ### IntelliJ IDEA ### 42 | .idea 43 | *.iws 44 | *.iml 45 | *.ipr 46 | 47 | ### NetBeans ### 48 | /nbproject/private/ 49 | /nbbuild/ 50 | /dist/ 51 | /nbdist/ 52 | /.nb-gradle/ 53 | build/ 54 | !**/src/main/**/build/ 55 | !**/src/test/**/build/ 56 | 57 | ### VS Code ### 58 | .vscode/ 59 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/utils/GeneralMessageAccessor.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.utils; 2 | 3 | import org.springframework.beans.factory.annotation.Qualifier; 4 | import org.springframework.context.MessageSource; 5 | import org.springframework.stereotype.Service; 6 | 7 | import java.util.Locale; 8 | import java.util.Objects; 9 | 10 | @Service 11 | public class GeneralMessageAccessor { 12 | 13 | private final MessageSource messageSource; 14 | 15 | GeneralMessageAccessor(@Qualifier("generalMessageSource") MessageSource messageSource) { 16 | this.messageSource = messageSource; 17 | } 18 | 19 | public String getMessage(Locale locale, String key, Object... parameter) { 20 | 21 | if (Objects.isNull(locale)) { 22 | return messageSource.getMessage(key, parameter, ProjectConstants.PAKISTAN_LOCALE); 23 | } 24 | 25 | return messageSource.getMessage(key, parameter, locale); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/utils/ExceptionMessageAccessor.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.utils; 2 | 3 | import org.springframework.beans.factory.annotation.Qualifier; 4 | import org.springframework.context.MessageSource; 5 | import org.springframework.stereotype.Service; 6 | 7 | import java.util.Locale; 8 | import java.util.Objects; 9 | 10 | @Service 11 | public class ExceptionMessageAccessor { 12 | 13 | private final MessageSource messageSource; 14 | 15 | ExceptionMessageAccessor(@Qualifier("exceptionMessageSource") MessageSource messageSource) { 16 | this.messageSource = messageSource; 17 | } 18 | 19 | public String getMessage(Locale locale, String key, Object... parameter) { 20 | 21 | if (Objects.isNull(locale)) { 22 | return messageSource.getMessage(key, parameter, ProjectConstants.PAKISTAN_LOCALE); 23 | } 24 | 25 | return messageSource.getMessage(key, parameter, locale); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/controller/LoginController.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.controller; 2 | 3 | import com.rimmelasghar.boilerplate.springboot.security.dto.LoginRequest; 4 | import com.rimmelasghar.boilerplate.springboot.security.dto.LoginResponse; 5 | import com.rimmelasghar.boilerplate.springboot.security.jwt.JwtTokenService; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.web.bind.annotation.*; 9 | 10 | import javax.validation.Valid; 11 | 12 | // rimmel asghar 13 | @CrossOrigin 14 | @RestController 15 | @RequiredArgsConstructor 16 | @RequestMapping("/login") 17 | public class LoginController { 18 | 19 | private final JwtTokenService jwtTokenService; 20 | 21 | @PostMapping 22 | public ResponseEntity loginRequest(@Valid @RequestBody LoginRequest loginRequest) { 23 | 24 | final LoginResponse loginResponse = jwtTokenService.getLoginResponse(loginRequest); 25 | 26 | return ResponseEntity.ok(loginResponse); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/exceptions/RegistrationControllerAdvice.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.exceptions; 2 | 3 | import com.rimmelasghar.boilerplate.springboot.controller.RegistrationController; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.bind.annotation.ExceptionHandler; 7 | import org.springframework.web.bind.annotation.RestControllerAdvice; 8 | 9 | import java.time.LocalDateTime; 10 | 11 | // rimmel asghar 12 | @RestControllerAdvice(basePackageClasses = RegistrationController.class) 13 | public class RegistrationControllerAdvice { 14 | 15 | @ExceptionHandler(RegistrationException.class) 16 | ResponseEntity handleRegistrationException(RegistrationException exception) { 17 | 18 | final ApiExceptionResponse response = new ApiExceptionResponse(exception.getErrorMessage(), HttpStatus.BAD_REQUEST, LocalDateTime.now()); 19 | 20 | return ResponseEntity.status(response.getStatus()).body(response); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/exceptions/LoginControllerAdvice.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.exceptions; 2 | 3 | import com.rimmelasghar.boilerplate.springboot.controller.LoginController; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.security.authentication.BadCredentialsException; 7 | import org.springframework.web.bind.annotation.ExceptionHandler; 8 | import org.springframework.web.bind.annotation.RestControllerAdvice; 9 | 10 | import java.time.LocalDateTime; 11 | 12 | // rimmel asghar 13 | @RestControllerAdvice(basePackageClasses = LoginController.class) 14 | public class LoginControllerAdvice { 15 | 16 | @ExceptionHandler(BadCredentialsException.class) 17 | ResponseEntity handleRegistrationException(BadCredentialsException exception) { 18 | 19 | final ApiExceptionResponse response = new ApiExceptionResponse(exception.getMessage(), HttpStatus.UNAUTHORIZED, LocalDateTime.now()); 20 | 21 | return ResponseEntity.status(response.getStatus()).body(response); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | 5 | db: 6 | container_name: db 7 | image: mysql:latest 8 | restart: always 9 | ports: 10 | - "3306:3306" # MySQL uses port 3306 11 | environment: 12 | MYSQL_ROOT_PASSWORD: example 13 | MYSQL_DATABASE: your_database_name # Replace with your desired database name 14 | MYSQL_USER: your_mysql_user # Replace with your desired MySQL username 15 | MYSQL_PASSWORD: your_mysql_password # Replace with your desired MySQL password 16 | 17 | app: 18 | container_name: app 19 | image: rimmelasghar/spring-boot-boilerplate:2.2.0 20 | ports: 21 | - "8080:8080" 22 | environment: 23 | - "SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/your_database_name" # Replace 'your_database_name' with the same name you used for MYSQL_DATABASE in the db service. 24 | - "SPRING_DATASOURCE_USERNAME=your_mysql_user" # Replace 'your_mysql_user' with the same name you used for MYSQL_USER in the db service. 25 | - "SPRING_DATASOURCE_PASSWORD=your_mysql_password" # Replace 'your_mysql_password' with the same name you used for MYSQL_PASSWORD in the db service. 26 | depends_on: 27 | - db 28 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/controller/RegistrationController.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.controller; 2 | 3 | import com.rimmelasghar.boilerplate.springboot.security.dto.RegistrationRequest; 4 | import com.rimmelasghar.boilerplate.springboot.security.dto.RegistrationResponse; 5 | import com.rimmelasghar.boilerplate.springboot.security.service.UserService; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.web.bind.annotation.*; 10 | 11 | import javax.validation.Valid; 12 | 13 | // rimmel asghar 14 | @CrossOrigin 15 | @RestController 16 | @RequiredArgsConstructor 17 | @RequestMapping("/register") 18 | public class RegistrationController { 19 | 20 | private final UserService userService; 21 | 22 | @PostMapping 23 | public ResponseEntity registrationRequest(@Valid @RequestBody RegistrationRequest registrationRequest) { 24 | 25 | final RegistrationResponse registrationResponse = userService.registration(registrationRequest); 26 | 27 | return ResponseEntity.status(HttpStatus.CREATED).body(registrationResponse); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/service/Impl/BookServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.service.Impl; 2 | import com.rimmelasghar.boilerplate.springboot.model.Book; 3 | import com.rimmelasghar.boilerplate.springboot.repository.BookRepository; 4 | import com.rimmelasghar.boilerplate.springboot.service.BookService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | @Service 12 | public class BookServiceImpl implements BookService { 13 | 14 | private final BookRepository bookRepository; 15 | 16 | @Autowired 17 | public BookServiceImpl(BookRepository bookRepository) { 18 | this.bookRepository = bookRepository; 19 | } 20 | 21 | @Override 22 | public List getAllBooks() { 23 | return bookRepository.findAll(); 24 | } 25 | 26 | @Override 27 | public Optional getBookById(Long id) { 28 | return bookRepository.findById(id); 29 | } 30 | 31 | @Override 32 | public Book saveBook(Book book) { 33 | return bookRepository.save(book); 34 | } 35 | 36 | @Override 37 | public void deleteBook(Long id) { 38 | bookRepository.deleteById(id); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /local-docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | 5 | db: 6 | image: mysql:5.7 7 | container_name: db 8 | restart: always 9 | ports: 10 | - "3306:3306" # MySQL uses port 3306 11 | environment: 12 | MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} # Set your desired MySQL root password 13 | MYSQL_DATABASE: ${DB_NAME} # Replace with your desired database name 14 | MYSQL_USER: ${DB_USER} # Replace with your desired MySQL username 15 | MYSQL_PASSWORD: ${DB_PASSWORD} # Replace with your desired MySQL password 16 | volumes: 17 | - db_data:/var/lib/mysql 18 | 19 | app: 20 | container_name: app 21 | build: 22 | context: . 23 | ports: 24 | - "8080:8080" 25 | environment: 26 | - "SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/${DB_NAME}" # Replace 'your_database_name' with the same name you used for MYSQL_DATABASE in the db service. 27 | - "SPRING_DATASOURCE_USERNAME=${DB_USER}" # Replace 'your_mysql_user' with the same name you used for MYSQL_USER in the db service. 28 | - "SPRING_DATASOURCE_PASSWORD=${DB_PASSWORD}" # Replace 'your_mysql_password' with the same name you used for MYSQL_PASSWORD in the db service. 29 | depends_on: 30 | - db 31 | healthcheck: 32 | test: ["CMD-SHELL", "curl -f http://localhost:8080/actuator/health || exit 1"] 33 | interval: 30s 34 | timeout: 10s 35 | retries: 5 36 | 37 | volumes: 38 | db_data: 39 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/model/Book.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.model; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.GenerationType; 6 | import javax.persistence.Id; 7 | 8 | @Entity 9 | public class Book { 10 | 11 | @Id 12 | @GeneratedValue(strategy = GenerationType.IDENTITY) 13 | private Long id; 14 | private String title; 15 | private String author; 16 | private String genre; 17 | 18 | // Constructors, getters, and setters 19 | 20 | // Default constructor (required by JPA) 21 | public Book() {} 22 | 23 | public Book(String title, String author, String genre) { 24 | this.title = title; 25 | this.author = author; 26 | this.genre = genre; 27 | } 28 | 29 | // Getters and Setters 30 | 31 | public Long getId() { 32 | return id; 33 | } 34 | 35 | public void setId(Long id) { 36 | this.id = id; 37 | } 38 | 39 | public String getTitle() { 40 | return title; 41 | } 42 | 43 | public void setTitle(String title) { 44 | this.title = title; 45 | } 46 | 47 | public String getAuthor() { 48 | return author; 49 | } 50 | 51 | public void setAuthor(String author) { 52 | this.author = author; 53 | } 54 | 55 | public String getGenre() { 56 | return genre; 57 | } 58 | 59 | public void setGenre(String genre) { 60 | this.genre = genre; 61 | } 62 | } -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/exceptions/ValidationAdvice.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.exceptions; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.context.support.DefaultMessageSourceResolvable; 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.http.ResponseEntity; 7 | import org.springframework.validation.FieldError; 8 | import org.springframework.web.bind.MethodArgumentNotValidException; 9 | import org.springframework.web.bind.annotation.ExceptionHandler; 10 | import org.springframework.web.bind.annotation.RestControllerAdvice; 11 | 12 | import java.time.LocalDateTime; 13 | import java.util.List; 14 | import java.util.stream.Collectors; 15 | 16 | // rimmel asghar 17 | @Slf4j 18 | @RestControllerAdvice 19 | public class ValidationAdvice { 20 | 21 | @ExceptionHandler(MethodArgumentNotValidException.class) 22 | public final ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException exception) { 23 | 24 | final List fieldErrors = exception.getBindingResult().getFieldErrors(); 25 | final List errorList = fieldErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.toList()); 26 | 27 | final ValidationErrorResponse validationErrorResponse = new ValidationErrorResponse(HttpStatus.BAD_REQUEST, LocalDateTime.now(), errorList); 28 | 29 | log.warn("Validation errors : {} , Parameters : {}", errorList, exception.getBindingResult().getTarget()); 30 | 31 | return ResponseEntity.status(validationErrorResponse.getStatus()).body(validationErrorResponse); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | error: 4 | include-message: always 5 | 6 | spring: 7 | datasource: 8 | url: jdbc:mysql://localhost:3306/${MYSQL_DATABASE:world}?createDatabaseIfNotExist=true&autoReconnect=true&useSSL=false 9 | username: ${MYSQL_USER:root} 10 | password: ${MYSQL_PASSWORD:dbpassword} 11 | jpa: 12 | database-platform: org.hibernate.dialect.MySQL55Dialect 13 | hibernate: 14 | ddl-auto: update 15 | show-sql: true 16 | format-sql: true 17 | type: info 18 | use-sql-comments: true 19 | properties: 20 | hibernate.use_sql_comments: true 21 | hibernate.format_sql: true 22 | 23 | springdoc: 24 | show-actuator: true 25 | paths-to-match: /** 26 | packages-to-scan: com.rimmelasghar.boilerplate.springboot 27 | 28 | management: 29 | endpoint: 30 | health: 31 | show-details: ALWAYS 32 | endpoints: 33 | web: 34 | exposure: 35 | include: "*" 36 | 37 | logging: 38 | level: 39 | org.springframework: INFO 40 | com.rimmelasghar.boilerplate.springboot: INFO 41 | 42 | jwt: 43 | secretKey: secret 44 | issuer: ${JWT_ISSUER:https://github.com/rimmelasghar} 45 | expirationMinute: 10 46 | 47 | swagger: 48 | contact-name: Rimmel Asghar 49 | contact-mail: ${SWAGGER_CONTACT_MAIL:rimmelasghar4@email.com} 50 | contact-url: ${SWAGGER_CONTACT_URL:https://rimmelasghar.com} 51 | app-name: ${SWAGGER_APP_NAME:Spring Boot Boilerplate Project} 52 | app-description: "Spring Boot Boilerplate is a starter kit for developing production ready SpringBoot Applications. This project includes : Spring Boot(3.1.2), Spring Data JPA, Spring Validation, Spring Security + JWT Token, MySQL, Mapstruct, Lombok, Swagger " 53 | app-version: ${SWAGGER_APP_VERSION:2.0.0} 54 | app-license-url: ${SWAGGER_APP_LICENSE_URL:https://www.apache.org/licenses/LICENSE-2.0.html} 55 | app-license: ${SWAGGER_APP_LICENSE:Apache 2.0} 56 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/security/service/UserDetailsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.security.service; 2 | 3 | import com.rimmelasghar.boilerplate.springboot.model.UserRole; 4 | import com.rimmelasghar.boilerplate.springboot.security.dto.AuthenticatedUserDto; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 8 | import org.springframework.security.core.userdetails.User; 9 | import org.springframework.security.core.userdetails.UserDetails; 10 | import org.springframework.security.core.userdetails.UserDetailsService; 11 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 12 | import org.springframework.stereotype.Service; 13 | 14 | import java.util.Collections; 15 | import java.util.Objects; 16 | 17 | // rimmel asghar 18 | @Slf4j 19 | @Service 20 | @RequiredArgsConstructor 21 | public class UserDetailsServiceImpl implements UserDetailsService { 22 | 23 | private static final String USERNAME_OR_PASSWORD_INVALID = "Invalid username or password."; 24 | 25 | private final UserService userService; 26 | 27 | @Override 28 | public UserDetails loadUserByUsername(String username) { 29 | 30 | final AuthenticatedUserDto authenticatedUser = userService.findAuthenticatedUserByUsername(username); 31 | 32 | if (Objects.isNull(authenticatedUser)) { 33 | throw new UsernameNotFoundException(USERNAME_OR_PASSWORD_INVALID); 34 | } 35 | 36 | final String authenticatedUsername = authenticatedUser.getUsername(); 37 | final String authenticatedPassword = authenticatedUser.getPassword(); 38 | final UserRole userRole = authenticatedUser.getUserRole(); 39 | final SimpleGrantedAuthority grantedAuthority = new SimpleGrantedAuthority(userRole.name()); 40 | 41 | return new User(authenticatedUsername, authenticatedPassword, Collections.singletonList(grantedAuthority)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/security/utils/SecurityConstants.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.security.utils; 2 | 3 | import org.springframework.security.core.Authentication; 4 | import org.springframework.security.core.context.SecurityContextHolder; 5 | import org.springframework.security.core.userdetails.UserDetails; 6 | 7 | // rimmel asghar 8 | public class SecurityConstants { 9 | 10 | // FIXME : Customize security constants for your application. 11 | 12 | /** 13 | * Token expiration time 1 days. 14 | */ 15 | public static final long EXPIRATION_TIME = 24 * 60 * 60 * 1000; 16 | 17 | /** 18 | * Secret key for signature 19 | */ 20 | public static final String SECRET_KEY = "mySecretKey"; 21 | 22 | /** 23 | * The company who provided token. 24 | * You can customize issuer name, this is given as an example. 25 | */ 26 | public static final String ISSUER = "www.boilerplate.design"; 27 | 28 | /** 29 | * Token Prefix 30 | * We will use this prefix when parsing JWT Token 31 | */ 32 | public static final String TOKEN_PREFIX = "Bearer "; 33 | 34 | /** 35 | * Authorization Prefix in HttpServletRequest 36 | * Authorization: 37 | * For Example : Authorization: Bearer YWxhZGxa1qea32GVuc2VzYW1l 38 | */ 39 | public static final String HEADER_STRING = "Authorization"; 40 | 41 | public static final String LOGIN_REQUEST_URI = "/login"; 42 | 43 | public static final String REGISTRATION_REQUEST_URI = "/register"; 44 | 45 | private SecurityConstants() { 46 | 47 | throw new UnsupportedOperationException(); 48 | } 49 | 50 | /** 51 | * @return authenticated username from Security Context 52 | */ 53 | public static String getAuthenticatedUsername() { 54 | 55 | final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 56 | final UserDetails userDetails = (UserDetails) authentication.getPrincipal(); 57 | 58 | return userDetails.getUsername(); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/configuration/MessageConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.configuration; 2 | 3 | import com.rimmelasghar.boilerplate.springboot.utils.ProjectConstants; 4 | import org.springframework.context.MessageSource; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.context.support.ReloadableResourceBundleMessageSource; 8 | import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; 9 | 10 | @Configuration 11 | public class MessageConfiguration { 12 | 13 | @Bean 14 | MessageSource generalMessageSource() { 15 | 16 | final ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); 17 | messageSource.setBasename("classpath:/messages/general/GeneralMessages"); 18 | messageSource.setDefaultEncoding(ProjectConstants.DEFAULT_ENCODING); 19 | 20 | return messageSource; 21 | } 22 | 23 | @Bean 24 | MessageSource exceptionMessageSource() { 25 | 26 | final ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); 27 | messageSource.setBasename("classpath:/messages/exception/ExceptionMessages"); 28 | messageSource.setDefaultEncoding(ProjectConstants.DEFAULT_ENCODING); 29 | 30 | return messageSource; 31 | } 32 | 33 | @Bean 34 | public MessageSource validationMessageSource() { 35 | 36 | final ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); 37 | messageSource.setBasename("classpath:/messages/validation/ValidationMessages"); 38 | messageSource.setDefaultEncoding(ProjectConstants.DEFAULT_ENCODING); 39 | 40 | return messageSource; 41 | } 42 | 43 | @Bean 44 | public LocalValidatorFactoryBean getValidator() { 45 | 46 | final LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean(); 47 | bean.setValidationMessageSource(validationMessageSource()); 48 | 49 | return bean; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/security/jwt/JwtTokenService.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.security.jwt; 2 | 3 | import com.rimmelasghar.boilerplate.springboot.security.mapper.UserMapper; 4 | import com.rimmelasghar.boilerplate.springboot.security.service.UserService; 5 | import com.rimmelasghar.boilerplate.springboot.model.User; 6 | import com.rimmelasghar.boilerplate.springboot.security.dto.AuthenticatedUserDto; 7 | import com.rimmelasghar.boilerplate.springboot.security.dto.LoginRequest; 8 | import com.rimmelasghar.boilerplate.springboot.security.dto.LoginResponse; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.security.authentication.AuthenticationManager; 12 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 13 | import org.springframework.stereotype.Service; 14 | 15 | // rimmel asghar 16 | @Slf4j 17 | @Service 18 | @RequiredArgsConstructor 19 | public class JwtTokenService { 20 | 21 | private final UserService userService; 22 | 23 | private final JwtTokenManager jwtTokenManager; 24 | 25 | private final AuthenticationManager authenticationManager; 26 | 27 | public LoginResponse getLoginResponse(LoginRequest loginRequest) { 28 | 29 | final String username = loginRequest.getUsername(); 30 | final String password = loginRequest.getPassword(); 31 | 32 | final UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username, password); 33 | 34 | authenticationManager.authenticate(usernamePasswordAuthenticationToken); 35 | 36 | final AuthenticatedUserDto authenticatedUserDto = userService.findAuthenticatedUserByUsername(username); 37 | 38 | final User user = UserMapper.INSTANCE.convertToUser(authenticatedUserDto); 39 | final String token = jwtTokenManager.generateToken(user); 40 | 41 | log.info("{} has successfully logged in!", user.getUsername()); 42 | 43 | return new LoginResponse(token); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/service/UserValidationService.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.service; 2 | 3 | import com.rimmelasghar.boilerplate.springboot.utils.ExceptionMessageAccessor; 4 | import com.rimmelasghar.boilerplate.springboot.exceptions.RegistrationException; 5 | import com.rimmelasghar.boilerplate.springboot.repository.UserRepository; 6 | import com.rimmelasghar.boilerplate.springboot.security.dto.RegistrationRequest; 7 | import lombok.RequiredArgsConstructor; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.stereotype.Service; 10 | 11 | // rimmel asghar 12 | @Slf4j 13 | @Service 14 | @RequiredArgsConstructor 15 | public class UserValidationService { 16 | 17 | private static final String EMAIL_ALREADY_EXISTS = "email_already_exists"; 18 | 19 | private static final String USERNAME_ALREADY_EXISTS = "username_already_exists"; 20 | 21 | private final UserRepository userRepository; 22 | 23 | private final ExceptionMessageAccessor exceptionMessageAccessor; 24 | 25 | public void validateUser(RegistrationRequest registrationRequest) { 26 | 27 | final String email = registrationRequest.getEmail(); 28 | final String username = registrationRequest.getUsername(); 29 | 30 | checkEmail(email); 31 | checkUsername(username); 32 | } 33 | 34 | private void checkUsername(String username) { 35 | 36 | final boolean existsByUsername = userRepository.existsByUsername(username); 37 | 38 | if (existsByUsername) { 39 | 40 | log.warn("{} is already being used!", username); 41 | 42 | final String existsUsername = exceptionMessageAccessor.getMessage(null, USERNAME_ALREADY_EXISTS); 43 | throw new RegistrationException(existsUsername); 44 | } 45 | 46 | } 47 | 48 | private void checkEmail(String email) { 49 | 50 | final boolean existsByEmail = userRepository.existsByEmail(email); 51 | 52 | if (existsByEmail) { 53 | 54 | log.warn("{} is already being used!", email); 55 | 56 | final String existsEmail = exceptionMessageAccessor.getMessage(null, EMAIL_ALREADY_EXISTS); 57 | throw new RegistrationException(existsEmail); 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/configuration/SecurityConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.configuration; 2 | 3 | import com.rimmelasghar.boilerplate.springboot.security.jwt.JwtAuthenticationFilter; 4 | import com.rimmelasghar.boilerplate.springboot.security.jwt.JwtAuthenticationEntryPoint; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.security.authentication.AuthenticationManager; 9 | import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; 10 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 11 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 12 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 13 | import org.springframework.security.config.http.SessionCreationPolicy; 14 | import org.springframework.security.web.SecurityFilterChain; 15 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 16 | 17 | @Configuration 18 | @EnableWebSecurity 19 | @RequiredArgsConstructor 20 | @EnableGlobalMethodSecurity(prePostEnabled = true) 21 | public class SecurityConfiguration { 22 | 23 | private final JwtAuthenticationFilter jwtAuthenticationFilter; 24 | 25 | private final JwtAuthenticationEntryPoint unauthorizedHandler; 26 | 27 | @Bean 28 | public AuthenticationManager authenticationManager(final AuthenticationConfiguration authenticationConfiguration) throws Exception { 29 | return authenticationConfiguration.getAuthenticationManager(); 30 | } 31 | 32 | @Bean 33 | public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 34 | 35 | //@formatter:off 36 | 37 | return http.cors().and().csrf().disable() 38 | .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) 39 | .authorizeRequests() 40 | .antMatchers("/register","/health","/login","/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html", "/actuator/**").permitAll() 41 | .anyRequest().authenticated().and() 42 | .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() 43 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) 44 | .and().build(); 45 | 46 | //@formatter:on 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/configuration/SwaggerConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.configuration; 2 | 3 | import io.swagger.v3.oas.models.Components; 4 | import io.swagger.v3.oas.models.OpenAPI; 5 | import io.swagger.v3.oas.models.info.Contact; 6 | import io.swagger.v3.oas.models.info.Info; 7 | import io.swagger.v3.oas.models.info.License; 8 | import io.swagger.v3.oas.models.security.SecurityRequirement; 9 | import io.swagger.v3.oas.models.security.SecurityScheme; 10 | import lombok.Getter; 11 | import lombok.Setter; 12 | import org.springframework.boot.context.properties.ConfigurationProperties; 13 | import org.springframework.context.annotation.Bean; 14 | import org.springframework.context.annotation.Configuration; 15 | 16 | import static io.swagger.v3.oas.models.security.SecurityScheme.Type.HTTP; 17 | 18 | 19 | @Getter 20 | @Setter 21 | @Configuration 22 | @ConfigurationProperties(prefix = "swagger") 23 | public class SwaggerConfiguration { 24 | 25 | private String appName; 26 | 27 | private String appDescription; 28 | 29 | private String appVersion; 30 | 31 | private String appLicense; 32 | 33 | private String appLicenseUrl; 34 | 35 | private String contactName; 36 | 37 | private String contactUrl; 38 | 39 | private String contactMail; 40 | 41 | @Bean 42 | public OpenAPI openAPI() { 43 | 44 | final Info apiInformation = getApiInformation(); 45 | final Components components = new Components(); 46 | 47 | final String schemeName = "bearerAuth"; 48 | components.addSecuritySchemes(schemeName, new SecurityScheme().name(schemeName).type(HTTP).scheme("bearer").bearerFormat("JWT")); 49 | 50 | final OpenAPI openAPI = new OpenAPI(); 51 | openAPI.setInfo(apiInformation); 52 | openAPI.setComponents(components); 53 | openAPI.addSecurityItem(new SecurityRequirement().addList(schemeName)); 54 | 55 | return openAPI; 56 | } 57 | 58 | private Info getApiInformation() { 59 | 60 | final License license = new License(); 61 | license.setName(appLicense); 62 | license.setUrl(appLicenseUrl); 63 | 64 | final Contact contact = new Contact(); 65 | contact.setName(contactName); 66 | contact.setUrl(contactUrl); 67 | contact.setEmail(contactMail); 68 | 69 | 70 | final Info info = new Info(); 71 | info.setTitle(appName); 72 | info.setVersion(appVersion); 73 | info.setDescription(appDescription); 74 | info.setLicense(license); 75 | info.setContact(contact); 76 | 77 | return info; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/security/jwt/JwtTokenManager.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.security.jwt; 2 | 3 | import com.auth0.jwt.JWT; 4 | import com.auth0.jwt.JWTVerifier; 5 | import com.auth0.jwt.algorithms.Algorithm; 6 | import com.auth0.jwt.interfaces.DecodedJWT; 7 | import com.rimmelasghar.boilerplate.springboot.model.User; 8 | import com.rimmelasghar.boilerplate.springboot.model.UserRole; 9 | import lombok.RequiredArgsConstructor; 10 | import org.springframework.stereotype.Component; 11 | 12 | import java.util.Date; 13 | 14 | // rimmel asghar 15 | @Component 16 | @RequiredArgsConstructor 17 | public class JwtTokenManager { 18 | 19 | private final JwtProperties jwtProperties; 20 | 21 | public String generateToken(User user) { 22 | 23 | final String username = user.getUsername(); 24 | final UserRole userRole = user.getUserRole(); 25 | 26 | //@formatter:off 27 | return JWT.create() 28 | .withSubject(username) 29 | .withIssuer(jwtProperties.getIssuer()) 30 | .withClaim("role", userRole.name()) 31 | .withIssuedAt(new Date()) 32 | .withExpiresAt(new Date(System.currentTimeMillis() + jwtProperties.getExpirationMinute() * 60 * 1000)) 33 | .sign(Algorithm.HMAC256(jwtProperties.getSecretKey().getBytes())); 34 | //@formatter:on 35 | } 36 | 37 | public String getUsernameFromToken(String token) { 38 | 39 | final DecodedJWT decodedJWT = getDecodedJWT(token); 40 | 41 | return decodedJWT.getSubject(); 42 | } 43 | 44 | public boolean validateToken(String token, String authenticatedUsername) { 45 | 46 | final String usernameFromToken = getUsernameFromToken(token); 47 | 48 | final boolean equalsUsername = usernameFromToken.equals(authenticatedUsername); 49 | final boolean tokenExpired = isTokenExpired(token); 50 | 51 | return equalsUsername && !tokenExpired; 52 | } 53 | 54 | private boolean isTokenExpired(String token) { 55 | 56 | final Date expirationDateFromToken = getExpirationDateFromToken(token); 57 | return expirationDateFromToken.before(new Date()); 58 | } 59 | 60 | private Date getExpirationDateFromToken(String token) { 61 | 62 | final DecodedJWT decodedJWT = getDecodedJWT(token); 63 | 64 | return decodedJWT.getExpiresAt(); 65 | } 66 | 67 | private DecodedJWT getDecodedJWT(String token) { 68 | 69 | final JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(jwtProperties.getSecretKey().getBytes())).build(); 70 | 71 | return jwtVerifier.verify(token); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/controller/BookController.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.controller; 2 | 3 | import com.rimmelasghar.boilerplate.springboot.model.Book; 4 | import com.rimmelasghar.boilerplate.springboot.service.BookService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.http.ResponseEntity; 7 | import org.springframework.web.bind.annotation.*; 8 | 9 | import java.util.List; 10 | import java.util.Optional; 11 | 12 | @RestController 13 | @RequestMapping("/api/books") 14 | public class BookController { 15 | 16 | private final BookService bookService; 17 | 18 | @Autowired 19 | public BookController(BookService bookService) { 20 | this.bookService = bookService; 21 | } 22 | 23 | @GetMapping 24 | public ResponseEntity> getAllBooks() { 25 | List books = bookService.getAllBooks(); 26 | return ResponseEntity.ok(books); 27 | } 28 | 29 | @GetMapping("/{id}") 30 | public ResponseEntity getBookById(@PathVariable Long id) { 31 | Optional book = bookService.getBookById(id); 32 | return book.map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build()); 33 | } 34 | 35 | @PostMapping 36 | public ResponseEntity addBook(@RequestBody Book book) { 37 | Book savedBook = bookService.saveBook(book); 38 | return ResponseEntity.ok(savedBook); 39 | } 40 | 41 | @PutMapping("/{id}") 42 | public ResponseEntity updateBook(@PathVariable Long id, @RequestBody Book updatedBook) { 43 | // Call the service method to update the book 44 | Optional existingBookOptional = bookService.getBookById(id); 45 | 46 | if (existingBookOptional.isPresent()) { 47 | Book existingBook = existingBookOptional.get(); 48 | existingBook.setTitle(updatedBook.getTitle()); 49 | existingBook.setAuthor(updatedBook.getAuthor()); 50 | existingBook.setGenre(updatedBook.getGenre()); 51 | 52 | // Save the updated book using the service 53 | Book savedBook = bookService.saveBook(existingBook); 54 | return ResponseEntity.ok(savedBook); 55 | } else { 56 | // Book with the given id not found 57 | return ResponseEntity.notFound().build(); 58 | } 59 | } 60 | 61 | @DeleteMapping("/{id}") 62 | public ResponseEntity deleteBook(@PathVariable Long id) { 63 | bookService.deleteBook(id); 64 | return ResponseEntity.noContent().build(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/security/service/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.security.service; 2 | 3 | import com.rimmelasghar.boilerplate.springboot.service.UserValidationService; 4 | import com.rimmelasghar.boilerplate.springboot.model.User; 5 | import com.rimmelasghar.boilerplate.springboot.model.UserRole; 6 | import com.rimmelasghar.boilerplate.springboot.security.dto.AuthenticatedUserDto; 7 | import com.rimmelasghar.boilerplate.springboot.security.dto.RegistrationRequest; 8 | import com.rimmelasghar.boilerplate.springboot.security.dto.RegistrationResponse; 9 | import com.rimmelasghar.boilerplate.springboot.security.mapper.UserMapper; 10 | import com.rimmelasghar.boilerplate.springboot.utils.GeneralMessageAccessor; 11 | import com.rimmelasghar.boilerplate.springboot.repository.UserRepository; 12 | import lombok.RequiredArgsConstructor; 13 | import lombok.extern.slf4j.Slf4j; 14 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 15 | import org.springframework.stereotype.Service; 16 | 17 | // rimmel asghar 18 | @Slf4j 19 | @Service 20 | @RequiredArgsConstructor 21 | public class UserServiceImpl implements UserService { 22 | 23 | private static final String REGISTRATION_SUCCESSFUL = "registration_successful"; 24 | 25 | private final UserRepository userRepository; 26 | 27 | private final BCryptPasswordEncoder bCryptPasswordEncoder; 28 | 29 | private final UserValidationService userValidationService; 30 | 31 | private final GeneralMessageAccessor generalMessageAccessor; 32 | 33 | @Override 34 | public User findByUsername(String username) { 35 | 36 | return userRepository.findByUsername(username); 37 | } 38 | 39 | @Override 40 | public RegistrationResponse registration(RegistrationRequest registrationRequest) { 41 | 42 | userValidationService.validateUser(registrationRequest); 43 | 44 | final User user = UserMapper.INSTANCE.convertToUser(registrationRequest); 45 | user.setPassword(bCryptPasswordEncoder.encode(user.getPassword())); 46 | user.setUserRole(UserRole.USER); 47 | 48 | userRepository.save(user); 49 | 50 | final String username = registrationRequest.getUsername(); 51 | final String registrationSuccessMessage = generalMessageAccessor.getMessage(null, REGISTRATION_SUCCESSFUL, username); 52 | 53 | log.info("{} registered successfully!", username); 54 | 55 | return new RegistrationResponse(registrationSuccessMessage); 56 | } 57 | 58 | @Override 59 | public AuthenticatedUserDto findAuthenticatedUserByUsername(String username) { 60 | 61 | final User user = findByUsername(username); 62 | 63 | return UserMapper.INSTANCE.convertToAuthenticatedUserDto(user); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/rimmelasghar/boilerplate/springboot/security/jwt/JwtAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package com.rimmelasghar.boilerplate.springboot.security.jwt; 2 | 3 | import com.rimmelasghar.boilerplate.springboot.security.service.UserDetailsServiceImpl; 4 | import com.rimmelasghar.boilerplate.springboot.security.utils.SecurityConstants; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 9 | import org.springframework.security.core.context.SecurityContext; 10 | import org.springframework.security.core.context.SecurityContextHolder; 11 | import org.springframework.security.core.userdetails.UserDetails; 12 | import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; 13 | import org.springframework.stereotype.Service; 14 | import org.springframework.web.filter.OncePerRequestFilter; 15 | 16 | import javax.servlet.FilterChain; 17 | import javax.servlet.ServletException; 18 | import javax.servlet.http.HttpServletRequest; 19 | import javax.servlet.http.HttpServletResponse; 20 | import java.io.IOException; 21 | import java.util.Objects; 22 | 23 | // rimmel asghar 24 | @Slf4j 25 | @Service 26 | @RequiredArgsConstructor 27 | public class JwtAuthenticationFilter extends OncePerRequestFilter { 28 | 29 | private final JwtTokenManager jwtTokenManager; 30 | 31 | private final UserDetailsServiceImpl userDetailsService; 32 | 33 | @Override 34 | protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException { 35 | 36 | final String requestURI = req.getRequestURI(); 37 | 38 | if (requestURI.contains(SecurityConstants.LOGIN_REQUEST_URI) || requestURI.contains(SecurityConstants.REGISTRATION_REQUEST_URI)) { 39 | chain.doFilter(req, res); 40 | return; 41 | } 42 | 43 | final String header = req.getHeader(SecurityConstants.HEADER_STRING); 44 | String username = null; 45 | String authToken = null; 46 | if (Objects.nonNull(header) && header.startsWith(SecurityConstants.TOKEN_PREFIX)) { 47 | 48 | authToken = header.replace(SecurityConstants.TOKEN_PREFIX, StringUtils.EMPTY); 49 | try { 50 | username = jwtTokenManager.getUsernameFromToken(authToken); 51 | } 52 | catch (Exception e) { 53 | log.error("Authentication Exception : {}", e.getMessage()); 54 | } 55 | } 56 | 57 | final SecurityContext securityContext = SecurityContextHolder.getContext(); 58 | 59 | if (Objects.nonNull(username) && Objects.isNull(securityContext.getAuthentication())) { 60 | 61 | final UserDetails userDetails = userDetailsService.loadUserByUsername(username); 62 | 63 | if (jwtTokenManager.validateToken(authToken, userDetails.getUsername())) { 64 | 65 | final UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); 66 | authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(req)); 67 | log.info("Authentication successful. Logged in username : {} ", username); 68 | securityContext.setAuthentication(authentication); 69 | } 70 | } 71 | 72 | chain.doFilter(req, res); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://github.com/rimmelasghar/SpringBoot-boilerPlate/blob/main/imgs/springboot-boilerplate.jpg) 2 | 3 | # Spring-Boot BoilerPlate 4 | 5 | [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) 6 | 7 | ## Description 8 | 9 | Spring Boot Boilerplate is an advanced foundation designed to facilitate the development of robust, production-ready Spring Boot applications. This comprehensive project encompasses a cutting-edge technology stack, featuring Spring Boot (version 3.1.2), Spring Data JPA, Spring Validation, Spring Security with JWT Token support, MySQL integration, Mapstruct for seamless data mapping, Lombok for concise code generation, and Swagger for streamlined API documentation. 10 | 11 | ## Table of Contents 12 | 13 | - [Installation](#installation) 14 | - [Features](#features) 15 | - [Documentation](#documentation) 16 | - [Contributing](#contributing) 17 | - [License](#license) 18 | - [Contact](#contact) 19 | 20 | ## Installation 21 | ``` 22 | $ git clone https://github.com/rimmelasghar/SpringBoot-boilerPlate.git 23 | $ cd SpringBoot-boilerPlate 24 | ``` 25 | 26 | Make sure you have docker and docker-compose installed [docker installation guide](https://docs.docker.com/compose/install/) 27 | ## Step 1: Configuration Setup 28 | create ```.env``` file in root folder. 29 | ``` 30 | DB_ROOT_PASSWORD=mySecretRootPass 31 | MYSQL_DATABASE=yourdbname 32 | MYSQL_USER=yourdbusername 33 | MYSQL_PASSWORD=dbpasswword 34 | JWT_ISSUER=https://github.com/rimmelasghar 35 | SWAGGER_CONTACT_MAIL=youremail 36 | SWAGGER_CONTACT_URL=yourwebsite 37 | SWAGGER_APP_NAME=Spring Boot Boilerplate Project 38 | SWAGGER_APP_VERSION=2.0.0 39 | SWAGGER_APP_LICENSE_URL=https://www.apache.org/licenses/LICENSE-2.0.html 40 | SWAGGER_APP_LICENSE=Apache 2.0 41 | ``` 42 | This .env file contains the essential environment variables needed for your application to run. 43 | 44 | ## Step 2: Build Docker Images 45 | Open a terminal or command prompt, navigate to your project's root folder, and run the following command to build the Docker images: 46 | ``` 47 | docker-compose build 48 | ``` 49 | This command will create Docker images based on the configurations defined in your docker-compose.yml file. 50 | ## Step 3: Start Application 51 | After the Docker images are built, run the following command to start your application: 52 | ``` 53 | docker-compose up 54 | ``` 55 | Now, your application will be up and running. You can access it in your web browser at http://localhost:8000. 56 | 57 | 58 | ## Features 59 | 60 | 1. **Spring Boot 3.1.2**: The application is built using the latest version of Spring Boot, providing a solid foundation for developing robust and efficient Spring applications. 61 | 62 | 2. **Spring Data JPA**: Spring Data JPA simplifies database access using the Java Persistence API (JPA) and provides easy-to-use repositories for interacting with the database. 63 | 64 | 3. **Spring Validation**: The application implements Spring Validation to ensure data integrity and validity, making it more reliable and secure. 65 | 66 | 4. **Spring Security + JWT Token**: Spring Security is integrated into the application to handle authentication and authorization. It uses JSON Web Tokens (JWT) for secure token-based authentication. 67 | 68 | 5. **MySQL**: The application is configured to use MySQL as the backend database, allowing for persistent data storage. 69 | 70 | 6. **Mapstruct**: Mapstruct is used to simplify the mapping between DTOs (Data Transfer Objects) and entities, reducing boilerplate code and enhancing maintainability. 71 | 72 | 7. **Lombok**: Lombok reduces the verbosity of Java code by providing annotations to automatically generate boilerplate code for getters, setters, constructors, etc. 73 | 74 | 8. **Swagger**: The application includes Swagger, a powerful tool for documenting and testing APIs. Swagger UI provides an interactive API documentation that makes it easy for developers to understand and use the API endpoints. 75 | 76 | These features collectively form a strong foundation for developing production-ready Spring Boot applications, saving development time and effort and ensuring best practices are followed throughout the development process. 77 | 78 | ## Documentation 79 | - Swagger UI: 80 | ![](https://github.com/rimmelasghar/SpringBoot-boilerPlate/blob/main/imgs/swagger-1.jpg) 81 | - Get Request: 82 | ![](https://github.com/rimmelasghar/SpringBoot-boilerPlate/blob/main/imgs/swagger-2.jpg) 83 | 84 | 85 | ## Contributing 86 | 87 | To contribute to this project, follow these steps: 88 | 89 | 1. Fork this repository. 90 | 2. Create a new branch: ```git checkout -b feature/your-feature``` 91 | 3. Make your changes and commit them: ```git commit -m 'Add some feature'``` 92 | 4. Push to the branch: ```git push origin feature/your-feature.``` 93 | 5. Submit a pull request. 94 | 95 | ## License 96 | 97 | This project is licensed under the MIT License. 98 | 99 | ## Contact 100 | 101 | 102 | Reach out to me ```rimmelasghar4@gmail.com``` 103 | 104 | made by Rimmel Asghar with ❤️ 105 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 2.7.10 10 | 11 | 12 | com.rimmelasghar 13 | springboot-boilerplate 14 | 2.2.0 15 | 16 | spring-boot-boilerplate 17 | 18 | Spring Boot Boilerplate is a starter kit. This project includes : Spring Boot(3.1.12), Spring Data JPA, Spring Validation, Spring Security + 19 | JWT Token, PostgreSQL, Mapstruct, Lombok, Swagger 20 | 21 | 22 | 23 | 24 | Rimmel Asghar 25 | rimmelasghar4@gmail.com 26 | https://rimmelasghar.com 27 | 28 | 29 | 30 | 31 | 17 32 | 4.3.0 33 | 3.12.0 34 | 1.6.15 35 | 1.5.3.Final 36 | 0.2.0 37 | 38 | 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-web 44 | 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-starter-data-jpa 49 | 50 | 51 | com.mysql 52 | mysql-connector-j 53 | runtime 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-starter-data-jpa 58 | 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-starter-validation 63 | 64 | 65 | 66 | org.springframework.boot 67 | spring-boot-starter-security 68 | 69 | 70 | 71 | org.springframework.boot 72 | spring-boot-starter-actuator 73 | 74 | 75 | 76 | com.auth0 77 | java-jwt 78 | ${jwt.version} 79 | 80 | 81 | 82 | org.postgresql 83 | postgresql 84 | runtime 85 | 86 | 87 | 88 | org.mapstruct 89 | mapstruct 90 | ${mapstruct.version} 91 | 92 | 93 | 94 | org.mapstruct 95 | mapstruct-processor 96 | ${mapstruct.version} 97 | 98 | 99 | 100 | org.projectlombok 101 | lombok 102 | provided 103 | 104 | 105 | 106 | org.projectlombok 107 | lombok-mapstruct-binding 108 | ${lombok-mapstruct-binding.version} 109 | 110 | 111 | 112 | 113 | org.apache.commons 114 | commons-lang3 115 | ${apache-commons.version} 116 | 117 | 118 | 119 | org.springdoc 120 | springdoc-openapi-ui 121 | ${openapi-swagger.version} 122 | 123 | 124 | 125 | org.springframework.boot 126 | spring-boot-starter-test 127 | test 128 | 129 | 130 | org.junit.vintage 131 | junit-vintage-engine 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | spring-boot-boilerplate 141 | 142 | 143 | 144 | 145 | org.springframework.boot 146 | spring-boot-maven-plugin 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------