Add custom UserDetails and use mapping between entity and DTO

This commit is contained in:
2023-05-10 00:11:30 +02:00
parent 0a3d3d25c6
commit ac4e9b69a3
6 changed files with 114 additions and 10 deletions

View File

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

View File

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

View File

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

View File

@@ -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<BankAccount> getAccounts(@AuthenticationPrincipal User user) {
return accountService.getAccounts(user);
public List<BankAccountDTO> getAccounts(@AuthenticationPrincipal UserDetailsImpl authenticatedUser) {
return accountService.getAccounts(authenticatedUser.user()).stream()
.map(account -> modelMapper.map(account, BankAccountDTO.class)).toList();
}
}

View File

@@ -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<? extends GrantedAuthority> 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;
}
}

View File

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