根据内容:https://www.ossez.com/t/spring-lombok/14129 修改实体类死循环问题

This commit is contained in:
YuCheng Hu 2022-10-02 17:26:46 -04:00
parent ea675b8482
commit efbefa7902
13 changed files with 215 additions and 102 deletions

16
pom.xml
View File

@ -71,17 +71,31 @@
<scope>provided</scope>
</dependency>
<!-- TEST -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!--/ TEST -->
</dependencies>
<build>

View File

@ -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<String> strRoles = signUpRequest.getRole();
Set<Role> roles = new HashSet<>();
Set<PersonRole> 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!"));

View File

@ -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<Long> {
@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<Role> 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<PersonRole> personRoles;
}

View File

@ -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<Long> {
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "person_id", nullable = false)
private Person person;
@Enumerated(EnumType.STRING)
@Column(length = 20)
private ERole personRoleKey;
}

View File

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

View File

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

View File

@ -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<Role, Long> {
Optional<Role> findByName(ERole name);
public interface RoleRepository extends JpaRepository<PersonRole, Long> {
Optional<PersonRole> findByPersonRoleKey(ERole name);
}

View File

@ -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<Person, Long> {
Optional<Person> findByUsername(String username);
public interface UserRepository extends CrudRepository<Person, Long> {
Optional<Person> findByUserName(String userName);
Boolean existsByUsername(String username);
Boolean existsByUserName(String userName);
Boolean existsByEmail(String email);
Boolean existsByUserEmail(String userEmail);
}

View File

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

View File

@ -36,16 +36,18 @@ public class UserDetailsImpl implements UserDetails {
}
public static UserDetailsImpl build(Person user) {
List<GrantedAuthority> authorities = user.getRoles().stream()
.map(role -> new SimpleGrantedAuthority(role.getName().name()))
.collect(Collectors.toList());
// List<GrantedAuthority> 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

View File

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

View File

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

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" ?>
<configuration debug="true">
<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/>
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned by default the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>
%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n
</Pattern>
</layout>
</appender>
<!-- FILE-DEBUG -->
<appender name="FILE-DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<prudent>true</prudent>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover -->
<fileNamePattern>${user.home}/logs/usreio/%d{yyyy-MM-dd}/usreio-debug.log</fileNamePattern>
<!-- keep 30 days' worth of history capped at 3GB total size -->
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<append>true</append>
<immediateFlush>true</immediateFlush>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- FILE-ERROR -->
<appender name="FILE-ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<prudent>true</prudent>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover -->
<fileNamePattern>${user.home}/logs/usreio/%d{yyyy-MM-dd}/usreio-error.log</fileNamePattern>
<!-- keep 30 days' worth of history capped at 3GB total size -->
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<append>true</append>
<immediateFlush>true</immediateFlush>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- LOGGER -->
<logger name="com.ossez" level="DEBUG"/>
<logger name="org.apache" level="INFO"/>
<logger name="org.hibernate" level="INFO"/>
<logger name="org.springframework" level="INFO"/>
<!-- ROOT AND APPENDER -->
<root level="debug">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE-DEBUG"/>
<appender-ref ref="FILE-ERROR"/>
</root>
</configuration>