Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
3ae568e
Add authentication, security, and JWT functionality
NourAlPha Apr 27, 2025
86d51d2
Add authentication, security, and JWT functionality
NourAlPha Apr 27, 2025
c7a6772
Update src/main/java/com/podzilla/auth/model/User.java
NourAlPha Apr 27, 2025
96bcc51
fix test error
NourAlPha Apr 27, 2025
bf83761
Merge branch 'Add-auth' of https://github.com/Podzilla/authentication…
NourAlPha Apr 27, 2025
920aad0
Create tmp
NourAlPha Apr 27, 2025
aeb7cf1
Fix authApplication
NourAlPha Apr 27, 2025
71785a1
Add refresh token
NourAlPha Apr 27, 2025
c4f7560
Add refresh token to the cookie instead of returning it back to the f…
NourAlPha Apr 27, 2025
8f9f175
Add admin role get all users api
NourAlPha Apr 28, 2025
f8f9b21
Update src/main/java/com/podzilla/auth/controller/AuthenticationContr…
NourAlPha Apr 28, 2025
0ce8b9c
Update src/main/java/com/podzilla/auth/controller/AuthenticationContr…
NourAlPha Apr 28, 2025
837add8
Update .github/workflows/ci-cd.yml
NourAlPha Apr 28, 2025
7235218
Rename signup endpoint to register and enhance refresh token management
NourAlPha Apr 28, 2025
a702c83
Add Swagger annotations for API documentation and implement custom ex…
NourAlPha Apr 28, 2025
e86ada4
Add Swagger annotations for API documentation and implement custom ex…
NourAlPha Apr 28, 2025
cd51b0e
Configure SpringDoc OpenAPI settings and permit access to Swagger UI …
NourAlPha Apr 28, 2025
251dc5b
Refactor API endpoints and rename JWTService to TokenService
NourAlPha Apr 30, 2025
fa225f6
Set server context path to /api in application properties
NourAlPha Apr 30, 2025
deb3870
Add Redis caching support and update Docker configuration for Redis s…
NourAlPha Apr 30, 2025
5068d72
Update Docker images for PostgreSQL, Grafana, Loki, Promtail, Redis, …
NourAlPha Apr 30, 2025
af4eecb
Refactor API endpoints and rename JWTService to TokenService
NourAlPha Apr 30, 2025
f5300fb
Set server context path to /api in application properties
NourAlPha Apr 30, 2025
4d13b40
Add Redis caching support and update Docker configuration for Redis s…
NourAlPha Apr 30, 2025
20c7ad4
Update Docker images for PostgreSQL, Grafana, Loki, Promtail, Redis, …
NourAlPha Apr 30, 2025
15eb1ee
Merge branch 'add-cache' of https://github.com/Podzilla/authenticatio…
NourAlPha Apr 30, 2025
715f235
Enhance global exception handling and logging for authentication erro…
NourAlPha Apr 30, 2025
2796156
Refactor authentication and token management: use builder pattern for…
NourAlPha Apr 30, 2025
6853011
Update security configuration to permit access to refresh token endpoint
NourAlPha Apr 30, 2025
0d6d721
Implement custom user details and enhance authentication: add CustomU…
NourAlPha May 1, 2025
274eb06
Create tmp
NourAlPha Apr 27, 2025
8d93c55
Fix authApplication
NourAlPha Apr 27, 2025
9c942a0
Add refresh token
NourAlPha Apr 27, 2025
b177e81
Add refresh token to the cookie instead of returning it back to the f…
NourAlPha Apr 27, 2025
70e0cef
Rename signup endpoint to register and enhance refresh token management
NourAlPha Apr 28, 2025
35b27e7
Add Swagger annotations for API documentation and implement custom ex…
NourAlPha Apr 28, 2025
aa71af9
Refactor API endpoints and rename JWTService to TokenService
NourAlPha Apr 30, 2025
b4f9750
Add Redis caching support and update Docker configuration for Redis s…
NourAlPha Apr 30, 2025
4efc873
Update Docker images for PostgreSQL, Grafana, Loki, Promtail, Redis, …
NourAlPha Apr 30, 2025
2df2cb3
Enhance global exception handling and logging for authentication erro…
NourAlPha Apr 30, 2025
18b0a65
Refactor authentication and token management: use builder pattern for…
NourAlPha Apr 30, 2025
6ca3a55
Update security configuration to permit access to refresh token endpoint
NourAlPha Apr 30, 2025
78f2eb6
Implement custom user details and enhance authentication: add CustomU…
NourAlPha May 1, 2025
dbee902
Merge branch 'add-cache' of https://github.com/Podzilla/authenticatio…
NourAlPha May 1, 2025
8a08f1d
test
NourAlPha May 1, 2025
6a182ce
tst2
NourAlPha May 1, 2025
5158aa4
Remove debug logging from login method in AuthenticationService
NourAlPha May 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
logs/
*.log
secret.env

