diff --git a/shopping-cart-backend/pom.xml b/shopping-cart-backend/pom.xml index 9a50ce9..069f296 100644 --- a/shopping-cart-backend/pom.xml +++ b/shopping-cart-backend/pom.xml @@ -34,13 +34,12 @@ org.springframework.boot - spring-boot-starter-test - test + spring-boot-starter-data-jpa org.springframework.boot - spring-boot-starter-data-jpa - ${spring-boot.version} + spring-boot-starter-test + test org.springframework.security diff --git a/shopping-cart-backend/shopping-cart.iml b/shopping-cart-backend/shopping-cart.iml index 1a6d61b..8054dec 100644 --- a/shopping-cart-backend/shopping-cart.iml +++ b/shopping-cart-backend/shopping-cart.iml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/ShoppingCartApplication.java b/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/Main.java similarity index 69% rename from shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/ShoppingCartApplication.java rename to shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/Main.java index fe346c6..76f346c 100644 --- a/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/ShoppingCartApplication.java +++ b/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/Main.java @@ -4,10 +4,10 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class ShoppingCartApplication { +public class Main { public static void main(String[] args) { - SpringApplication.run(ShoppingCartApplication.class, args); + SpringApplication.run(Main.class, args); } } diff --git a/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/config/PasswordEncoderConfig.java b/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/config/PasswordEncoderConfig.java new file mode 100644 index 0000000..63fed92 --- /dev/null +++ b/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/config/PasswordEncoderConfig.java @@ -0,0 +1,13 @@ +package fptu.swp391.shoppingcart.authentication.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +@Configuration +public class PasswordEncoderConfig { + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/config/SecurityConfig.java b/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/config/SecurityConfig.java new file mode 100644 index 0000000..826260f --- /dev/null +++ b/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/config/SecurityConfig.java @@ -0,0 +1,32 @@ +package fptu.swp391.shoppingcart.authentication.config; + +import fptu.swp391.shoppingcart.authentication.service.AuthenticationProviderService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +public class SecurityConfig extends WebSecurityConfigurerAdapter { + @Autowired + private AuthenticationProviderService authenticationProvider; + + @Override + protected void configure(AuthenticationManagerBuilder auth) { + auth.authenticationProvider(authenticationProvider); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + //@formatter:off + http + .csrf().disable() + .authorizeRequests() + .mvcMatchers("/login").permitAll() + .anyRequest().authenticated() + .and().httpBasic(); +// .and().formLogin(); + //@formatter:on + } +} diff --git a/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/entity/Authority.java b/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/entity/Authority.java new file mode 100644 index 0000000..af92d8d --- /dev/null +++ b/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/entity/Authority.java @@ -0,0 +1,17 @@ +package fptu.swp391.shoppingcart.authentication.entity; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +@Entity +@Getter +@Setter +@Table(name = "AUTHORITY") +public class Authority extends BaseEntity { + @Column(name = "NAME", nullable = false, length = 50) + private String name; +} diff --git a/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/entity/BaseEntity.java b/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/entity/BaseEntity.java new file mode 100644 index 0000000..d4486bb --- /dev/null +++ b/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/entity/BaseEntity.java @@ -0,0 +1,21 @@ +package fptu.swp391.shoppingcart.authentication.entity; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; + +@Getter +@Setter +@MappedSuperclass +public abstract class BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "ID", nullable = false) + private Long id; + + @Version + @Column(name = "VERSION", nullable = false) + private int version; +} diff --git a/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/entity/UserEntity.java b/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/entity/UserEntity.java new file mode 100644 index 0000000..449f3e1 --- /dev/null +++ b/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/entity/UserEntity.java @@ -0,0 +1,49 @@ +package fptu.swp391.shoppingcart.authentication.entity; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.LinkedHashSet; +import java.util.Objects; +import java.util.Set; + +@Entity +@Getter +@Setter +@Table(name = "USER") +public class UserEntity extends BaseEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + @Column(name = "USERNAME", nullable = false, unique = true) + private String username; + + @Column(name = "PASSWORD", nullable = false) + private String password; + + @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true) + @JoinColumn(name = "USER_ID") + private Set authorities = new LinkedHashSet<>(); + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + UserEntity that = (UserEntity) o; + + if (!username.equals(that.username)) return false; + if (!password.equals(that.password)) return false; + return Objects.equals(authorities, that.authorities); + } + + @Override + public int hashCode() { + int result = username.hashCode(); + result = 31 * result + password.hashCode(); + result = 31 * result + (authorities != null ? authorities.hashCode() : 0); + return result; + } +} diff --git a/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/model/CustomUserDetails.java b/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/model/CustomUserDetails.java new file mode 100644 index 0000000..c571392 --- /dev/null +++ b/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/model/CustomUserDetails.java @@ -0,0 +1,55 @@ +package fptu.swp391.shoppingcart.authentication.model; + +import fptu.swp391.shoppingcart.authentication.entity.UserEntity; +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.stream.Collectors; + +public class CustomUserDetails implements UserDetails{ + + private final UserEntity user; + + public CustomUserDetails(UserEntity user) { + this.user = user; + } + + @Override + public Collection getAuthorities() { + return user.getAuthorities().stream() + .map(a -> new SimpleGrantedAuthority(a.getName())) + .collect(Collectors.toList()); + } + + @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/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/repository/UserRepository.java b/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/repository/UserRepository.java new file mode 100644 index 0000000..8dcdfdf --- /dev/null +++ b/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/repository/UserRepository.java @@ -0,0 +1,10 @@ +package fptu.swp391.shoppingcart.authentication.repository; + +import fptu.swp391.shoppingcart.authentication.entity.UserEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface UserRepository extends JpaRepository { + Optional findUserByUsername(String username); +} diff --git a/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/service/AuthenticationProviderService.java b/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/service/AuthenticationProviderService.java new file mode 100644 index 0000000..b4b397d --- /dev/null +++ b/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/service/AuthenticationProviderService.java @@ -0,0 +1,39 @@ +package fptu.swp391.shoppingcart.authentication.service; + +import fptu.swp391.shoppingcart.authentication.model.CustomUserDetails; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; + +@Service +public class AuthenticationProviderService implements AuthenticationProvider { + @Autowired + private JpaUserDetailsService userDetailsService; + + @Autowired + private BCryptPasswordEncoder bCryptPasswordEncoder; + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + String username = authentication.getName(); + String password = authentication.getCredentials().toString(); + + CustomUserDetails userDetails = userDetailsService.loadUserByUsername(username); + + if (bCryptPasswordEncoder.matches(password, userDetails.getPassword())) { + return new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities()); + } + + throw new BadCredentialsException("Bad credentials"); + } + + @Override + public boolean supports(Class authentication) { + return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); + } +} diff --git a/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/service/JpaUserDetailsService.java b/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/service/JpaUserDetailsService.java new file mode 100644 index 0000000..d5f4351 --- /dev/null +++ b/shopping-cart-backend/src/main/java/fptu/swp391/shoppingcart/authentication/service/JpaUserDetailsService.java @@ -0,0 +1,27 @@ +package fptu.swp391.shoppingcart.authentication.service; + +import fptu.swp391.shoppingcart.authentication.entity.UserEntity; +import fptu.swp391.shoppingcart.authentication.model.CustomUserDetails; +import fptu.swp391.shoppingcart.authentication.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import java.util.function.Supplier; + +@Service +public class JpaUserDetailsService implements UserDetailsService { + @Autowired + private UserRepository userRepository; + + @Override + public CustomUserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + Supplier s = + () -> new UsernameNotFoundException("Problem during authentication!"); + + UserEntity u = userRepository.findUserByUsername(username).orElseThrow(s); + + return new CustomUserDetails(u); + } +} diff --git a/shopping-cart-backend/src/test/java/fptu/swp391/shoppingcart/ShoppingCartApplicationTests.java b/shopping-cart-backend/src/test/java/fptu/swp391/shoppingcart/MainTests.java similarity index 83% rename from shopping-cart-backend/src/test/java/fptu/swp391/shoppingcart/ShoppingCartApplicationTests.java rename to shopping-cart-backend/src/test/java/fptu/swp391/shoppingcart/MainTests.java index 3cc01cc..cd8018b 100644 --- a/shopping-cart-backend/src/test/java/fptu/swp391/shoppingcart/ShoppingCartApplicationTests.java +++ b/shopping-cart-backend/src/test/java/fptu/swp391/shoppingcart/MainTests.java @@ -4,7 +4,7 @@ import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest -class ShoppingCartApplicationTests { +class MainTests { @Test void contextLoads() {