Commit 8e3182b4 authored by Chamod Ishankha's avatar Chamod Ishankha

authentication and user registration, login

parent 4e940852
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
<description>baby-care-backend-service</description> <description>baby-care-backend-service</description>
<properties> <properties>
<java.version>17</java.version> <java.version>17</java.version>
<org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
</properties> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
...@@ -29,6 +30,14 @@ ...@@ -29,6 +30,14 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId> <artifactId>spring-boot-starter-security</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId> <artifactId>spring-boot-starter-validation</artifactId>
...@@ -44,11 +53,6 @@ ...@@ -44,11 +53,6 @@
<scope>runtime</scope> <scope>runtime</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId> <artifactId>spring-boot-configuration-processor</artifactId>
...@@ -69,6 +73,59 @@ ...@@ -69,6 +73,59 @@
<artifactId>spring-security-test</artifactId> <artifactId>spring-security-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.15.4</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!--jwt deps-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
<!--mappers-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
...@@ -85,7 +142,37 @@ ...@@ -85,7 +142,37 @@
</excludes> </excludes>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<arg>-Amapstruct.verbose=true</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins> </plugins>
<finalName>baby-care-service</finalName>
</build> </build>
</project> </project>
{
"type": "service_account",
"project_id": "baby-care-db",
"private_key_id": "6250427b6ce3875947e6c1bba3cf35de4d73ae01",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCPllNzLD8Kl5nt\nHdy7JnEJeVYYUQzEt7zzMl3IOOSdHX9F982rTX4Co1J6pqlzb6g1GHltaHcyDwO6\niCXDF+hg7SXQ//UC3O4sz+/LFHDuCGV9t2/R6MOH6SYiNAlg7UY+5UdUN1M2Si0C\nBEXGJ3pvKwRv25Yx4cd4ea0IviNxqnX9H6r2FfSE7OHO4f1yastu0swPsBYizP2c\niB7SeThhQz02X5ICvXN3qrET+3/QT1R95svZmTczKmOsc5mMnUmjPkzr2yxrq28Q\nd3G76sBqs2lMeOe3nCqqppRmbJBjXErx/fQDeNcHcDpWIT4bprUPFA2JOJftzxe2\n3TC339ITAgMBAAECggEAEgqO2LFOCHws7ISsh91YJZsXzL1eT9dQR/RjoTU40F70\n72sn7eayNw2jXuLS9ryoU2eMVklidKk7TDQwHnfCDG+vqTeH5wCwcnP8CrW6TMZE\nTkv+ok7PRqPuVSzZotKdWWjAkh3TeEZQqmi0BaZPNWmfyJg8Im9ee046hhcx5+WV\naghN26wX42mAb0TumdCWG2ZcBRwdJ9o32VIDOU98fD28NlRQkE/raGzKr/U6PqJI\nvLN8JFCbKemyGNKrPqnewj+SiB3MoQ7FoADtNUXEXPYHd9oRS/DY+4lmMG0CkPOG\njoiuM9vSAahwrjyLjqocEEcKFoSUlik5rP15QX6hDQKBgQDJRpr0RGI4gi9L+Xyj\n3kj22l8ACSfJ+pCRr8H36nWt6O6b4F5QrRkHdlIBs3yR90m6xrVTVmW31qcqsDhu\nae3C45ULb21FT7SReuflamMpjpn3LnlrmgSE5bAu7RqyZ4Xx67ke9/jrklZ5ouMC\n/vagSD6WcFMy5Qt4TX0nRIudNQKBgQC2oGxP+k+wZh56X7K716aoyrf+GEen4q3P\nt+1yXTY00EYRDR3YODudcO7i/fJ+vbGqOWLIRt2ljaMakHwkTRpFmT2iLn7CwWPz\nPpalMuR6aBBqrNV+shlCPhJ+Wa5LSviDlUUC6kPgSB3sc7jJ/UB/JnWjcYpRKToD\ner/EV15DJwKBgGI+cWIrgtncSaPG6ly+IlVDr70/usku4aQK6x3MtpYlrVougTrV\n0vK+gTbNWk+RKc7eMSG1eT7rqnr+uvzB/cjz9ws231pGlnewS0LC1BYzwdlSOPgZ\nnH0V9BJjr3VHbKzyB1Ejbr/llQoYRMBwnQcNXGdn+mRMQnX9NMqW/aLFAoGAP0ly\nn3iX2wJYd4HoV24RBBdUEYrqcgR8xSnw8BvbKAww68HG4OIDzriPIDxup3snMV03\nnT3pm3Bs4EzHqHDLtOi40hYYuXb5IxdCbzcmtECRx9XjGkevtyP5lq2PV+pAGqFE\nzBDQus1jCUjE9cJ8clOqmpxDGYMW5pPIFstMECsCgYBmqjb/qRjzcm7m1FW+JhO9\noJsqRNQ/5tYAw+FvmbwaQ3ySXuXGwHlzMu7neaNMGJc3GSSucj6wauTJKCBp4UhP\nmpK/YJ5lCjnbl2ZxNGJNjtEnCLWbkvLxg9++aZ0lsxbGiEVGW6V9WZ/RfcpAJQNa\neN73MvA+QbV2+8h28CKGfA==\n-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-j785c@baby-care-db.iam.gserviceaccount.com",
"client_id": "116620214787799634875",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-j785c%40baby-care-db.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}
...@@ -2,6 +2,7 @@ package com.kaluwa.enterprises.babycarebackendservice; ...@@ -2,6 +2,7 @@ package com.kaluwa.enterprises.babycarebackendservice;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication @SpringBootApplication
public class BabyCareBackendServiceApplication { public class BabyCareBackendServiceApplication {
...@@ -11,3 +12,4 @@ public class BabyCareBackendServiceApplication { ...@@ -11,3 +12,4 @@ public class BabyCareBackendServiceApplication {
} }
} }
package com.kaluwa.enterprises.babycarebackendservice.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
return objectMapper;
}
}
package com.kaluwa.enterprises.babycarebackendservice.config;
import com.kaluwa.enterprises.babycarebackendservice.error.AuthorizationAlertException;
import com.kaluwa.enterprises.babycarebackendservice.service.impl.BabyCareUserDetailsService;
import com.kaluwa.enterprises.babycarebackendservice.utils.JWTUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.HandlerExceptionResolver;
import java.io.IOException;
@Component
public class AuthenticationFilter extends OncePerRequestFilter {
private final JWTUtils jwtUtils;
private final BabyCareUserDetailsService userDetailsService;
@Autowired
@Qualifier("handlerExceptionResolver")
private HandlerExceptionResolver resolver;
public AuthenticationFilter(JWTUtils jwtUtils, BabyCareUserDetailsService userDetailsService) {
this.jwtUtils = jwtUtils;
this.userDetailsService = userDetailsService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
final String authHeader = request.getHeader("Authorization");
final String jwtToken;
final String userEmail;
if (authHeader == null || authHeader.isBlank() || !authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
jwtToken = authHeader.substring(7);
userEmail = jwtUtils.extractUsername(jwtToken);
if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(userEmail);
if (jwtUtils.isTokenValid(jwtToken, userDetails)) {
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities()
);
token.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
securityContext.setAuthentication(token);
SecurityContextHolder.setContext(securityContext);
}
}
filterChain.doFilter(request, response);
} catch (Exception e) {
e.printStackTrace();
resolver.resolveException(request, response, null, new AuthorizationAlertException(e.getMessage(), "error", "unauthorized"));
}
}
}
package com.kaluwa.enterprises.babycarebackendservice.config;
import com.kaluwa.enterprises.babycarebackendservice.service.impl.BabyCareUserDetailsService;
import jakarta.servlet.DispatcherType;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import static org.springframework.security.config.Customizer.withDefaults;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final BabyCareUserDetailsService userDetailsService;
private final AuthenticationFilter authFilter;
public SecurityConfig(BabyCareUserDetailsService userDetailsService, AuthenticationFilter authFilter) {
this.userDetailsService = userDetailsService;
this.authFilter = authFilter;
}
String[] whiteListedEndpoints = new String[]{
"/auth/login",
"/auth/register",
"/auth/forgot-password",
// swagger
"/v2/api-docs",
"/v3/api-docs",
"/v3/api-docs/**",
"/swagger-resources",
"/swagger-resources/**",
"/configuration/ui",
"/configuration/security",
"/swagger-ui/**",
"/webjars/**",
"/swagger-ui.html"
};
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(request -> request.requestMatchers(whiteListedEndpoints).permitAll()
.anyRequest()
.authenticated())
.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authenticationProvider(authenticationProvider()).addFilterBefore(
authFilter, UsernamePasswordAuthenticationFilter.class
);
return http.build();
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
return daoAuthenticationProvider;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
}
package com.kaluwa.enterprises.babycarebackendservice.config;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.info.Contact;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.info.License;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import io.swagger.v3.oas.annotations.servers.Server;
@OpenAPIDefinition(
info = @Info(
title = "BabyCare Backend API",
version = "v1.0",
description = "API for managing BabyCare backend services",
termsOfService = "Terms of service",
contact = @Contact(
name = "Kaluwa Enterprises",
email = "kaluwa.enterprises@gmail.com",
url = "https://www.kaluwa-enterprises.com"
),
license = @License(
name = "Apache 2.0",
url = "http://www.apache.org/licenses/LICENSE-2.0.html"
)
),
servers = {
@Server(
description = "Local ENV",
url = "http://localhost:8080/api/v1/baby-care"
)
}
)
@SecurityScheme(
name = "BearerAuth",
description = "Bearer Token Authentication",
scheme = "bearer",
type = SecuritySchemeType.HTTP,
bearerFormat = "JWT",
in = SecuritySchemeIn.HEADER
)
public class SwaggerConfig {
}
\ No newline at end of file
package com.kaluwa.enterprises.babycarebackendservice.constants;
public class Common {
public static final String TOKEN_TYPE = "bearer";
}
package com.kaluwa.enterprises.babycarebackendservice.constants;
public class Status {
public final static String STATUS_NEW = "NEW";
}
package com.kaluwa.enterprises.babycarebackendservice.constants;
public class TableNames {
public final static String USER_TABLE = "users";
}
package com.kaluwa.enterprises.babycarebackendservice.constants;
public class UserRoles {
public static final String ADMIN = "ADMIN";
public static final String USER = "USER";
}
package com.kaluwa.enterprises.babycarebackendservice.dao;
import com.kaluwa.enterprises.babycarebackendservice.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface UserDao extends JpaRepository<User, Long> {
Optional<User> findByEmail(String username);
boolean existsByEmail(String email);
}
package com.kaluwa.enterprises.babycarebackendservice.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AuthenticationDto {
private Long userId;
private String firstName;
private String lastName;
private String email;
private String phone;
private String role;
private String status;
private TokenDto tokenDto;
}
package com.kaluwa.enterprises.babycarebackendservice.dto;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginRequest {
@NotEmpty(message = "Email is required")
@NotNull(message = "Email is required")
private String email;
@NotEmpty(message = "Password is required")
@NotNull(message = "Password is required")
private String password;
}
package com.kaluwa.enterprises.babycarebackendservice.dto;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RegisterRequest {
@NotEmpty(message = "First name is required")
@NotNull(message = "First name is required")
private String firstName;
@NotEmpty(message = "Last name is required")
@NotNull(message = "Last name is required")
private String lastName;
@NotEmpty(message = "Email is required")
@NotNull(message = "Email is required")
private String email;
@NotEmpty(message = "Password is required")
@NotNull(message = "Password is required")
private String password;
@NotEmpty(message = "Phone is required")
@NotNull(message = "Phone is required")
private String phone;
private String role;
private String status;
}
package com.kaluwa.enterprises.babycarebackendservice.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TokenDto {
private String tokenType;
private long expiresIn;
private String token;
private String refreshToken;
}
package com.kaluwa.enterprises.babycarebackendservice.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserDto {
private Long userId;
private String firstName;
private String lastName;
private String email;
private String phone;
private String role;
private String status;
}
package com.kaluwa.enterprises.babycarebackendservice.error;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class AuthorizationAlertException extends RuntimeException {
private final String message;
private final String entityName;
private final String errorKey;
/**
* Instantiates a new Bad request alert exception.
*
* @param message the message
* @param entityName the entity name
* @param errorKey the error key
*/
public AuthorizationAlertException(String message, String entityName, String errorKey) {
this.message = message;
this.entityName = entityName;
this.errorKey = errorKey;
}
}
package com.kaluwa.enterprises.babycarebackendservice.error;
import lombok.Getter;
import lombok.Setter;
/**
* The type Bad request alert exception.
*/
@Getter
@Setter
/**
* @author : Steffan Devotta
* @version : 1.0
* @date : Sep 30, 2021
* @copyright : © 2010-2021 Informatics International Limited. All Rights Reserved
*/
public class BadRequestAlertException extends RuntimeException {
private static final long serialVersionUID = 1L;
private final String message;
private final String entityName;
private final String errorKey;
/**
* Instantiates a new Bad request alert exception.
*
* @param message the message
* @param entityName the entity name
* @param errorKey the error key
*/
public BadRequestAlertException(String message, String entityName, String errorKey) {
this.message = message;
this.entityName = entityName;
this.errorKey = errorKey;
}
}
package com.kaluwa.enterprises.babycarebackendservice.error;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.Map;
@ControllerAdvice
public class ErrorHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(BadRequestAlertException.class)
public ResponseEntity<Object> handleBadRequestAlertException(BadRequestAlertException ex, WebRequest request) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("timestamp", LocalDateTime.now().toString());
body.put("message", ex.getMessage());
body.put("error", ex.getErrorKey());
return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(AuthorizationAlertException.class)
public ResponseEntity<Object> handleAuthorizationAlertException(AuthorizationAlertException ae, WebRequest request) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("timestamp", LocalDateTime.now().toString());
body.put("error", ae.getMessage());
return new ResponseEntity<>(body, HttpStatus.UNAUTHORIZED);
}
}
package com.kaluwa.enterprises.babycarebackendservice.global;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
@Slf4j
public class CustomExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationExceptions(MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
FieldError error = result.getFieldError();
String message = error.getField()+ " : " +error.getDefaultMessage();
log.debug("Validation error: {}", message);
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setHttpCode(400);
errorResponse.setErrorCode("VALIDATION FAILED!");
errorResponse.setDescription(message);
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}
}
package com.kaluwa.enterprises.babycarebackendservice.global;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ErrorResponse {
private int httpCode;
private String errorCode;
private String description;
}
package com.kaluwa.enterprises.babycarebackendservice.mappers;
import com.kaluwa.enterprises.babycarebackendservice.dto.UserDto;
import com.kaluwa.enterprises.babycarebackendservice.model.User;
import org.mapstruct.Mapper;
import java.util.List;
@Mapper(componentModel = "spring")
public interface UserMapper {
UserDto toDto(User user);
User toEntity(UserDto userDto);
List<UserDto> listToDto(List<User> users);
}
package com.kaluwa.enterprises.babycarebackendservice.model;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
import static com.kaluwa.enterprises.babycarebackendservice.constants.TableNames.USER_TABLE;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = USER_TABLE)
public class User implements UserDetails {
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
private Long userId;
private String firstName;
private String lastName;
private String email;
private String password;
private String phone;
private String role;
private String status;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(new SimpleGrantedAuthority(role));
}
@Override
public String getUsername() {
return email;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
package com.kaluwa.enterprises.babycarebackendservice.rest;
import com.kaluwa.enterprises.babycarebackendservice.dto.AuthenticationDto;
import com.kaluwa.enterprises.babycarebackendservice.dto.LoginRequest;
import com.kaluwa.enterprises.babycarebackendservice.dto.RegisterRequest;
import com.kaluwa.enterprises.babycarebackendservice.dto.UserDto;
import com.kaluwa.enterprises.babycarebackendservice.model.User;
import com.kaluwa.enterprises.babycarebackendservice.service.UserService;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/auth")
public class AuthenticationController {
private final UserService userService;
public AuthenticationController(UserService userService) {
this.userService = userService;
}
// login
@PostMapping("/login")
public ResponseEntity<AuthenticationDto> login(@RequestBody @Valid LoginRequest loginRequest) {
log.info("Inside authentication controller login method");
return new ResponseEntity<>(userService.login(loginRequest), HttpStatus.OK);
}
// register
@PostMapping("/register")
public ResponseEntity<UserDto> register(@RequestBody @Valid RegisterRequest registerRequest) {
log.info("Inside authentication controller register method");
return new ResponseEntity<>(userService.register(registerRequest), HttpStatus.CREATED);
}
// forgot password
// change password
// logout
}
package com.kaluwa.enterprises.babycarebackendservice.rest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test")
@Slf4j
public class TestController {
@GetMapping
public ResponseEntity<String> test() {
log.info("Inside test controller");
return new ResponseEntity<>("Test OK!", HttpStatus.OK);
}
}
package com.kaluwa.enterprises.babycarebackendservice.service;
import com.kaluwa.enterprises.babycarebackendservice.dto.AuthenticationDto;
import com.kaluwa.enterprises.babycarebackendservice.dto.LoginRequest;
import com.kaluwa.enterprises.babycarebackendservice.dto.RegisterRequest;
import com.kaluwa.enterprises.babycarebackendservice.dto.UserDto;
public interface UserService {
AuthenticationDto login(LoginRequest loginRequest);
UserDto register(RegisterRequest registerRequest);
}
package com.kaluwa.enterprises.babycarebackendservice.service.impl;
import com.kaluwa.enterprises.babycarebackendservice.dao.UserDao;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class BabyCareUserDetailsService implements UserDetailsService {
private final UserDao userDao;
public BabyCareUserDetailsService(UserDao userDao) {
this.userDao = userDao;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userDao.findByEmail(username).orElseThrow();
}
}
package com.kaluwa.enterprises.babycarebackendservice.service.impl;
import com.kaluwa.enterprises.babycarebackendservice.dao.UserDao;
import com.kaluwa.enterprises.babycarebackendservice.dto.*;
import com.kaluwa.enterprises.babycarebackendservice.error.BadRequestAlertException;
import com.kaluwa.enterprises.babycarebackendservice.mappers.UserMapper;
import com.kaluwa.enterprises.babycarebackendservice.model.User;
import com.kaluwa.enterprises.babycarebackendservice.service.UserService;
import com.kaluwa.enterprises.babycarebackendservice.utils.JWTUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import static com.kaluwa.enterprises.babycarebackendservice.constants.Common.TOKEN_TYPE;
import static com.kaluwa.enterprises.babycarebackendservice.constants.Status.STATUS_NEW;
import static com.kaluwa.enterprises.babycarebackendservice.constants.UserRoles.USER;
import static com.kaluwa.enterprises.babycarebackendservice.utils.JWTUtils.EXPIRATION_TIME;
@Service
@Slf4j
public class UserServiceImpl implements UserService {
private final UserDao userDao;
private final BabyCareUserDetailsService userDetailsService;
private final JWTUtils jwtUtils;
private final PasswordEncoder passwordEncoder;
private final AuthenticationManager authenticationManager;
private final UserMapper userMapper;
public UserServiceImpl(UserDao userDao, BabyCareUserDetailsService userDetailsService, JWTUtils jwtUtils, PasswordEncoder passwordEncoder, AuthenticationManager authenticationManager, UserMapper userMapper) {
this.userDao = userDao;
this.userDetailsService = userDetailsService;
this.jwtUtils = jwtUtils;
this.passwordEncoder = passwordEncoder;
this.authenticationManager = authenticationManager;
this.userMapper = userMapper;
}
@Override
public AuthenticationDto login(LoginRequest loginRequest) {
log.info("Inside user service login method");
AuthenticationDto authenticationDto = new AuthenticationDto();
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getEmail(), loginRequest.getPassword()));
User user = userDao.findByEmail(loginRequest.getEmail()).orElseThrow();
String jwt = jwtUtils.generateToken(user);
String refreshToken = jwtUtils.generateRefreshToken(new HashMap<>(), user);
authenticationDto.setUserId(user.getUserId());
authenticationDto.setFirstName(user.getFirstName());
authenticationDto.setLastName(user.getLastName());
authenticationDto.setEmail(user.getEmail());
authenticationDto.setPhone(user.getPhone());
authenticationDto.setRole(user.getRole());
authenticationDto.setStatus(user.getStatus());
TokenDto tokenDto = new TokenDto();
tokenDto.setTokenType(TOKEN_TYPE);
tokenDto.setToken(jwt);
tokenDto.setRefreshToken(refreshToken);
tokenDto.setExpiresIn(EXPIRATION_TIME);
authenticationDto.setTokenDto(tokenDto);
return authenticationDto;
} catch (Exception e) {
throw new BadRequestAlertException(e.getMessage(), "User", "User login failed");
}
}
@Override
public UserDto register(RegisterRequest registerRequest) {
log.info("Inside user service register method");
UserDto userDto = new UserDto();
try {
// validation
if (userDao.existsByEmail(registerRequest.getEmail())) {
throw new BadRequestAlertException("Email already exists", "User", "User registration failed");
}
userDto.setFirstName(registerRequest.getFirstName());
userDto.setLastName(registerRequest.getLastName());
userDto.setEmail(registerRequest.getEmail());
userDto.setPhone(registerRequest.getPhone());
userDto.setRole(USER);
userDto.setStatus(STATUS_NEW);
User user = userMapper.toEntity(userDto);
user.setPassword(passwordEncoder.encode(registerRequest.getPassword()));
userDto = userMapper.toDto(userDao.save(user));
return userDto;
} catch (Exception e) {
throw new BadRequestAlertException(e.getMessage(), "User", "User registration failed");
}
}
}
package com.kaluwa.enterprises.babycarebackendservice.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.function.Function;
@Component
public class JWTUtils {
private SecretKey key;
public static final long EXPIRATION_TIME = 86400000; // 24 hours
public JWTUtils() {
String secretString = "65465454513515456489879874512132154987745372522283393725456679995342135446687687";
byte[] keyBytes = Base64.getDecoder().decode(secretString.getBytes(StandardCharsets.UTF_8));
this.key = new SecretKeySpec(keyBytes, "HmacSHA256");
}
public String generateToken(UserDetails userDetails) {
return Jwts.builder()
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(key)
.compact();
}
public String generateRefreshToken(HashMap<String, Object> claims, UserDetails userDetails) {
return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(key)
.compact();
}
public String extractUsername(String token) {
return extractClaims(token, Claims::getSubject);
}
private <T> T extractClaims(String token, Function<Claims, T> claimsTFunction) {
return claimsTFunction.apply(
Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody()
);
}
public boolean isTokenValid(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
private boolean isTokenExpired(String token) {
return extractClaims(token, Claims::getExpiration).before(new Date());
}
}
spring: spring:
application: application:
name: baby-care-backend-service name: baby-care-backend-service
security:
user:
name: user
password: password
datasource:
url: jdbc:mysql://localhost:3306/baby_care_db?useSSL=false&serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
format_sql: true
server: server:
port: 8080 port: 8080
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment