Add JWT authentication
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
package com.faf223.expensetrackerfaf.config;
|
package com.faf223.expensetrackerfaf.config;
|
||||||
|
|
||||||
|
import com.faf223.expensetrackerfaf.repository.CredentialRepository;
|
||||||
import com.faf223.expensetrackerfaf.repository.UserRepository;
|
import com.faf223.expensetrackerfaf.repository.UserRepository;
|
||||||
import com.faf223.expensetrackerfaf.security.PersonDetails;
|
import com.faf223.expensetrackerfaf.security.PersonDetails;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -17,17 +18,18 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class ApplicationConfig {
|
public class ApplicationConfig {
|
||||||
|
|
||||||
private final UserRepository repository;
|
private final UserRepository userRepository;
|
||||||
|
private final CredentialRepository credentialRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ApplicationConfig(UserRepository repository) {
|
public ApplicationConfig(UserRepository userRepository, CredentialRepository credentialRepository) {
|
||||||
this.repository = repository;
|
this.userRepository = userRepository;
|
||||||
|
this.credentialRepository = credentialRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public UserDetailsService userDetailsService() {
|
public UserDetailsService userDetailsService() {
|
||||||
return username -> new PersonDetails(repository.findByEmail(username)
|
return username -> new PersonDetails(credentialRepository.findByEmail(username).orElseThrow((() -> new UsernameNotFoundException("User not found"))));
|
||||||
.orElseThrow(() -> new UsernameNotFoundException("User not found")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
|
|
||||||
private final JwtService jwtService;
|
private final JwtService jwtService;
|
||||||
private final UserDetailsService userDetailsService;
|
private final UserDetailsService userDetailsService;
|
||||||
private final TokenRepository tokenRepository;
|
|
||||||
|
|
||||||
public JwtAuthenticationFilter(JwtService jwtService, UserDetailsService userDetailsService) {
|
public JwtAuthenticationFilter(JwtService jwtService, UserDetailsService userDetailsService) {
|
||||||
this.jwtService = jwtService;
|
this.jwtService = jwtService;
|
||||||
@@ -48,18 +47,11 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
userEmail = jwtService.extractUsername(jwt);
|
userEmail = jwtService.extractUsername(jwt);
|
||||||
if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||||
UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail);
|
UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail);
|
||||||
var isTokenValid = tokenRepository.findByToken(jwt)
|
if (jwtService.isTokenValid(jwt, userDetails)) {
|
||||||
.map(t -> !t.isExpired() && !t.isRevoked())
|
|
||||||
.orElse(false);
|
|
||||||
if (jwtService.isTokenValid(jwt, userDetails) && isTokenValid) {
|
|
||||||
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
|
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
|
||||||
userDetails,
|
userDetails, null, userDetails.getAuthorities());
|
||||||
null,
|
authToken.setDetails(new WebAuthenticationDetailsSource()
|
||||||
userDetails.getAuthorities()
|
.buildDetails(request));
|
||||||
);
|
|
||||||
authToken.setDetails(
|
|
||||||
new WebAuthenticationDetailsSource().buildDetails(request)
|
|
||||||
);
|
|
||||||
SecurityContextHolder.getContext().setAuthentication(authToken);
|
SecurityContextHolder.getContext().setAuthentication(authToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,17 +45,7 @@ public class JwtService {
|
|||||||
return buildToken(extraClaims, userDetails, jwtExpiration);
|
return buildToken(extraClaims, userDetails, jwtExpiration);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String generateRefreshToken(
|
private String buildToken(Map<String, Object> extraClaims, UserDetails userDetails, long expiration) {
|
||||||
UserDetails userDetails
|
|
||||||
) {
|
|
||||||
return buildToken(new HashMap<>(), userDetails, refreshExpiration);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildToken(
|
|
||||||
Map<String, Object> extraClaims,
|
|
||||||
UserDetails userDetails,
|
|
||||||
long expiration
|
|
||||||
) {
|
|
||||||
return Jwts
|
return Jwts
|
||||||
.builder()
|
.builder()
|
||||||
.setClaims(extraClaims)
|
.setClaims(extraClaims)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public class SecurityConfiguration {
|
|||||||
http
|
http
|
||||||
.csrf(csrf -> csrf.disable())
|
.csrf(csrf -> csrf.disable())
|
||||||
.authorizeHttpRequests(auth -> auth
|
.authorizeHttpRequests(auth -> auth
|
||||||
.requestMatchers("").permitAll()
|
.requestMatchers("/api/v1/auth/**").permitAll()
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
)
|
)
|
||||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ import lombok.NoArgsConstructor;
|
|||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class RegisterRequest {
|
public class RegisterRequest {
|
||||||
|
|
||||||
private String firstName;
|
private String firstname; // Change field name to match JSON
|
||||||
private String lastName;
|
private String lastname; // Change field name to match JSON
|
||||||
|
private String username; // Change field name to match JSON
|
||||||
private String email;
|
private String email; // Change field name to match JSON
|
||||||
private String password;
|
private String password;
|
||||||
private Role role;
|
private Role role;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,35 @@
|
|||||||
package com.faf223.expensetrackerfaf.model;
|
package com.faf223.expensetrackerfaf.model;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import lombok.Data;
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Entity(name = "credentials")
|
@Entity(name = "credentials")
|
||||||
public class Credential {
|
@NoArgsConstructor
|
||||||
@Id
|
@AllArgsConstructor
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
public class Credential {
|
||||||
private Long credentialId;
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long credentialId;
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn(name = "user_uuid")
|
@JoinColumn(name = "user_uuid")
|
||||||
private User user;
|
private User user;
|
||||||
|
|
||||||
private String email;
|
private String email;
|
||||||
private String password;
|
private String password;
|
||||||
}
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
private Role role;
|
||||||
|
|
||||||
|
public Credential(User user, String email, String password) {
|
||||||
|
this.user = user;
|
||||||
|
this.email = email;
|
||||||
|
this.password = password;
|
||||||
|
|
||||||
|
this.role = Role.USER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
package com.faf223.expensetrackerfaf.model;
|
package com.faf223.expensetrackerfaf.model;
|
||||||
|
|
||||||
public enum Role {
|
public enum Role {
|
||||||
UNREGISTERED, REGISTERED, ADMIN;
|
USER, ADMIN
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,28 +9,32 @@ import lombok.NoArgsConstructor;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Entity
|
@Entity(name = "users")
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Table(name = "User")
|
|
||||||
public class User {
|
public class User {
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@Column(name = "user_uuid")
|
||||||
private long id;
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
private String firstName;
|
private String userUuid;
|
||||||
private String lastName;
|
|
||||||
private String email;
|
|
||||||
private String password;
|
|
||||||
|
|
||||||
@Enumerated(EnumType.STRING)
|
@Column(name = "name")
|
||||||
private Role role;
|
private String firstName;
|
||||||
|
|
||||||
|
@Column(name = "surname")
|
||||||
|
private String lastName;
|
||||||
|
|
||||||
|
@Column(name = "username")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
private String password;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "user")
|
@OneToMany(mappedBy = "user")
|
||||||
private List<Expense> expenses;
|
private List<Expense> expenses;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "user")
|
@OneToMany(mappedBy = "user")
|
||||||
private List<Income> incomes;
|
private List<Income> incomes;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import com.faf223.expensetrackerfaf.model.Credential;
|
|||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface CredentialRepository extends JpaRepository<Credential, Long> {
|
public interface CredentialRepository extends JpaRepository<Credential, Long> {
|
||||||
|
Optional<Credential> findByEmail(String email);
|
||||||
}
|
}
|
||||||
@@ -3,8 +3,5 @@ package com.faf223.expensetrackerfaf.repository;
|
|||||||
import com.faf223.expensetrackerfaf.model.User;
|
import com.faf223.expensetrackerfaf.model.User;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
import java.util.Optional;
|
public interface UserRepository extends JpaRepository<User, String> {
|
||||||
|
|
||||||
public interface UserRepository extends JpaRepository<User, Long> {
|
|
||||||
Optional<User> findByEmail(String username);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
package com.faf223.expensetrackerfaf.security;
|
package com.faf223.expensetrackerfaf.security;
|
||||||
|
|
||||||
import com.faf223.expensetrackerfaf.model.Role;
|
import com.faf223.expensetrackerfaf.model.Credential;
|
||||||
import com.faf223.expensetrackerfaf.model.User;
|
|
||||||
import jakarta.persistence.EnumType;
|
|
||||||
import jakarta.persistence.Enumerated;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
@@ -18,31 +14,28 @@ import java.util.List;
|
|||||||
@Data
|
@Data
|
||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor(force = true)
|
@NoArgsConstructor(force = true)
|
||||||
@AllArgsConstructor
|
//@AllArgsConstructor
|
||||||
public class PersonDetails implements UserDetails {
|
public class PersonDetails implements UserDetails {
|
||||||
|
|
||||||
private final User user;
|
private final Credential credential;
|
||||||
|
|
||||||
@Enumerated(EnumType.STRING)
|
public PersonDetails(Credential credential) {
|
||||||
private Role role;
|
this.credential = credential;
|
||||||
|
|
||||||
public PersonDetails(User user) {
|
|
||||||
this.user = user;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||||
return List.of(new SimpleGrantedAuthority(role.name()));
|
return List.of(new SimpleGrantedAuthority(credential.getRole().name()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPassword() {
|
public String getPassword() {
|
||||||
return user.getPassword();
|
return credential.getPassword();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return user.getEmail();
|
return credential.getEmail();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -4,42 +4,62 @@ import com.faf223.expensetrackerfaf.config.JwtService;
|
|||||||
import com.faf223.expensetrackerfaf.controller.auth.AuthenticationRequest;
|
import com.faf223.expensetrackerfaf.controller.auth.AuthenticationRequest;
|
||||||
import com.faf223.expensetrackerfaf.controller.auth.AuthenticationResponse;
|
import com.faf223.expensetrackerfaf.controller.auth.AuthenticationResponse;
|
||||||
import com.faf223.expensetrackerfaf.controller.auth.RegisterRequest;
|
import com.faf223.expensetrackerfaf.controller.auth.RegisterRequest;
|
||||||
import com.faf223.expensetrackerfaf.model.Role;
|
import com.faf223.expensetrackerfaf.model.Credential;
|
||||||
import com.faf223.expensetrackerfaf.model.User;
|
import com.faf223.expensetrackerfaf.model.User;
|
||||||
|
import com.faf223.expensetrackerfaf.repository.CredentialRepository;
|
||||||
import com.faf223.expensetrackerfaf.repository.UserRepository;
|
import com.faf223.expensetrackerfaf.repository.UserRepository;
|
||||||
import com.faf223.expensetrackerfaf.security.PersonDetails;
|
import com.faf223.expensetrackerfaf.security.PersonDetails;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class AuthenticationService {
|
public class AuthenticationService {
|
||||||
|
|
||||||
private final UserRepository repository;
|
private final UserRepository userRepository;
|
||||||
|
private final CredentialRepository credentialRepository;
|
||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
private final JwtService jwtService;
|
private final JwtService jwtService;
|
||||||
|
private final AuthenticationManager authenticationManager;
|
||||||
|
|
||||||
public AuthenticationService(UserRepository repository, PasswordEncoder passwordEncoder, JwtService jwtService) {
|
public AuthenticationService(UserRepository repository, CredentialRepository credentialRepository, PasswordEncoder passwordEncoder, JwtService jwtService, AuthenticationManager authenticationManager) {
|
||||||
this.repository = repository;
|
this.userRepository = repository;
|
||||||
|
this.credentialRepository = credentialRepository;
|
||||||
this.passwordEncoder = passwordEncoder;
|
this.passwordEncoder = passwordEncoder;
|
||||||
this.jwtService = jwtService;
|
this.jwtService = jwtService;
|
||||||
}
|
this.authenticationManager = authenticationManager;
|
||||||
|
|
||||||
public AuthenticationResponse authenticate(AuthenticationRequest request) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuthenticationResponse register(RegisterRequest request) {
|
public AuthenticationResponse register(RegisterRequest request) {
|
||||||
|
|
||||||
User user = User.builder()
|
User user = User.builder()
|
||||||
.firstName(request.getFirstName())
|
.firstName(request.getFirstname())
|
||||||
.lastName(request.getLastName())
|
.lastName(request.getLastname())
|
||||||
.email(request.getEmail())
|
|
||||||
.password(passwordEncoder.encode(request.getPassword()))
|
.password(passwordEncoder.encode(request.getPassword()))
|
||||||
.role(request.getRole())
|
.username(request.getUsername())
|
||||||
.build();
|
.build();
|
||||||
repository.save(user);
|
System.out.println(user);
|
||||||
String jwtToken = jwtService.generateToken(new PersonDetails(user));
|
userRepository.save(user);
|
||||||
|
Credential credential = new Credential(user, request.getEmail(), passwordEncoder.encode(request.getPassword()));
|
||||||
|
credentialRepository.save(credential);
|
||||||
|
|
||||||
|
String jwtToken = jwtService.generateToken(new PersonDetails(credential));
|
||||||
return AuthenticationResponse.builder()
|
return AuthenticationResponse.builder()
|
||||||
.token(jwtToken)
|
.token(jwtToken)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AuthenticationResponse authenticate(AuthenticationRequest request) {
|
||||||
|
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(request.getEmail(), request.getPassword()));
|
||||||
|
|
||||||
|
Credential credential = credentialRepository.findByEmail(request.getEmail()).orElseThrow((() -> new UsernameNotFoundException("User not found")));
|
||||||
|
|
||||||
|
String jwtToken = jwtService.generateToken(new PersonDetails(credential));
|
||||||
|
return AuthenticationResponse.builder()
|
||||||
|
.token(jwtToken)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user