added jwt

This commit is contained in:
Ksan 2025-10-31 13:20:42 +01:00
parent 78ba74cd0d
commit 43011c1509
14 changed files with 518 additions and 83 deletions

View File

@ -33,9 +33,12 @@ dependencies {
runtimeOnly 'org.postgresql:postgresql' runtimeOnly 'org.postgresql:postgresql'
testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher' testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation "org.springframework.boot:spring-boot-starter-security"
implementation("org.simplejavamail:simple-java-mail:8.0.1") implementation("org.simplejavamail:simple-java-mail:8.0.1")
implementation("net.sourceforge.htmlunit:htmlunit:2.70.0") implementation("net.sourceforge.htmlunit:htmlunit:2.70.0")
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.13.0")
implementation("io.jsonwebtoken:jjwt-api:0.13.0")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.13.0")
} }
generateJava { generateJava {

View File

@ -45,6 +45,7 @@ CREATE TABLE IF NOT EXISTS public.users
email character varying(255) COLLATE pg_catalog."default" NOT NULL, email character varying(255) COLLATE pg_catalog."default" NOT NULL,
password character varying(255) COLLATE pg_catalog."default" NOT NULL, password character varying(255) COLLATE pg_catalog."default" NOT NULL,
reg_time timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, reg_time timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
notification_type character varying(255) COLLATE pg_catalog."default" NOT NULL DEFAULT 'NO_NOTIFICATION'::character varying,
CONSTRAINT users_pkey PRIMARY KEY (id), CONSTRAINT users_pkey PRIMARY KEY (id),
CONSTRAINT unique_email UNIQUE (email) CONSTRAINT unique_email UNIQUE (email)
); );

View File

@ -0,0 +1,53 @@
package dev.ksan.etfoglasiserver.config;
import dev.ksan.etfoglasiserver.service.JWTService;
import dev.ksan.etfoglasiserver.service.MyUserDetailsService;
import dev.ksan.etfoglasiserver.service.UserService;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@Component
public class JwtFilter extends OncePerRequestFilter {
@Autowired
private JWTService jwtService;
@Autowired
ApplicationContext context;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
String token = null;
String username = null;
if (authHeader != null && authHeader.startsWith("Bearer ")) {
token = authHeader.substring(7);
username = jwtService.extractEmail(token);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = context.getBean(MyUserDetailsService.class).loadUserByUsername(username);
if (jwtService.validateToken(token, userDetails)) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authToken.setDetails(new WebAuthenticationDetailsSource()
.buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
filterChain.doFilter(request, response);
}
}

View File

@ -0,0 +1,63 @@
package dev.ksan.etfoglasiserver.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private JwtFilter jwtFilter;
@Autowired
private UserDetailsService userDetailsService;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.csrf(customizer -> customizer.disable()).
authorizeHttpRequests(request -> request
.requestMatchers("login", "register").permitAll()
.anyRequest().authenticated()).
httpBasic(Customizer.withDefaults()).
sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(userDetailsService);
provider.setPasswordEncoder(new BCryptPasswordEncoder(12));
return provider;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
}

View File

@ -1,50 +1,75 @@
package dev.ksan.etfoglasiserver.controller; package dev.ksan.etfoglasiserver.controller;
import dev.ksan.etfoglasiserver.dto.JwtResponseDTO;
import dev.ksan.etfoglasiserver.dto.UserCreationDTO; import dev.ksan.etfoglasiserver.dto.UserCreationDTO;
import dev.ksan.etfoglasiserver.dto.UserDTO; import dev.ksan.etfoglasiserver.dto.UserDTO;
import dev.ksan.etfoglasiserver.dto.UserLoginDTO;
import dev.ksan.etfoglasiserver.model.NotificationMethod; import dev.ksan.etfoglasiserver.model.NotificationMethod;
import dev.ksan.etfoglasiserver.model.User;
import dev.ksan.etfoglasiserver.service.UserService; import dev.ksan.etfoglasiserver.service.UserService;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@RestController @RestController
public class UserController { public class UserController {
@Autowired UserService service; @Autowired UserService service;
@GetMapping("/users") @GetMapping("/users")
public List<UserDTO> getUsers() { public ResponseEntity<List<UserDTO>> getUsers() {
return service.getUsers().stream().map(this::getUserDTO).collect(Collectors.toList());
return new ResponseEntity(service.getUsers(), HttpStatus.OK);
} }
@GetMapping("users/{userId}") @GetMapping("users/{userId}")
public UserDTO getUserById(@PathVariable UUID userId) { public ResponseEntity<UserDTO> getUserById(@PathVariable UUID userId) {
return getUserDTO(service.getUserById(userId)); UserDTO user = service.getUserById(userId);
if (user != null) {
return new ResponseEntity<>(user, HttpStatus.OK);
} }
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
public UserDTO getUserDTO(User user) {
return new UserDTO(user.getId(), user.getEmail(), user.getSubjectSet());
} }
@PutMapping("/users") @PutMapping("/users")
public void updateUser(@RequestBody UserCreationDTO user) { public ResponseEntity<UserDTO> updateUser(@RequestBody UserCreationDTO user) {
NotificationMethod method = NotificationMethod.valueOf(user.getNotification()); NotificationMethod method = NotificationMethod.valueOf(user.getNotification());
user.setNotificationMethod(method); user.setNotificationMethod(method);
service.updateUser(user); UserDTO userUpdated = service.updateUser(user);
if (userUpdated != null) {
return new ResponseEntity<>(userUpdated, HttpStatus.OK);
}
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
} }
/*
@PostMapping("/users") @PostMapping("/users")
public void addUser(@RequestBody UserCreationDTO user) { public void addUser(@RequestBody UserCreationDTO user) {
NotificationMethod method = NotificationMethod.valueOf(user.getNotification()); NotificationMethod method = NotificationMethod.valueOf(user.getNotification());
user.setNotificationMethod(method); user.setNotificationMethod(method);
service.addUser(user); service.addUser(user);
} }
*/
@PostMapping("/login")
public ResponseEntity<JwtResponseDTO> login(@RequestBody UserLoginDTO user) {
JwtResponseDTO userVer = service.verify(user);
if (userVer != null) return new ResponseEntity<>(userVer, HttpStatus.OK);
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
@PostMapping("/register")
public ResponseEntity<UserDTO> register(@RequestBody UserCreationDTO user) {
UserDTO newUser = service.register(user);
if (newUser != null) return new ResponseEntity<>(newUser, HttpStatus.CREATED);
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
@DeleteMapping("/users/{userId}") @DeleteMapping("/users/{userId}")
public void deleteUser(@PathVariable UUID userId) { public void deleteUser(@PathVariable UUID userId) {

View File

@ -0,0 +1,38 @@
package dev.ksan.etfoglasiserver.dto;
public class JwtResponseDTO {
private String token;
private String email;
private String message;
public JwtResponseDTO(String token, String email, String message) {
this.token = token;
this.email = email;
this.message = message;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -2,7 +2,6 @@ package dev.ksan.etfoglasiserver.dto;
import dev.ksan.etfoglasiserver.model.NotificationMethod; import dev.ksan.etfoglasiserver.model.NotificationMethod;
import dev.ksan.etfoglasiserver.model.Subject; import dev.ksan.etfoglasiserver.model.Subject;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
@ -11,9 +10,11 @@ public class UserDTO {
private String email; private String email;
private Set<Subject> subjectSet; private Set<Subject> subjectSet;
private NotificationMethod notificationMethod; private NotificationMethod notificationMethod;
public UserDTO() {} public UserDTO() {}
public UserDTO(UUID id, String email, Set<Subject> subjectSet) { public UserDTO(
UUID id, String email, Set<Subject> subjectSet, NotificationMethod notificationMethod2) {
this.id = id; this.id = id;
this.email = email; this.email = email;
this.subjectSet = subjectSet; this.subjectSet = subjectSet;
@ -22,12 +23,15 @@ public class UserDTO {
public UUID getId() { public UUID getId() {
return id; return id;
} }
public void setId(UUID id) { public void setId(UUID id) {
this.id = id; this.id = id;
} }
public String getEmail() { public String getEmail() {
return email; return email;
} }
public void setEmail(String email) { public void setEmail(String email) {
this.email = email; this.email = email;
} }

View File

@ -0,0 +1,23 @@
package dev.ksan.etfoglasiserver.dto;
public class UserLoginDTO {
private String email;
private String password;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

View File

@ -0,0 +1,52 @@
package dev.ksan.etfoglasiserver.model;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Collections;
public class UserPrincipal implements UserDetails {
private User user;
public UserPrincipal(User user) {
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singleton(new SimpleGrantedAuthority("USER"));
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getEmail();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}

View File

@ -0,0 +1,76 @@
package dev.ksan.etfoglasiserver.service;
import dev.ksan.etfoglasiserver.model.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.function.Function;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
@Service
public class JWTService {
private String secretKey = "";
private JWTService() {
try {
KeyGenerator keyGen = KeyGenerator.getInstance("HmacSHA256");
SecretKey secKey = keyGen.generateKey();
secretKey = Base64.getEncoder().encodeToString(secKey.getEncoded());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
public String generateToken(String email) {
Map<String, Object> claims = new HashMap<>();
return Jwts.builder().claims().add(claims).subject(email)
.issuedAt(new Date(System.currentTimeMillis())).expiration(new Date(System.currentTimeMillis()+60*60*300))
.and().signWith(getKey()).compact();
}
private SecretKey getKey() {
byte[] keyBytes = Decoders.BASE64.decode(secretKey);
return Keys.hmacShaKeyFor(keyBytes);
}
public String extractEmail(String token) {
return extractClaim(token, Claims::getSubject);
}
private <T> T extractClaim(String token, Function<Claims, T> claimResolver) {
final Claims claims = extractAllClaims(token);
return claimResolver.apply(claims);
}
private Claims extractAllClaims(String token) {
return Jwts.parser()
.verifyWith(getKey())
.build()
.parseSignedClaims(token)
.getPayload();
}
public boolean validateToken(String token, UserDetails userDetails) {
final String userName = extractEmail(token);
return (userName.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
private boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
private Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
}

View File

@ -11,15 +11,18 @@ import java.io.BufferedReader;
import java.io.FileReader; import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
@Service
public class MailService { public class MailService {
String address; String address;
String passwd; String passwd;
Mailer mailer; Mailer mailer;
public MailService(){
this(".creds");
}
public MailService(String filename) { public MailService(String filename) {
try(BufferedReader br = new BufferedReader((new FileReader(filename)))) { try(BufferedReader br = new BufferedReader((new FileReader(filename)))) {
address = br.readLine(); address = br.readLine();
System.out.println(address);
passwd = br.readLine(); passwd = br.readLine();
}catch(IOException e) { }catch(IOException e) {
e.printStackTrace(); e.printStackTrace();

View File

@ -0,0 +1,30 @@
package dev.ksan.etfoglasiserver.service;
import dev.ksan.etfoglasiserver.model.User;
import dev.ksan.etfoglasiserver.model.UserPrincipal;
import dev.ksan.etfoglasiserver.repository.UserRepo;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired private UserRepo userRepo;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
Optional<User> user = userRepo.findByEmail(email);
if (!user.isPresent()) {
throw new UsernameNotFoundException("user not found");
}
return new UserPrincipal(user.get());
}
}

View File

@ -16,12 +16,15 @@ import org.springframework.stereotype.Service;
@Service @Service
public class SubjectService { public class SubjectService {
@Autowired SubjectRepo subjectRepo; @Autowired private SubjectRepo subjectRepo;
@Autowired @Autowired
UserRepo userRepo; private UserRepo userRepo;
@Autowired @Autowired
private UserService userService; private UserService userService;
@Autowired
private MailService mailService;
public List<Subject> getSubjects() { public List<Subject> getSubjects() {
return subjectRepo.findAll(); return subjectRepo.findAll();
} }
@ -80,8 +83,7 @@ public class SubjectService {
public void sendEmailNotification(Entry entry,User user) { public void sendEmailNotification(Entry entry,User user) {
MailService mail = new MailService(".creds"); mailService.sendEmail(user.getEmail(),entry.getTitle(),entry.getParagraph());
mail.sendEmail(user.getEmail(),entry.getTitle(),entry.getParagraph());
} }
//todo //todo

View File

@ -1,13 +1,27 @@
package dev.ksan.etfoglasiserver.service; package dev.ksan.etfoglasiserver.service;
import dev.ksan.etfoglasiserver.dto.JwtResponseDTO;
import dev.ksan.etfoglasiserver.dto.UserCreationDTO; import dev.ksan.etfoglasiserver.dto.UserCreationDTO;
import dev.ksan.etfoglasiserver.model.NotificationMethod; import dev.ksan.etfoglasiserver.dto.UserDTO;
import dev.ksan.etfoglasiserver.dto.UserLoginDTO;
import dev.ksan.etfoglasiserver.model.User; import dev.ksan.etfoglasiserver.model.User;
import dev.ksan.etfoglasiserver.model.UserPrincipal;
import dev.ksan.etfoglasiserver.repository.UserRepo; import dev.ksan.etfoglasiserver.repository.UserRepo;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -16,12 +30,20 @@ import org.springframework.transaction.annotation.Transactional;
public class UserService { public class UserService {
@Autowired UserRepo userRepo; @Autowired UserRepo userRepo;
public List<User> getUsers() { @Autowired private AuthenticationManager authManager;
return userRepo.findAll();
@Autowired private JWTService jwtService;
private BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
public List<UserDTO> getUsers() {
return userRepo.findAll().stream().map(this::getUserDTO).collect(Collectors.toList());
} }
public User getUserById(UUID userId) { public UserDTO getUserById(UUID userId) {
return userRepo.findById(userId).orElseThrow(() -> new RuntimeException("User not found")); User user = userRepo.findById(userId).orElseThrow(() -> new RuntimeException("User not found"));
return getUserDTO(user);
} }
public void addUser(UserCreationDTO user) { public void addUser(UserCreationDTO user) {
@ -34,14 +56,13 @@ public class UserService {
} }
} }
public UserDTO updateUser(UserCreationDTO user) {
public void updateUser(UserCreationDTO user) {
Optional<User> existingUserOpt = userRepo.findByEmail(user.getEmail()); Optional<User> existingUserOpt = userRepo.findByEmail(user.getEmail());
if (userRepo.findByEmail(user.getNewEmail()).isPresent()) { if (userRepo.findByEmail(user.getNewEmail()).isPresent()) {
throw new RuntimeException("Email taken"); throw new RuntimeException("Email taken");
} }
if (userRepo.findByEmail(user.getEmail()).isPresent()) { if (existingUserOpt.isPresent()) {
if (this.isValidEmail(user.getEmail())) { if (this.isValidEmail(user.getEmail())) {
@ -49,7 +70,6 @@ public class UserService {
if (user.getPassword() != null && user.getPassword().length() > 0) { if (user.getPassword() != null && user.getPassword().length() > 0) {
if (this.isValidPassword(user.getPassword())) { if (this.isValidPassword(user.getPassword())) {
existingUser.setPassword(user.getPassword()); existingUser.setPassword(user.getPassword());
} else throw new RuntimeException("Password too short"); } else throw new RuntimeException("Password too short");
} }
@ -61,20 +81,52 @@ public class UserService {
existingUser.setEmail(user.getEmail()); existingUser.setEmail(user.getEmail());
} }
existingUser.setSubjectSet(user.getSubjectSet()); existingUser.setSubjectSet(user.getSubjectSet());
existingUser.setNotificationMethod(user.getNotificationMethod()); existingUser.setNotificationMethod(user.getNotificationMethod());
userRepo.save(existingUser); userRepo.save(existingUser);
return getUserDTO(existingUser);
} else throw new RuntimeException("Invalid email"); } else throw new RuntimeException("Invalid email");
} else throw new RuntimeException("User not found"); } else throw new RuntimeException("User not found");
} }
public void deleteUser(UUID userId) { public void deleteUser(UUID userId) {
userRepo.deleteById(userId); userRepo.deleteById(userId);
} }
public UserDTO register(UserCreationDTO user) {
if (userRepo.findByEmail(user.getEmail()).isPresent()) {
throw new RuntimeException("User already exists");
}
if (this.isValidEmail(user.getEmail()) && this.isValidPassword(user.getPassword())) {
User newUser = new User(user);
newUser.setPassword(encoder.encode(user.getPassword()));
userRepo.save(newUser);
return getUserDTO(newUser);
}
throw new RuntimeException("Error creating user");
}
public UserDTO getUserDTO(User user) {
return new UserDTO(
user.getId(), user.getEmail(), user.getSubjectSet(), user.getNotificationMethod());
}
public boolean isValidPassword(String pass) {
return pass.length() >= 8;
}
public List<User> findUsersBySubjectId(UUID id) {
List<User> users = userRepo.findUsersBySubjectId(id);
if (users.isEmpty()) return null;
return users;
}
public boolean isValidEmail(String email) { public boolean isValidEmail(String email) {
if (email == null) { if (email == null) {
return false; return false;
@ -87,14 +139,24 @@ public class UserService {
throw new RuntimeException("Invalid email"); throw new RuntimeException("Invalid email");
} }
public boolean isValidPassword(String pass) { public JwtResponseDTO verify(UserLoginDTO user) {
return pass.length() >= 8;
Authentication authentication = authManager.authenticate(new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword()));
if (authentication.isAuthenticated()) {
String token = jwtService.generateToken(user.getEmail());
return new JwtResponseDTO(token, user.getEmail(), HttpStatus.OK.toString());
} else {
throw new BadCredentialsException("Invalid username or password");
}
}
public Optional<User> getAccountByEmail(String email) {
return userRepo.findByEmail(email);
} }
public List<User> findUsersBySubjectId(UUID id) {
List<User> users = userRepo.findUsersBySubjectId(id);
if (users.isEmpty()) return null;
return users;
} }
}