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

View File

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

View File

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

View File

@ -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(

View File

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

View File

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

View File

@ -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<String, Object> extraClaims, UserDetails userDetails) {
public String generateToken(Map<String, Object> extraClaims, User userDetails) {
return buildToken(extraClaims, userDetails, jwtExpiration);
}
@ -47,22 +47,22 @@ public class JWTService
private String buildToken(
Map<String, Object> 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) {

View File

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

View File

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