From ac4e9b69a394970e062e7db14092b8321c919cf9 Mon Sep 17 00:00:00 2001 From: kapcake Date: Wed, 10 May 2023 00:11:30 +0200 Subject: [PATCH] Add custom UserDetails and use mapping between entity and DTO --- .../bankingservice/config/AuthConfig.java | 34 ++++++++++++++ .../config/BankingServiceConfig.java | 10 +++-- .../controllers/AuthController.java | 19 ++++++++ .../controllers/BankingServiceController.java | 12 +++-- .../security/UserDetailsImpl.java | 45 +++++++++++++++++++ .../security/UserDetailsServiceImpl.java | 4 +- 6 files changed, 114 insertions(+), 10 deletions(-) create mode 100644 src/main/java/net/kapcake/bankingservice/config/AuthConfig.java create mode 100644 src/main/java/net/kapcake/bankingservice/controllers/AuthController.java create mode 100644 src/main/java/net/kapcake/bankingservice/security/UserDetailsImpl.java diff --git a/src/main/java/net/kapcake/bankingservice/config/AuthConfig.java b/src/main/java/net/kapcake/bankingservice/config/AuthConfig.java new file mode 100644 index 0000000..c6494a5 --- /dev/null +++ b/src/main/java/net/kapcake/bankingservice/config/AuthConfig.java @@ -0,0 +1,34 @@ +package net.kapcake.bankingservice.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +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.crypto.factory.PasswordEncoderFactories; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +public class AuthConfig { + @Bean + public SecurityFilterChain authenticationFilterChain(HttpSecurity http) throws Exception { + return http + .csrf().disable() + .authorizeHttpRequests(requests -> requests + .anyRequest().authenticated() + ) + .httpBasic().and() + .sessionManagement(sessionManagement -> { + sessionManagement.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED); + }) + .build(); + + } + + @Bean + public PasswordEncoder passwordEncoder() { + return PasswordEncoderFactories.createDelegatingPasswordEncoder(); + } +} diff --git a/src/main/java/net/kapcake/bankingservice/config/BankingServiceConfig.java b/src/main/java/net/kapcake/bankingservice/config/BankingServiceConfig.java index d5b944a..73fa900 100644 --- a/src/main/java/net/kapcake/bankingservice/config/BankingServiceConfig.java +++ b/src/main/java/net/kapcake/bankingservice/config/BankingServiceConfig.java @@ -1,14 +1,16 @@ package net.kapcake.bankingservice.config; +import org.modelmapper.ModelMapper; +import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.crypto.factory.PasswordEncoderFactories; -import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.web.client.RestTemplate; @Configuration public class BankingServiceConfig { + @Bean - public PasswordEncoder passwordEncoder() { - return PasswordEncoderFactories.createDelegatingPasswordEncoder(); + public ModelMapper modelMapper() { + return new ModelMapper(); } } diff --git a/src/main/java/net/kapcake/bankingservice/controllers/AuthController.java b/src/main/java/net/kapcake/bankingservice/controllers/AuthController.java new file mode 100644 index 0000000..676c500 --- /dev/null +++ b/src/main/java/net/kapcake/bankingservice/controllers/AuthController.java @@ -0,0 +1,19 @@ +package net.kapcake.bankingservice.controllers; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import net.kapcake.bankingservice.security.UserDetailsImpl; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@Slf4j +public class AuthController { + @PostMapping("/login") + public void login(HttpServletRequest request) { + Authentication auth = (Authentication) request.getUserPrincipal(); + UserDetailsImpl user = (UserDetailsImpl) auth.getPrincipal(); + log.info("User {} logged in.", user.getUsername()); + } +} diff --git a/src/main/java/net/kapcake/bankingservice/controllers/BankingServiceController.java b/src/main/java/net/kapcake/bankingservice/controllers/BankingServiceController.java index 9c0094b..589a02f 100644 --- a/src/main/java/net/kapcake/bankingservice/controllers/BankingServiceController.java +++ b/src/main/java/net/kapcake/bankingservice/controllers/BankingServiceController.java @@ -1,7 +1,10 @@ package net.kapcake.bankingservice.controllers; -import net.kapcake.bankingservice.domain.BankAccount; +import net.kapcake.bankingservice.model.domain.BankAccountDTO; +import net.kapcake.bankingservice.model.entities.BankAccount; +import net.kapcake.bankingservice.security.UserDetailsImpl; import net.kapcake.bankingservice.services.AccountService; +import org.modelmapper.ModelMapper; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.User; import org.springframework.web.bind.annotation.GetMapping; @@ -12,13 +15,16 @@ import java.util.List; @RestController public class BankingServiceController { private final AccountService accountService; + private final ModelMapper modelMapper; public BankingServiceController(AccountService accountService) { this.accountService = accountService; + this.modelMapper = modelMapper; } @GetMapping("/accounts") - public List getAccounts(@AuthenticationPrincipal User user) { - return accountService.getAccounts(user); + public List getAccounts(@AuthenticationPrincipal UserDetailsImpl authenticatedUser) { + return accountService.getAccounts(authenticatedUser.user()).stream() + .map(account -> modelMapper.map(account, BankAccountDTO.class)).toList(); } } diff --git a/src/main/java/net/kapcake/bankingservice/security/UserDetailsImpl.java b/src/main/java/net/kapcake/bankingservice/security/UserDetailsImpl.java new file mode 100644 index 0000000..d5ba2ed --- /dev/null +++ b/src/main/java/net/kapcake/bankingservice/security/UserDetailsImpl.java @@ -0,0 +1,45 @@ +package net.kapcake.bankingservice.security; + +import net.kapcake.bankingservice.model.entities.User; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; + +public record UserDetailsImpl(User user) implements UserDetails { + + @Override + public Collection getAuthorities() { + return null; + } + + @Override + public String getPassword() { + return user.getPassword(); + } + + @Override + public String getUsername() { + return user.getUsername(); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } +} diff --git a/src/main/java/net/kapcake/bankingservice/security/UserDetailsServiceImpl.java b/src/main/java/net/kapcake/bankingservice/security/UserDetailsServiceImpl.java index 35f86a0..2e42a5f 100644 --- a/src/main/java/net/kapcake/bankingservice/security/UserDetailsServiceImpl.java +++ b/src/main/java/net/kapcake/bankingservice/security/UserDetailsServiceImpl.java @@ -8,8 +8,6 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import static org.springframework.security.core.userdetails.User.withUsername; - @Service public class UserDetailsServiceImpl implements UserDetailsService { private final UserRepository userRepository; @@ -23,6 +21,6 @@ public class UserDetailsServiceImpl implements UserDetailsService { public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username) .orElseThrow(() -> new UsernameNotFoundException("User (" + username + ") does not exist")); - return withUsername(username).password(user.getPassword()).authorities("USER").build(); + return new UserDetailsImpl(user); } }