From 4990b5b92cd4bd531b4513a71e133d5d31c51ca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beatrice=20Dellac=C3=A0?= Date: Wed, 7 Aug 2024 22:37:00 +0200 Subject: [PATCH] implement users resource and email based auth --- .../java/wtf/beatrice/releasehive/Main.java | 3 +- .../config/ApplicationConfiguration.java | 2 +- .../config/JWTAuthenticationFilter.java | 14 +++--- .../repository/UserRepository.java | 2 + .../releasehive/resource/UserResource.java | 46 +++++++++++++++++++ .../releasehive/service/JWTService.java | 18 ++++---- .../service/UserDetailsExtendedService.java | 25 ++++++++++ .../releasehive/service/UserService.java | 23 ++++++++++ 8 files changed, 114 insertions(+), 19 deletions(-) create mode 100644 src/main/java/wtf/beatrice/releasehive/resource/UserResource.java create mode 100644 src/main/java/wtf/beatrice/releasehive/service/UserDetailsExtendedService.java create mode 100644 src/main/java/wtf/beatrice/releasehive/service/UserService.java diff --git a/src/main/java/wtf/beatrice/releasehive/Main.java b/src/main/java/wtf/beatrice/releasehive/Main.java index d75935a..fd897fa 100644 --- a/src/main/java/wtf/beatrice/releasehive/Main.java +++ b/src/main/java/wtf/beatrice/releasehive/Main.java @@ -36,8 +36,7 @@ public class Main { transaction.commit(); users.forEach(user -> LOGGER.info("ID: {}, Name: {}", user.getUuid(), user.getUsername())); - - } +} private static final Thread shutdownHook = new Thread(() -> { HibernateManager.shutdown(); diff --git a/src/main/java/wtf/beatrice/releasehive/config/ApplicationConfiguration.java b/src/main/java/wtf/beatrice/releasehive/config/ApplicationConfiguration.java index 22d8cbd..e1485b9 100644 --- a/src/main/java/wtf/beatrice/releasehive/config/ApplicationConfiguration.java +++ b/src/main/java/wtf/beatrice/releasehive/config/ApplicationConfiguration.java @@ -24,7 +24,7 @@ public class ApplicationConfiguration @Bean UserDetailsService userDetailsService() { - return username -> userRepository.findByEmail(username) + return email -> userRepository.findByEmail(email) .orElseThrow(() -> new UsernameNotFoundException("User not found")); } diff --git a/src/main/java/wtf/beatrice/releasehive/config/JWTAuthenticationFilter.java b/src/main/java/wtf/beatrice/releasehive/config/JWTAuthenticationFilter.java index c226529..51c1a11 100644 --- a/src/main/java/wtf/beatrice/releasehive/config/JWTAuthenticationFilter.java +++ b/src/main/java/wtf/beatrice/releasehive/config/JWTAuthenticationFilter.java @@ -9,13 +9,13 @@ import org.springframework.lang.NonNull; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; 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 wtf.beatrice.releasehive.model.User; import wtf.beatrice.releasehive.service.JWTService; +import wtf.beatrice.releasehive.service.UserDetailsExtendedService; import java.io.IOException; @@ -25,11 +25,11 @@ public class JWTAuthenticationFilter extends OncePerRequestFilter private final HandlerExceptionResolver handlerExceptionResolver; private final JWTService jwtService; - private final UserDetailsService userDetailsService; + private final UserDetailsExtendedService userDetailsService; public JWTAuthenticationFilter( @Autowired JWTService jwtService, - @Autowired UserDetailsService userDetailsService, + @Autowired UserDetailsExtendedService userDetailsService, @Autowired HandlerExceptionResolver handlerExceptionResolver) { this.jwtService = jwtService; this.userDetailsService = userDetailsService; @@ -52,12 +52,12 @@ public class JWTAuthenticationFilter extends OncePerRequestFilter try { final String jwt = authHeader.substring(7); - final String userEmail = jwtService.extractUsername(jwt); + final String email = jwtService.extractEmail(jwt); Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (userEmail != null && authentication == null) { - UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail); + if (email != null && authentication == null) { + User userDetails = this.userDetailsService.loadUserByEmail(email); if (jwtService.isTokenValid(jwt, userDetails)) { UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( diff --git a/src/main/java/wtf/beatrice/releasehive/repository/UserRepository.java b/src/main/java/wtf/beatrice/releasehive/repository/UserRepository.java index 51a8cad..91b288e 100644 --- a/src/main/java/wtf/beatrice/releasehive/repository/UserRepository.java +++ b/src/main/java/wtf/beatrice/releasehive/repository/UserRepository.java @@ -11,4 +11,6 @@ import java.util.UUID; public interface UserRepository extends JpaRepository { Optional findByEmail(String email); + + Optional findByUsername(String username); } diff --git a/src/main/java/wtf/beatrice/releasehive/resource/UserResource.java b/src/main/java/wtf/beatrice/releasehive/resource/UserResource.java new file mode 100644 index 0000000..15424de --- /dev/null +++ b/src/main/java/wtf/beatrice/releasehive/resource/UserResource.java @@ -0,0 +1,46 @@ +package wtf.beatrice.releasehive.resource; + + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import wtf.beatrice.releasehive.model.User; +import wtf.beatrice.releasehive.service.UserService; + +import java.util.List; + +@RestController +@RequestMapping("/api/v1/users") +public class UserResource +{ + private final UserService userService; + + public UserResource(@Autowired UserService userService) + { + this.userService = userService; + } + + @GetMapping( + value = "/me", + produces = "application/json") + public ResponseEntity authenticatedUser() { + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + User currentUser = (User) authentication.getPrincipal(); + + return ResponseEntity.ok(currentUser); + } + + @GetMapping( + value = "/all", + produces = "application/json") + public ResponseEntity> getAllUsers() { + List users = userService.getAllUsers(); + return ResponseEntity.ok(users); + } + +} diff --git a/src/main/java/wtf/beatrice/releasehive/service/JWTService.java b/src/main/java/wtf/beatrice/releasehive/service/JWTService.java index 5e3193a..3919c90 100644 --- a/src/main/java/wtf/beatrice/releasehive/service/JWTService.java +++ b/src/main/java/wtf/beatrice/releasehive/service/JWTService.java @@ -6,8 +6,8 @@ import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.security.Keys; import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Service; +import wtf.beatrice.releasehive.model.User; import java.security.Key; import java.util.Date; @@ -24,7 +24,7 @@ public class JWTService @Value("${security.jwt.expiration-time}") private long jwtExpiration; - public String extractUsername(String token) { + public String extractEmail(String token) { return extractClaim(token, Claims::getSubject); } @@ -33,11 +33,11 @@ public class JWTService return claimsResolver.apply(claims); } - public String generateToken(UserDetails userDetails) { + public String generateToken(User userDetails) { return generateToken(new HashMap<>(), userDetails); } - public String generateToken(Map extraClaims, UserDetails userDetails) { + public String generateToken(Map extraClaims, User userDetails) { return buildToken(extraClaims, userDetails, jwtExpiration); } @@ -47,22 +47,22 @@ public class JWTService private String buildToken( Map extraClaims, - UserDetails userDetails, + User userDetails, long expiration ) { return Jwts .builder() .setClaims(extraClaims) - .setSubject(userDetails.getUsername()) + .setSubject(userDetails.getEmail()) .setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() + expiration)) .signWith(getSignInKey(), SignatureAlgorithm.HS256) .compact(); } - public boolean isTokenValid(String token, UserDetails userDetails) { - final String username = extractUsername(token); - return (username.equals(userDetails.getUsername())) && !isTokenExpired(token); + public boolean isTokenValid(String token, User userDetails) { + final String email = extractEmail(token); + return (email.equals(userDetails.getEmail())) && !isTokenExpired(token); } private boolean isTokenExpired(String token) { diff --git a/src/main/java/wtf/beatrice/releasehive/service/UserDetailsExtendedService.java b/src/main/java/wtf/beatrice/releasehive/service/UserDetailsExtendedService.java new file mode 100644 index 0000000..ae23761 --- /dev/null +++ b/src/main/java/wtf/beatrice/releasehive/service/UserDetailsExtendedService.java @@ -0,0 +1,25 @@ +package wtf.beatrice.releasehive.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import wtf.beatrice.releasehive.model.User; +import wtf.beatrice.releasehive.repository.UserRepository; + +@Service +public class UserDetailsExtendedService { + + private final UserRepository userRepository; + + public UserDetailsExtendedService(@Autowired UserRepository userRepository) { + this.userRepository = userRepository; + } + + public User loadUserByUsername(String username) throws UsernameNotFoundException { + return userRepository.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException(username)); + } + + public User loadUserByEmail(String email) throws UsernameNotFoundException { + return userRepository.findByEmail(email).orElseThrow(() -> new UsernameNotFoundException(email)); + } +} diff --git a/src/main/java/wtf/beatrice/releasehive/service/UserService.java b/src/main/java/wtf/beatrice/releasehive/service/UserService.java new file mode 100644 index 0000000..ef74e84 --- /dev/null +++ b/src/main/java/wtf/beatrice/releasehive/service/UserService.java @@ -0,0 +1,23 @@ +package wtf.beatrice.releasehive.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import wtf.beatrice.releasehive.model.User; +import wtf.beatrice.releasehive.repository.UserRepository; + +import java.util.List; + +@Service +public class UserService +{ + private final UserRepository userRepository; + + public UserService(@Autowired UserRepository userRepository) { + this.userRepository = userRepository; + } + + public List getAllUsers() { + return userRepository.findAll(); + } + +}