根据内容: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> <scope>provided</scope>
</dependency> </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> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.security</groupId> <groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId> <artifactId>spring-security-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!--/ TEST -->
</dependencies> </dependencies>
<build> <build>

View File

@ -8,7 +8,7 @@ import java.util.stream.Collectors;
import javax.validation.Valid; import javax.validation.Valid;
import com.ossez.spring.security.models.ERole; 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.LoginRequest;
import com.ossez.spring.security.payload.request.SignupRequest; import com.ossez.spring.security.payload.request.SignupRequest;
import com.ossez.spring.security.payload.response.JwtResponse; import com.ossez.spring.security.payload.response.JwtResponse;
@ -36,26 +36,26 @@ import com.ossez.spring.security.repository.UserRepository;
@RestController @RestController
@RequestMapping("/custom") @RequestMapping("/custom")
public class AuthController { public class AuthController {
@Autowired private final AuthenticationManager authenticationManager;
AuthenticationManager authenticationManager; private final UserRepository userRepository;
private final RoleRepository roleRepository;
private final PasswordEncoder encoder;
private final JwtUtils jwtUtils;
@Autowired @Autowired
UserRepository userRepository; public AuthController(AuthenticationManager authenticationManager, UserRepository userRepository, RoleRepository roleRepository, PasswordEncoder encoder, JwtUtils jwtUtils) {
this.authenticationManager = authenticationManager;
@Autowired this.userRepository = userRepository;
RoleRepository roleRepository; this.roleRepository = roleRepository;
this.encoder = encoder;
@Autowired this.jwtUtils = jwtUtils;
PasswordEncoder encoder; }
@Autowired
JwtUtils jwtUtils;
@PostMapping("/signin") @PostMapping("/signin")
public ResponseEntity<?> authenticateUser(@Valid @RequestBody LoginRequest loginRequest) { public ResponseEntity<?> authenticateUser(@Valid @RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager.authenticate( Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword())); new UsernamePasswordAuthenticationToken(loginRequest.getUserName(), loginRequest.getUserPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication); SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtUtils.generateJwtToken(authentication); String jwt = jwtUtils.generateJwtToken(authentication);
@ -74,13 +74,13 @@ public class AuthController {
@PostMapping("/register") @PostMapping("/register")
public ResponseEntity<?> registerUser(@Valid @RequestBody SignupRequest signUpRequest) { public ResponseEntity<?> registerUser(@Valid @RequestBody SignupRequest signUpRequest) {
if (userRepository.existsByUsername(signUpRequest.getUsername())) { if (userRepository.existsByUserName(signUpRequest.getUsername())) {
return ResponseEntity return ResponseEntity
.badRequest() .badRequest()
.body(new MessageResponse("Error: Username is already taken!")); .body(new MessageResponse("Error: Username is already taken!"));
} }
if (userRepository.existsByEmail(signUpRequest.getEmail())) { if (userRepository.existsByUserEmail(signUpRequest.getEmail())) {
return ResponseEntity return ResponseEntity
.badRequest() .badRequest()
.body(new MessageResponse("Error: Email is already in use!")); .body(new MessageResponse("Error: Email is already in use!"));
@ -94,36 +94,36 @@ public class AuthController {
Set<String> strRoles = signUpRequest.getRole(); Set<String> strRoles = signUpRequest.getRole();
Set<Role> roles = new HashSet<>(); Set<PersonRole> roles = new HashSet<>();
if (strRoles == null) { 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.")); .orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(userRole); roles.add(userRole);
} else { } else {
strRoles.forEach(role -> { strRoles.forEach(role -> {
switch (role) { switch (role) {
case "admin": case "admin":
Role adminRole = roleRepository.findByName(ERole.ROLE_ADMIN) PersonRole adminRole = roleRepository.findByPersonRoleKey(ERole.ROLE_ADMIN)
.orElseThrow(() -> new RuntimeException("Error: Role is not found.")); .orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(adminRole); roles.add(adminRole);
break; break;
case "mod": case "mod":
Role modRole = roleRepository.findByName(ERole.ROLE_MODERATOR) PersonRole modRole = roleRepository.findByPersonRoleKey(ERole.ROLE_MODERATOR)
.orElseThrow(() -> new RuntimeException("Error: Role is not found.")); .orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(modRole); roles.add(modRole);
break; break;
default: default:
Role userRole = roleRepository.findByName(ERole.ROLE_USER) PersonRole userRole = roleRepository.findByPersonRoleKey(ERole.ROLE_USER)
.orElseThrow(() -> new RuntimeException("Error: Role is not found.")); .orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(userRole); roles.add(userRole);
} }
}); });
} }
person.setRoles(roles); // person.setRoles(roles);
userRepository.save(person); userRepository.save(person);
return ResponseEntity.ok(new MessageResponse("User registered successfully!")); return ResponseEntity.ok(new MessageResponse("User registered successfully!"));

View File

@ -1,7 +1,8 @@
package com.ossez.spring.security.models.entity; package com.ossez.spring.security.models.entity;
import lombok.Data; import lombok.*;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import org.springframework.data.jpa.domain.AbstractPersistable;
import javax.persistence.*; import javax.persistence.*;
import javax.validation.constraints.Email; import javax.validation.constraints.Email;
@ -12,32 +13,31 @@ import java.util.Set;
/** /**
* Person Entity * Person Entity
*
* @author YuCheng Hu * @author YuCheng Hu
*/ */
@Getter
@Setter
@Entity @Entity
@Data()
@Accessors(chain = true) @Accessors(chain = true)
@Table(name = "Person", uniqueConstraints = {@UniqueConstraint(columnNames = "user_name"), @UniqueConstraint(columnNames = "user_email")}) //@Table(name = "person", uniqueConstraints = {@UniqueConstraint(columnNames = "user_name"), @UniqueConstraint(columnNames = "user_email")})
public class Person { public class Person extends AbstractPersistable<Long> {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank @NotBlank
@Size(max = 20) @Size(max = 20)
private String userName; private String userName;
@NotBlank @NotBlank
@Size(max = 120) @Size(max = 120)
private String userPassword; private String userPassword;
@NotBlank @NotBlank
@Size(max = 50)
@Email @Email
private String userEmail; private String userEmail;
@ManyToMany(fetch = FetchType.LAZY) //// @ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) //// @JoinTable(name = "person_role", joinColumns = @JoinColumn(name = "id"))
private Set<Role> roles = new HashSet<>(); @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; package com.ossez.spring.security.payload.request;
import lombok.Data;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
/**
* Login Request Obj
*
* @author YuCheng Hu
*/
@Data
public class LoginRequest { public class LoginRequest {
@NotBlank @NotBlank
private String username; private String userName;
@NotBlank @NotBlank
private String password; private String userPassword;
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;
}
} }

View File

@ -3,11 +3,11 @@ package com.ossez.spring.security.repository;
import java.util.Optional; import java.util.Optional;
import com.ossez.spring.security.models.ERole; 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.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@Repository @Repository
public interface RoleRepository extends JpaRepository<Role, Long> { public interface RoleRepository extends JpaRepository<PersonRole, Long> {
Optional<Role> findByName(ERole name); Optional<PersonRole> findByPersonRoleKey(ERole name);
} }

View File

@ -3,15 +3,16 @@ package com.ossez.spring.security.repository;
import java.util.Optional; import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import com.ossez.spring.security.models.entity.Person; import com.ossez.spring.security.models.entity.Person;
@Repository @Repository
public interface UserRepository extends JpaRepository<Person, Long> { public interface UserRepository extends CrudRepository<Person, Long> {
Optional<Person> findByUsername(String username); 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() http.cors().and().csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests().antMatchers("/h2-console/**").permitAll() .authorizeRequests().antMatchers("/custom/**").permitAll()
.antMatchers("/api/test/**").permitAll() .antMatchers("/api/test/**").permitAll()
.anyRequest().authenticated(); .anyRequest().authenticated();

View File

@ -36,16 +36,18 @@ public class UserDetailsImpl implements UserDetails {
} }
public static UserDetailsImpl build(Person user) { public static UserDetailsImpl build(Person user) {
List<GrantedAuthority> authorities = user.getRoles().stream() // List<GrantedAuthority> authorities = user.getPersonRoles().stream()
.map(role -> new SimpleGrantedAuthority(role.getName().name())) // .map(role -> new SimpleGrantedAuthority(role.toString()))
.collect(Collectors.toList()); // .collect(Collectors.toList());
//
// return new UserDetailsImpl(
// user.getId(),
// user.getUserName(),
// user.getUserEmail(),
// user.getUserPassword(),
// authorities);
return new UserDetailsImpl( return null;
user.getId(),
user.getUserName(),
user.getUserEmail(),
user.getUserPassword(),
authorities);
} }
@Override @Override

View File

@ -12,16 +12,19 @@ import com.ossez.spring.security.repository.UserRepository;
@Service @Service
public class UserDetailsServiceImpl implements UserDetailsService { public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired final UserRepository userRepository;
UserRepository userRepository;
@Override @Autowired
@Transactional public UserDetailsServiceImpl(UserRepository userRepository) {
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { this.userRepository = userRepository;
Person user = userRepository.findByUsername(username) }
.orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username));
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>