diff --git a/pom.xml b/pom.xml index d01a2a4..e229e37 100644 --- a/pom.xml +++ b/pom.xml @@ -71,17 +71,31 @@ provided + + + org.junit.jupiter + junit-jupiter + test + + + junit + junit + 4.12 + test + org.springframework.boot spring-boot-starter-test test - org.springframework.security spring-security-test test + + + diff --git a/src/main/java/com/ossez/spring/security/controllers/AuthController.java b/src/main/java/com/ossez/spring/security/controllers/AuthController.java index 34b431c..0483e7c 100644 --- a/src/main/java/com/ossez/spring/security/controllers/AuthController.java +++ b/src/main/java/com/ossez/spring/security/controllers/AuthController.java @@ -8,7 +8,7 @@ import java.util.stream.Collectors; import javax.validation.Valid; import com.ossez.spring.security.models.ERole; -import com.ossez.spring.security.models.entity.Role; +import com.ossez.spring.security.models.entity.PersonRole; import com.ossez.spring.security.payload.request.LoginRequest; import com.ossez.spring.security.payload.request.SignupRequest; import com.ossez.spring.security.payload.response.JwtResponse; @@ -36,26 +36,26 @@ import com.ossez.spring.security.repository.UserRepository; @RestController @RequestMapping("/custom") public class AuthController { - @Autowired - AuthenticationManager authenticationManager; + private final AuthenticationManager authenticationManager; + private final UserRepository userRepository; + private final RoleRepository roleRepository; + private final PasswordEncoder encoder; + private final JwtUtils jwtUtils; @Autowired - UserRepository userRepository; - - @Autowired - RoleRepository roleRepository; - - @Autowired - PasswordEncoder encoder; - - @Autowired - JwtUtils jwtUtils; + public AuthController(AuthenticationManager authenticationManager, UserRepository userRepository, RoleRepository roleRepository, PasswordEncoder encoder, JwtUtils jwtUtils) { + this.authenticationManager = authenticationManager; + this.userRepository = userRepository; + this.roleRepository = roleRepository; + this.encoder = encoder; + this.jwtUtils = jwtUtils; + } @PostMapping("/signin") public ResponseEntity authenticateUser(@Valid @RequestBody LoginRequest loginRequest) { Authentication authentication = authenticationManager.authenticate( - new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword())); + new UsernamePasswordAuthenticationToken(loginRequest.getUserName(), loginRequest.getUserPassword())); SecurityContextHolder.getContext().setAuthentication(authentication); String jwt = jwtUtils.generateJwtToken(authentication); @@ -74,13 +74,13 @@ public class AuthController { @PostMapping("/register") public ResponseEntity registerUser(@Valid @RequestBody SignupRequest signUpRequest) { - if (userRepository.existsByUsername(signUpRequest.getUsername())) { + if (userRepository.existsByUserName(signUpRequest.getUsername())) { return ResponseEntity .badRequest() .body(new MessageResponse("Error: Username is already taken!")); } - if (userRepository.existsByEmail(signUpRequest.getEmail())) { + if (userRepository.existsByUserEmail(signUpRequest.getEmail())) { return ResponseEntity .badRequest() .body(new MessageResponse("Error: Email is already in use!")); @@ -94,36 +94,36 @@ public class AuthController { Set strRoles = signUpRequest.getRole(); - Set roles = new HashSet<>(); + Set roles = new HashSet<>(); if (strRoles == null) { - Role userRole = roleRepository.findByName(ERole.ROLE_USER) + PersonRole userRole = roleRepository.findByPersonRoleKey(ERole.ROLE_USER) .orElseThrow(() -> new RuntimeException("Error: Role is not found.")); roles.add(userRole); } else { strRoles.forEach(role -> { switch (role) { case "admin": - Role adminRole = roleRepository.findByName(ERole.ROLE_ADMIN) + PersonRole adminRole = roleRepository.findByPersonRoleKey(ERole.ROLE_ADMIN) .orElseThrow(() -> new RuntimeException("Error: Role is not found.")); roles.add(adminRole); break; case "mod": - Role modRole = roleRepository.findByName(ERole.ROLE_MODERATOR) + PersonRole modRole = roleRepository.findByPersonRoleKey(ERole.ROLE_MODERATOR) .orElseThrow(() -> new RuntimeException("Error: Role is not found.")); roles.add(modRole); break; default: - Role userRole = roleRepository.findByName(ERole.ROLE_USER) + PersonRole userRole = roleRepository.findByPersonRoleKey(ERole.ROLE_USER) .orElseThrow(() -> new RuntimeException("Error: Role is not found.")); roles.add(userRole); } }); } - person.setRoles(roles); +// person.setRoles(roles); userRepository.save(person); return ResponseEntity.ok(new MessageResponse("User registered successfully!")); diff --git a/src/main/java/com/ossez/spring/security/models/entity/Person.java b/src/main/java/com/ossez/spring/security/models/entity/Person.java index 7a6a359..ec3988e 100644 --- a/src/main/java/com/ossez/spring/security/models/entity/Person.java +++ b/src/main/java/com/ossez/spring/security/models/entity/Person.java @@ -1,7 +1,8 @@ package com.ossez.spring.security.models.entity; -import lombok.Data; +import lombok.*; import lombok.experimental.Accessors; +import org.springframework.data.jpa.domain.AbstractPersistable; import javax.persistence.*; import javax.validation.constraints.Email; @@ -12,32 +13,31 @@ import java.util.Set; /** * Person Entity + * * @author YuCheng Hu */ +@Getter +@Setter @Entity -@Data() @Accessors(chain = true) -@Table(name = "Person", uniqueConstraints = {@UniqueConstraint(columnNames = "user_name"), @UniqueConstraint(columnNames = "user_email")}) -public class Person { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; +//@Table(name = "person", uniqueConstraints = {@UniqueConstraint(columnNames = "user_name"), @UniqueConstraint(columnNames = "user_email")}) +public class Person extends AbstractPersistable { - @NotBlank + @NotBlank @Size(max = 20) private String userName; - @NotBlank + @NotBlank @Size(max = 120) private String userPassword; - @NotBlank - @Size(max = 50) + @NotBlank @Email private String userEmail; - @ManyToMany(fetch = FetchType.LAZY) - @JoinTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) - private Set roles = new HashSet<>(); + //// @ManyToMany(fetch = FetchType.LAZY) +//// @JoinTable(name = "person_role", joinColumns = @JoinColumn(name = "id")) + @OneToMany(mappedBy = "person", fetch = FetchType.LAZY, cascade = CascadeType.ALL) + private Set personRoles; } diff --git a/src/main/java/com/ossez/spring/security/models/entity/PersonRole.java b/src/main/java/com/ossez/spring/security/models/entity/PersonRole.java new file mode 100644 index 0000000..16f8721 --- /dev/null +++ b/src/main/java/com/ossez/spring/security/models/entity/PersonRole.java @@ -0,0 +1,20 @@ +package com.ossez.spring.security.models.entity; + +import com.ossez.spring.security.models.ERole; +import lombok.Data; +import org.springframework.data.jpa.domain.AbstractPersistable; + +import javax.persistence.*; + +@Entity +@Data +public class PersonRole extends AbstractPersistable { + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "person_id", nullable = false) + private Person person; + + @Enumerated(EnumType.STRING) + @Column(length = 20) + private ERole personRoleKey; +} \ No newline at end of file diff --git a/src/main/java/com/ossez/spring/security/models/entity/Role.java b/src/main/java/com/ossez/spring/security/models/entity/Role.java deleted file mode 100644 index de354d3..0000000 --- a/src/main/java/com/ossez/spring/security/models/entity/Role.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.ossez.spring.security.models.entity; - -import com.ossez.spring.security.models.ERole; -import lombok.Data; - -import javax.persistence.*; - -@Entity -@Table(name = "roles") -@Data -public class Role { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Integer id; - - @Enumerated(EnumType.STRING) - @Column(length = 20) - private ERole name; -} \ No newline at end of file diff --git a/src/main/java/com/ossez/spring/security/payload/request/LoginRequest.java b/src/main/java/com/ossez/spring/security/payload/request/LoginRequest.java index 1c7d14b..dcc94b1 100644 --- a/src/main/java/com/ossez/spring/security/payload/request/LoginRequest.java +++ b/src/main/java/com/ossez/spring/security/payload/request/LoginRequest.java @@ -1,27 +1,19 @@ package com.ossez.spring.security.payload.request; +import lombok.Data; + import javax.validation.constraints.NotBlank; +/** + * Login Request Obj + * + * @author YuCheng Hu + */ +@Data public class LoginRequest { - @NotBlank - private String username; + @NotBlank + private String userName; - @NotBlank - private String password; - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } + @NotBlank + private String userPassword; } diff --git a/src/main/java/com/ossez/spring/security/repository/RoleRepository.java b/src/main/java/com/ossez/spring/security/repository/RoleRepository.java index 480b895..d452031 100644 --- a/src/main/java/com/ossez/spring/security/repository/RoleRepository.java +++ b/src/main/java/com/ossez/spring/security/repository/RoleRepository.java @@ -3,11 +3,11 @@ package com.ossez.spring.security.repository; import java.util.Optional; import com.ossez.spring.security.models.ERole; -import com.ossez.spring.security.models.entity.Role; +import com.ossez.spring.security.models.entity.PersonRole; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository -public interface RoleRepository extends JpaRepository { - Optional findByName(ERole name); +public interface RoleRepository extends JpaRepository { + Optional findByPersonRoleKey(ERole name); } diff --git a/src/main/java/com/ossez/spring/security/repository/UserRepository.java b/src/main/java/com/ossez/spring/security/repository/UserRepository.java index 4d2ea68..828ce48 100644 --- a/src/main/java/com/ossez/spring/security/repository/UserRepository.java +++ b/src/main/java/com/ossez/spring/security/repository/UserRepository.java @@ -3,15 +3,16 @@ package com.ossez.spring.security.repository; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; import com.ossez.spring.security.models.entity.Person; @Repository -public interface UserRepository extends JpaRepository { - Optional findByUsername(String username); +public interface UserRepository extends CrudRepository { + Optional findByUserName(String userName); - Boolean existsByUsername(String username); + Boolean existsByUserName(String userName); - Boolean existsByEmail(String email); + Boolean existsByUserEmail(String userEmail); } diff --git a/src/main/java/com/ossez/spring/security/security/WebSecurityConfig.java b/src/main/java/com/ossez/spring/security/security/WebSecurityConfig.java index 3be3322..b851d83 100644 --- a/src/main/java/com/ossez/spring/security/security/WebSecurityConfig.java +++ b/src/main/java/com/ossez/spring/security/security/WebSecurityConfig.java @@ -86,7 +86,7 @@ public class WebSecurityConfig { // extends WebSecurityConfigurerAdapter { http.cors().and().csrf().disable() .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() - .authorizeRequests().antMatchers("/h2-console/**").permitAll() + .authorizeRequests().antMatchers("/custom/**").permitAll() .antMatchers("/api/test/**").permitAll() .anyRequest().authenticated(); diff --git a/src/main/java/com/ossez/spring/security/security/services/UserDetailsImpl.java b/src/main/java/com/ossez/spring/security/security/services/UserDetailsImpl.java index 0a0e15b..2d9487f 100644 --- a/src/main/java/com/ossez/spring/security/security/services/UserDetailsImpl.java +++ b/src/main/java/com/ossez/spring/security/security/services/UserDetailsImpl.java @@ -36,16 +36,18 @@ public class UserDetailsImpl implements UserDetails { } public static UserDetailsImpl build(Person user) { - List authorities = user.getRoles().stream() - .map(role -> new SimpleGrantedAuthority(role.getName().name())) - .collect(Collectors.toList()); +// List authorities = user.getPersonRoles().stream() +// .map(role -> new SimpleGrantedAuthority(role.toString())) +// .collect(Collectors.toList()); +// +// return new UserDetailsImpl( +// user.getId(), +// user.getUserName(), +// user.getUserEmail(), +// user.getUserPassword(), +// authorities); - return new UserDetailsImpl( - user.getId(), - user.getUserName(), - user.getUserEmail(), - user.getUserPassword(), - authorities); + return null; } @Override diff --git a/src/main/java/com/ossez/spring/security/security/services/UserDetailsServiceImpl.java b/src/main/java/com/ossez/spring/security/security/services/UserDetailsServiceImpl.java index 353c7d6..2c6c3fe 100644 --- a/src/main/java/com/ossez/spring/security/security/services/UserDetailsServiceImpl.java +++ b/src/main/java/com/ossez/spring/security/security/services/UserDetailsServiceImpl.java @@ -12,16 +12,19 @@ import com.ossez.spring.security.repository.UserRepository; @Service public class UserDetailsServiceImpl implements UserDetailsService { - @Autowired - UserRepository userRepository; + final UserRepository userRepository; - @Override - @Transactional - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - Person user = userRepository.findByUsername(username) - .orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username)); + @Autowired + public UserDetailsServiceImpl(UserRepository userRepository) { + this.userRepository = userRepository; + } - return UserDetailsImpl.build(user); - } + @Override + @Transactional + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + Person user = userRepository.findByUserName(username).orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username)); + + return UserDetailsImpl.build(user); + } } diff --git a/src/test/java/com/ossez/spring/security/PersonTest.java b/src/test/java/com/ossez/spring/security/PersonTest.java new file mode 100644 index 0000000..608f85b --- /dev/null +++ b/src/test/java/com/ossez/spring/security/PersonTest.java @@ -0,0 +1,37 @@ +package com.ossez.spring.security; + +import com.ossez.spring.security.models.entity.Person; +import com.ossez.spring.security.repository.UserRepository; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.annotation.Commit; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author YuCheng Hu + */ +@Transactional +@DataJpaTest +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@Slf4j +public class PersonTest { + + @Autowired + private UserRepository userRepository; + + @Test + @Commit + void testQueryPerson() { + + Person person = userRepository.findByUserName("huyuchengus").stream().findFirst().orElse(null); + + log.debug("Get Person Email - [{}]", person.getUserEmail()); + log.debug("ROLEs - [{}]", person.getPersonRoles().size()); + } + +} \ No newline at end of file diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml new file mode 100644 index 0000000..fc9a7d2 --- /dev/null +++ b/src/test/resources/logback.xml @@ -0,0 +1,63 @@ + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n + + + + + + true + + + ${user.home}/logs/usreio/%d{yyyy-MM-dd}/usreio-debug.log + + 30 + 3GB + + true + true + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + true + + ERROR + + + + ${user.home}/logs/usreio/%d{yyyy-MM-dd}/usreio-error.log + + 30 + 3GB + + true + true + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + \ No newline at end of file