### STS ###
.apt_generated
Expand Down
28 changes: 24 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
services:
backend:
image: openjdk:25-ea-4-jdk-oraclelinux9
container_name: auth
ports:
- "8080:8080"
env_file:
Expand All @@ -9,13 +10,15 @@ services:
- auth_db
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://auth_db:5432/authDB
- SPRING_DATA_REDIS_HOST=redis_cache
Comment thread
NourAlPha marked this conversation as resolved.
volumes:
- ./target:/app
- ./logs:/logs
command: [ "java", "-jar", "/app/auth-0.0.1-SNAPSHOT.jar" ]

auth_db:
image: postgres:latest
image: postgres:14.17
container_name: auth_db
environment:
POSTGRES_PASSWORD: 1234
POSTGRES_USER: postgres
Expand All @@ -24,13 +27,15 @@ services:
- "5432:5432"

loki:
image: grafana/loki:latest
image: grafana/loki:3.5.0
container_name: loki
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml

promtail:
image: grafana/promtail:latest
image: grafana/promtail:3.5.0
container_name: promtail
volumes:
- ./promtail-config.yml:/etc/promtail/promtail-config.yaml
- ./logs:/logs
Expand All @@ -39,8 +44,23 @@ services:
- loki

grafana:
image: grafana/grafana:latest
image: grafana/grafana:11.6.1
container_name: grafana
ports:
- "3000:3000"
depends_on:
- loki


redis_cache:
image: redis:7.4.3
container_name: redisCache
ports:
- "6379:6379"

