implement users resource and email based auth
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Bea 2024-08-07 22:37:00 +02:00
parent fea42d33eb
commit 4990b5b92c
8 changed files with 114 additions and 19 deletions

@ -36,8 +36,7 @@ public class Main {
transaction.commit(); transaction.commit();
users.forEach(user -> LOGGER.info("ID: {}, Name: {}", user.getUuid(), user.getUsername())); users.forEach(user -> LOGGER.info("ID: {}, Name: {}", user.getUuid(), user.getUsername()));
}
}
private static final Thread shutdownHook = new Thread(() -> { private static final Thread shutdownHook = new Thread(() -> {
HibernateManager.shutdown(); HibernateManager.shutdown();

@ -24,7 +24,7 @@ public class ApplicationConfiguration
@Bean @Bean
UserDetailsService userDetailsService() { UserDetailsService userDetailsService() {
return username -> userRepository.findByEmail(username) return email -> userRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException("User not found")); .orElseThrow(() -> new UsernameNotFoundException("User not found"));
} }

@ -9,13 +9,13 @@ import org.springframework.lang.NonNull;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder; 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.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.HandlerExceptionResolver;
import wtf.beatrice.releasehive.model.User;
import wtf.beatrice.releasehive.service.JWTService; import wtf.beatrice.releasehive.service.JWTService;
import wtf.beatrice.releasehive.service.UserDetailsExtendedService;
import java.io.IOException; import java.io.IOException;
@ -25,11 +25,11 @@ public class JWTAuthenticationFilter extends OncePerRequestFilter
private final HandlerExceptionResolver handlerExceptionResolver; private final HandlerExceptionResolver handlerExceptionResolver;
private final JWTService jwtService; private final JWTService jwtService;
private final UserDetailsService userDetailsService; private final UserDetailsExtendedService userDetailsService;
public JWTAuthenticationFilter( public JWTAuthenticationFilter(
@Autowired JWTService jwtService, @Autowired JWTService jwtService,
@Autowired UserDetailsService userDetailsService, @Autowired UserDetailsExtendedService userDetailsService,
@Autowired HandlerExceptionResolver handlerExceptionResolver) { @Autowired HandlerExceptionResolver handlerExceptionResolver) {
this.jwtService = jwtService; this.jwtService = jwtService;
this.userDetailsService = userDetailsService; this.userDetailsService = userDetailsService;
@ -52,12 +52,12 @@ public class JWTAuthenticationFilter extends OncePerRequestFilter
try { try {
final String jwt = authHeader.substring(7); final String jwt = authHeader.substring(7);
final String userEmail = jwtService.extractUsername(jwt); final String email = jwtService.extractEmail(jwt);
Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (userEmail != null && authentication == null) { if (email != null && authentication == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail); User userDetails = this.userDetailsService.loadUserByEmail(email);
if (jwtService.isTokenValid(jwt, userDetails)) { if (jwtService.isTokenValid(jwt, userDetails)) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(

@ -11,4 +11,6 @@ import java.util.UUID;
public interface UserRepository extends JpaRepository<User, UUID> { public interface UserRepository extends JpaRepository<User, UUID> {
Optional<User> findByEmail(String email); Optional<User> findByEmail(String email);
Optional<User> findByUsername(String username);
} }

@ -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<User> authenticatedUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User currentUser = (User) authentication.getPrincipal();
return ResponseEntity.ok(currentUser);
}
@GetMapping(
value = "/all",
produces = "application/json")
public ResponseEntity<List<User>> getAllUsers() {
List<User> users = userService.getAllUsers();
return ResponseEntity.ok(users);
}
}

@ -6,8 +6,8 @@ import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys; import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import wtf.beatrice.releasehive.model.User;
import java.security.Key; import java.security.Key;
import java.util.Date; import java.util.Date;
@ -24,7 +24,7 @@ public class JWTService
@Value("${security.jwt.expiration-time}") @Value("${security.jwt.expiration-time}")
private long jwtExpiration; private long jwtExpiration;
public String extractUsername(String token) { public String extractEmail(String token) {
return extractClaim(token, Claims::getSubject); return extractClaim(token, Claims::getSubject);
} }
@ -33,11 +33,11 @@ public class JWTService
return claimsResolver.apply(claims); return claimsResolver.apply(claims);
} }
public String generateToken(UserDetails userDetails) { public String generateToken(User userDetails) {
return generateToken(new HashMap<>(), userDetails); return generateToken(new HashMap<>(), userDetails);
} }
public String generateToken(Map<String, Object> extraClaims, UserDetails userDetails) { public String generateToken(Map<String, Object> extraClaims, User userDetails) {
return buildToken(extraClaims, userDetails, jwtExpiration); return buildToken(extraClaims, userDetails, jwtExpiration);
} }
@ -47,22 +47,22 @@ public class JWTService
private String buildToken( private String buildToken(
Map<String, Object> extraClaims, Map<String, Object> extraClaims,
UserDetails userDetails, User userDetails,
long expiration long expiration
) { ) {
return Jwts return Jwts
.builder() .builder()
.setClaims(extraClaims) .setClaims(extraClaims)
.setSubject(userDetails.getUsername()) .setSubject(userDetails.getEmail())
.setIssuedAt(new Date(System.currentTimeMillis())) .setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + expiration)) .setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(getSignInKey(), SignatureAlgorithm.HS256) .signWith(getSignInKey(), SignatureAlgorithm.HS256)
.compact(); .compact();
} }
public boolean isTokenValid(String token, UserDetails userDetails) { public boolean isTokenValid(String token, User userDetails) {
final String username = extractUsername(token); final String email = extractEmail(token);
return (username.equals(userDetails.getUsername())) && !isTokenExpired(token); return (email.equals(userDetails.getEmail())) && !isTokenExpired(token);
} }
private boolean isTokenExpired(String token) { private boolean isTokenExpired(String token) {

@ -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));
}
}

@ -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<User> getAllUsers() {
return userRepository.findAll();
}
}