-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: Add User authentification #42
Open
syphax-bouazzouni
wants to merge
9
commits into
main
Choose a base branch
from
feature/add-user-auth
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+594
−17
Open
Changes from 7 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
47d8d86
update deploy CI to use gradle
syphax-bouazzouni 4e58d1c
add PostgreSQL database and H2 for test
syphax-bouazzouni d55e991
add user model and repository layers
syphax-bouazzouni 0fe3b3f
add jwt token generator and filter validator
syphax-bouazzouni 43e5d66
add spring security using the jwtAuthenticationFilter and users
syphax-bouazzouni 042472f
add AuthController to register, login and logout users
syphax-bouazzouni 1ff5994
add a global exception handler
syphax-bouazzouni a3d7ee3
add properties tests for jwt
syphax-bouazzouni 2c04796
add better swagger documentation for the authentication features
syphax-bouazzouni File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,6 @@ | ||
FROM openjdk:11-jre | ||
RUN rm -rf /usr/local/tomcat/webapps/* | ||
RUN mkdir -p /logs | ||
COPY ./target/API-Gateway-0.0.1-SNAPSHOT.jar /usr/local/tomcat/webapps/API-Gateway-0.0.1-SNAPSHOT.jar | ||
COPY ./build/libs/api-gateway-1.0-SNAPSHOT.jar /usr/local/tomcat/webapps/API-Gateway-0.0.1-SNAPSHOT.jar | ||
EXPOSE 8080 | ||
CMD ["sh","-c", "java -jar /usr/local/tomcat/webapps/API-Gateway-0.0.1-SNAPSHOT.jar"] | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
71 changes: 71 additions & 0 deletions
71
src/main/java/org/semantics/apigateway/SecurityConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package org.semantics.apigateway; | ||
|
||
import jakarta.servlet.http.HttpServletResponse; | ||
import org.semantics.apigateway.service.auth.AuthService; | ||
import org.semantics.apigateway.service.auth.JwtAuthenticationFilter; | ||
import org.semantics.apigateway.service.auth.TokenBlacklist; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.security.authentication.AuthenticationManager; | ||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; | ||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; | ||
import org.springframework.security.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; | ||
|
||
@Configuration | ||
@EnableWebSecurity | ||
public class SecurityConfig { | ||
|
||
private final AuthService userDetailsService; | ||
private final TokenBlacklist tokenBlacklist; | ||
private final JwtAuthenticationFilter jwtAuthenticationFilter; | ||
|
||
public SecurityConfig(AuthService userDetailsService, TokenBlacklist tokenBlacklist, JwtAuthenticationFilter jwtAuthenticationFilter) { | ||
this.userDetailsService = userDetailsService; | ||
this.tokenBlacklist = tokenBlacklist; | ||
this.jwtAuthenticationFilter = jwtAuthenticationFilter; | ||
} | ||
|
||
@Bean | ||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { | ||
http | ||
.csrf(AbstractHttpConfigurer::disable) | ||
.authorizeHttpRequests(auth -> auth | ||
.requestMatchers("/user/**").authenticated() | ||
.anyRequest().permitAll() | ||
) | ||
.addFilterBefore(jwtAuthenticationFilter, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.class) | ||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) | ||
.logout(logout -> logout | ||
.logoutUrl("/auth/logout") | ||
.logoutSuccessHandler((request, response, authentication) -> { | ||
String token = request.getHeader("Authorization"); | ||
if (token != null && token.startsWith("Bearer ")) { | ||
token = token.substring(7); | ||
tokenBlacklist.blacklistToken(token); | ||
} | ||
response.setStatus(HttpServletResponse.SC_OK); | ||
response.getWriter().write("You have been logged out successfully."); | ||
}) | ||
); | ||
|
||
return http.build(); | ||
} | ||
|
||
@Bean | ||
public AuthenticationManager authManager(HttpSecurity http) throws Exception { | ||
AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class); | ||
authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); | ||
return authenticationManagerBuilder.build(); | ||
} | ||
|
||
@Bean | ||
public PasswordEncoder passwordEncoder() { | ||
return new BCryptPasswordEncoder(); | ||
} | ||
|
||
} |
75 changes: 75 additions & 0 deletions
75
src/main/java/org/semantics/apigateway/controller/AuthController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package org.semantics.apigateway.controller; | ||
|
||
import io.swagger.v3.oas.annotations.Operation; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.validation.Valid; | ||
import org.semantics.apigateway.model.responses.SuccessResponse; | ||
import org.semantics.apigateway.model.user.*; | ||
import org.semantics.apigateway.service.auth.JwtUtil; | ||
import org.semantics.apigateway.service.auth.UserRepository; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.security.authentication.AuthenticationManager; | ||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.GrantedAuthority; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
import org.springframework.security.crypto.password.PasswordEncoder; | ||
import org.springframework.web.bind.annotation.*; | ||
|
||
import java.util.Collections; | ||
import java.util.Date; | ||
|
||
@RestController | ||
@RequestMapping("/auth") | ||
public class AuthController { | ||
|
||
private final AuthenticationManager authenticationManager; | ||
private final JwtUtil jwtUtil; | ||
private final UserRepository userRepository; | ||
private final PasswordEncoder passwordEncoder; | ||
|
||
public AuthController(AuthenticationManager authenticationManager, JwtUtil jwtUtil, | ||
UserRepository userRepository, PasswordEncoder passwordEncoder) { | ||
this.authenticationManager = authenticationManager; | ||
this.jwtUtil = jwtUtil; | ||
this.userRepository = userRepository; | ||
this.passwordEncoder = passwordEncoder; | ||
} | ||
|
||
@PostMapping("/register") | ||
@Operation(tags = {"Users"}) | ||
@ResponseStatus(HttpStatus.CREATED) | ||
public SuccessResponse registerUser(@Valid @RequestBody RegisterRequest user) { | ||
User newUser = new User(); | ||
newUser.setUsername(user.getUsername()); | ||
newUser.setPassword(passwordEncoder.encode(user.getPassword())); | ||
newUser.setRoles(Collections.singleton(Role.USER)); | ||
userRepository.save(newUser); | ||
|
||
return new SuccessResponse( | ||
"User created successfully", | ||
"success"); | ||
} | ||
|
||
@PostMapping("/login") | ||
@Operation(tags = {"Users"}) | ||
public AuthResponse loginUser(@RequestBody LoginRequest loginRequest) { | ||
Authentication authentication = authenticationManager.authenticate( | ||
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword())); | ||
String token = jwtUtil.generateToken(authentication.getName()); | ||
UserDetails userDetails = (UserDetails) authentication.getPrincipal(); | ||
|
||
Date expiration = jwtUtil.extractExpiration(token); | ||
GrantedAuthority role = userDetails.getAuthorities().stream().findFirst().orElse(null); | ||
return new AuthResponse(token, loginRequest.getUsername(), role != null ? role.getAuthority() : "", expiration); | ||
|
||
} | ||
|
||
@GetMapping("/logout") | ||
@Operation(tags = {"Users"}) | ||
public SuccessResponse logout(HttpServletRequest request) { | ||
SecurityContextHolder.clearContext(); | ||
return new SuccessResponse("Logged out successfully.", "success"); | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
src/main/java/org/semantics/apigateway/controller/GlobalExceptionHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package org.semantics.apigateway.controller; | ||
|
||
import io.jsonwebtoken.ExpiredJwtException; | ||
import io.jsonwebtoken.MalformedJwtException; | ||
import org.semantics.apigateway.model.responses.ErrorResponse; | ||
import org.semantics.apigateway.model.user.InvalidJwtException; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.security.authentication.BadCredentialsException; | ||
import org.springframework.web.bind.annotation.ControllerAdvice; | ||
import org.springframework.web.bind.annotation.ExceptionHandler; | ||
|
||
import java.security.SignatureException; | ||
|
||
@ControllerAdvice | ||
public class GlobalExceptionHandler { | ||
|
||
@ExceptionHandler(InvalidJwtException.class) | ||
public ResponseEntity<ErrorResponse> handleJwtException(InvalidJwtException ex) { | ||
ErrorResponse errorResponse = new ErrorResponse("JWT_ERROR", ex.getMessage()); | ||
return new ResponseEntity<>(errorResponse, HttpStatus.UNAUTHORIZED); | ||
} | ||
|
||
@ExceptionHandler({ExpiredJwtException.class, MalformedJwtException.class, SignatureException.class}) | ||
public ResponseEntity<ErrorResponse> handleJwtProcessingException(Exception ex) { | ||
ErrorResponse errorResponse = new ErrorResponse("JWT_ERROR", "Invalid or expired token: " + ex.getMessage()); | ||
return new ResponseEntity<>(errorResponse, HttpStatus.UNAUTHORIZED); | ||
} | ||
|
||
@ExceptionHandler({BadCredentialsException.class}) | ||
public ResponseEntity<ErrorResponse> handleBadCredentialsException(Exception ex) { | ||
ErrorResponse errorResponse = new ErrorResponse("BAD_CREDENTIALS", ex.getMessage()); | ||
return new ResponseEntity<>(errorResponse, HttpStatus.UNAUTHORIZED); | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
src/main/java/org/semantics/apigateway/model/responses/ErrorResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package org.semantics.apigateway.model.responses; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
|
||
@AllArgsConstructor | ||
@Getter | ||
public class ErrorResponse { | ||
private String error; | ||
private String message; | ||
} |
12 changes: 12 additions & 0 deletions
12
src/main/java/org/semantics/apigateway/model/responses/SuccessResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package org.semantics.apigateway.model.responses; | ||
|
||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
|
||
@AllArgsConstructor | ||
@Getter | ||
public class SuccessResponse { | ||
private String message; | ||
private String status; | ||
} |
24 changes: 24 additions & 0 deletions
24
src/main/java/org/semantics/apigateway/model/user/AuthResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package org.semantics.apigateway.model.user; | ||
|
||
import lombok.Getter; | ||
import lombok.Setter; | ||
|
||
import java.util.Date; | ||
|
||
@Setter | ||
@Getter | ||
public class AuthResponse { | ||
|
||
private String token; | ||
private String username; | ||
private String role; | ||
private Date expiration; | ||
|
||
public AuthResponse(String token, String username, String role, Date expiration) { | ||
this.token = token; | ||
this.username = username; | ||
this.role = role; | ||
this.expiration = expiration; | ||
} | ||
|
||
} |
10 changes: 10 additions & 0 deletions
10
src/main/java/org/semantics/apigateway/model/user/InvalidJwtException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package org.semantics.apigateway.model.user; | ||
|
||
import lombok.Getter; | ||
|
||
@Getter | ||
public class InvalidJwtException extends RuntimeException { | ||
public InvalidJwtException(String message) { | ||
super(message); | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
src/main/java/org/semantics/apigateway/model/user/LoginRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package org.semantics.apigateway.model.user; | ||
|
||
import lombok.Getter; | ||
import lombok.Setter; | ||
import jakarta.validation.constraints.NotNull; | ||
|
||
@Getter | ||
@Setter | ||
public class LoginRequest { | ||
|
||
@NotNull | ||
private String username; | ||
|
||
@NotNull | ||
private String password; | ||
} |
11 changes: 11 additions & 0 deletions
11
src/main/java/org/semantics/apigateway/model/user/RegisterRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package org.semantics.apigateway.model.user; | ||
|
||
import lombok.Getter; | ||
import lombok.Setter; | ||
|
||
@Getter | ||
@Setter | ||
public class RegisterRequest { | ||
String username; | ||
String password; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package org.semantics.apigateway.model.user; | ||
|
||
public enum Role { | ||
ADMIN, USER | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
need to extract this to
.env