redisinsight:
image: redis/redisinsight:2.68
container_name: redisInsight
ports:
- "5540:5540"
restart: always
2 changes: 2 additions & 0 deletions src/main/java/com/podzilla/auth/AuthApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching
public class AuthApplication {

public static void main(final String[] args) {
Expand Down
42 changes: 42 additions & 0 deletions src/main/java/com/podzilla/auth/dto/CustomUserDetails.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.podzilla.auth.dto;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.Set;

@JsonIgnoreProperties(ignoreUnknown = true)
public class CustomUserDetails implements UserDetails {

private String username;
private String password;
private Set<GrantedAuthority> authorities;

public CustomUserDetails() {
// No-arg constructor required by Jackson
}

public CustomUserDetails(final String username, final String password,
final Set<GrantedAuthority> authorities) {
this.username = username;
this.password = password;
this.authorities = authorities;
}

@Override
public String getUsername() {
return username;
}

@Override
public String getPassword() {
return password;
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,31 @@

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@RestControllerAdvice
public class GlobalExceptionHandler {
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {


@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<ErrorResponse> handleAccessDeniedException(
final AccessDeniedException exception) {
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body(new ErrorResponse(exception.getMessage(),
HttpStatus.FORBIDDEN));
}

@ExceptionHandler(AuthenticationException.class)
public ResponseEntity<ErrorResponse> handleAuthenticationException(
final AuthenticationException exception) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(new ErrorResponse(exception.getMessage(),
HttpStatus.UNAUTHORIZED));
}

@ExceptionHandler(NotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFoundException(
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/com/podzilla/auth/model/RefreshToken.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Column;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

Expand All @@ -21,8 +23,10 @@
@Entity
@Table(name = "refresh_tokens")
@Getter
@Builder
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EntityListeners(AuditingEntityListener.class)
public class RefreshToken {

Expand Down
38 changes: 38 additions & 0 deletions src/main/java/com/podzilla/auth/redis/RedisCacheConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.podzilla.auth.redis;

import com.podzilla.auth.dto.CustomUserDetails;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;

import java.time.Duration;

@Configuration
public class RedisCacheConfig {

private static final int CACHE_TTL = 60 * 60;

@Bean
public RedisCacheManager cacheManager(
final RedisConnectionFactory redisConnectionFactory) {

RedisCacheConfiguration defaultConfig = RedisCacheConfiguration
.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(CACHE_TTL))
.disableCachingNullValues()
.serializeValuesWith(
RedisSerializationContext.
SerializationPair.
fromSerializer(
new Jackson2JsonRedisSerializer<>(
CustomUserDetails.class)));

return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(defaultConfig)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ protected void doFilterInternal(final HttpServletRequest request,
context.setAuthentication(authToken);
SecurityContextHolder.setContext(context);


LOGGER.info("User {} authenticated", userEmail);
} catch (Exception e) {
LOGGER.error("Invalid JWT token: {}", e.getMessage());
}
Expand Down

This file was deleted.

19 changes: 8 additions & 11 deletions src/main/java/com/podzilla/auth/security/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
Expand Down Expand Up @@ -35,22 +34,20 @@ SecurityFilterChain securityFilterChain(final HttpSecurity http)
.csrf(AbstractHttpConfigurer::disable)
.addFilterBefore(jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter.class)
.exceptionHandling(exceptionHandling ->
exceptionHandling
.authenticationEntryPoint(
new RestAuthenticationEntryPoint())
)
.authorizeHttpRequests((auth) ->
auth.requestMatchers(
HttpMethod.GET, "public_resource")
auth
.requestMatchers("/auth/login")
.permitAll()
.requestMatchers("/auth/register")
.permitAll()
.requestMatchers("/auth/refresh-token")
.permitAll()
.requestMatchers("/auth/**").permitAll()
.requestMatchers("/admin/**")
.hasRole("ADMIN")
.requestMatchers("/swagger-ui/**",
"/v3/api-docs/**").permitAll()
"/v3/api-docs/**")
.permitAll()
.anyRequest().authenticated()

)
.sessionManagement(s -> s
.sessionCreationPolicy(
Expand Down
21 changes: 15 additions & 6 deletions src/main/java/com/podzilla/auth/service/AuthenticationService.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,22 +65,31 @@ public String login(final LoginRequest loginRequest,
}

public void registerAccount(final SignupRequest signupRequest) {
if (signupRequest.getPassword().isEmpty()) {
throw new ValidationException("Password cannot be empty.");
}

if (userRepository.existsByEmail(signupRequest.getEmail())) {
throw new ValidationException("Email already in use.");
}

User account = new User(
signupRequest.getName(),
signupRequest.getEmail(),
passwordEncoder.encode(signupRequest.getPassword()));
User account =
User.builder()
.name(signupRequest.getName())
.email(signupRequest.getEmail())
.password(
passwordEncoder.encode(
signupRequest.getPassword()))
.build();
Role role = roleRepository.findByErole(ERole.ROLE_USER).orElse(null);
account.setRoles(Collections.singleton(role));
userRepository.save(account);
}

public void logoutUser(final HttpServletResponse response) {
public void logoutUser(
final HttpServletResponse response) {
tokenService.removeAccessTokenFromCookie(response);
tokenService.removeRefreshTokenFromCookie(response);
tokenService.removeRefreshTokenFromCookieAndExpire(response);
}

public String refreshToken(final HttpServletRequest request,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.podzilla.auth.service;

import com.podzilla.auth.dto.CustomUserDetails;
import com.podzilla.auth.exception.NotFoundException;
import com.podzilla.auth.model.User;
import com.podzilla.auth.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
Expand All @@ -24,6 +26,7 @@ public CustomUserDetailsService(final UserRepository userRepository) {
}

@Override
@Cacheable(value = "userDetails", key = "#email")
public UserDetails loadUserByUsername(final String email) {
User user = userRepository.findByEmail(email)
.orElseThrow(() ->
Expand All @@ -37,7 +40,7 @@ public UserDetails loadUserByUsername(final String email) {
role.getErole().name()))
.collect(Collectors.toSet());

return new org.springframework.security.core.userdetails.User(
return new CustomUserDetails(
user.getEmail(),
user.getPassword(),
authorities
Expand Down
Loading