【问题标题】:StackOverflowError with Hibernate when trying to implement JWT Security尝试实现 JWT 安全性时出现 Hibernate 的 StackOverflowError
【发布时间】:2020-09-02 08:44:35
【问题描述】:

我是这个主题的新手,当我尝试授权时,我不断收到“java.lang.StackOverflowError: null”。我在邮递员中收到内部服务器错误,在控制台中我收到 stackoveflowerror。 这是我的 AuthenticationController:


   @PostMapping("/signup")
   public ResponseEntity<?> registerUser(@Valid @RequestBody SignUpRequest signUpRequest) {
       if (userService.existsByEmail(signUpRequest.getEmail())) {
           throw new BadRequestException("email already exists");
       }
       User user = UserMapper.INSTANCE.registerRequestoUser(signUpRequest);
       user.setPassword(passwordEncoder.encode(user.getPassword()));
       Optional<Role> optionalRole = roleService.getByName("user");

       if (optionalRole.isPresent()) {
           Role userRole = optionalRole.get();
           user.addRole(userRole);
           Optional<User> optionalUser = userService.create(user);
           if (optionalUser.isPresent()) {
               User result = optionalUser.get();
               URI location = ServletUriComponentsBuilder
                       .fromCurrentContextPath().path("/api/v1/users/email/{email}")
                       .buildAndExpand(result.getEmail()).toUri();

               return ResponseEntity.created(location).body("User registered successfully");
           }

       }
       return (ResponseEntity<?>) ResponseEntity.badRequest();
   }

我的 SignUpRequest 类:


public class SignUpRequest {
   @NotBlank
   private String firstName;

   @NotBlank
   private String lastName;

   @NotBlank
   @Email
   private String email;

   @NotBlank
   @Size(min = 8, max = 20)
   private String password;

   private Set<String> roles;

我的用户实体和我的角色实体:

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User extends BaseEntity {

    private String firstName;

    private String lastName;

    @Email(message = "Email should be valid")
    private String email;

    @Size(min = 3, max = 100, message
            = "password must be between 3 and 50 characters")
    private String password;

    @OneToMany(mappedBy = "user",
            cascade = {CascadeType.PERSIST, CascadeType.MERGE,
                    CascadeType.DETACH, CascadeType.REFRESH})
    private Set<Appointment> appointments;


    @ManyToMany(targetEntity = Role.class,
            cascade = {CascadeType.ALL},
            fetch = FetchType.EAGER)
    @JoinTable(
            name = "user_role",
            joinColumns = {@JoinColumn(name = "user_id")},
            inverseJoinColumns = {@JoinColumn(name = "role_id")}
    )
    private Set<Role> roles;
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Role extends BaseEntity {

    @NotNull
    private String name;

    @ManyToMany(mappedBy = "roles")
    private Set<User> users;

    public String  getName() {
        return name;
    }

这是我的邮递员请求

{
        "firstName":"name",
        "lastName":"name",
        "email": "an@gmail.com",
        "password": "thepassword123",
        "roles": ["user"]

}

这是堆栈跟踪中的一部分:

at at com.project.rushhour.entity.User.hashCode(User.java:15) ~[classes/:na]
...
at at com.project.rushhour.entity.Role.hashCode(Role.java:15) ~[classes/:na]
...


at com.project.rushhour.entity.User.hashCode(User.java:15) ~[classes/:na]
...
at com.project.rushhour.entity.Role.hashCode(Role.java:15) ~[classes/:na]
...


at com.project.rushhour.entity.User.hashCode(User.java:15) ~[classes/:na]
...
at com.project.rushhour.entity.Role.hashCode(Role.java:15) ~[classes/:na]
...


我认为错误是在程序尝试解析角色时出现的,但不确定到底是什么问题。 jwt的东西设置正确,只是逻辑错了

【问题讨论】:

  • 嗨,这是完整的例外pastebin.com/tU92bnKP 它很长
  • 显示你的堆栈跟踪。它应该是问题的一部分。不要使用任何外部资源。
  • @MaartenBodewes:不 :) 那是不正确的。通常很少有行重复多次。通常这些是大约 15-20 行。所以粘贴 15-20 行就足够了。
  • @MaartenBodewes:我添加了堆栈跟踪的重复部分。如您所见,它占用的空间并不多:)
  • @mkashi:您没有表现出任何分析堆栈跟踪的努力,实际上您的问题应该被否决。但我喜欢你的问题是关于 StackOverflowError - 这是这个网站的名称。所以我投票赞成你的问题:)

标签: java spring hibernate spring-boot lombok


【解决方案1】:

问题的原因是,您有从 UserRole 以及从 Role 到用户的关系。但是您还没有明确定义 hashCode 方法。您使用 @Data 注释。默认情况下,Lombok 会生成 hashCode 方法,其中包含 所有 实体的属性。

hashCode 通常计算如下:

h = hashCode(property1);
h = h*31 + hashCode(property2);
h = h*31 + hashCode(property3);
h = h*31 + hashCode(property4);
h = h*31 + hashCode(property5);
...

用户上调用hashCode时,如果计算属性firstNamelastName等的hash码。然后还添加属性 roles 的哈希码。 roles的哈希码计算包括对每个Role元素的哈希码的计算。

但哈希码Role的计算包括属性users的哈希码计算,它在每个User元素上调用hashCode()。正如我们在上面看到的,User 的哈希码将再次与 Role 的哈希码进行比较。等,直到堆栈容量被使用。然后你会得到 StackOverflowError

你怎么能避免它?从 hashCode() 方法中排除递归。要么手动实现 hashCode() 。或者告诉 Lombok 从 hashCode() 中排除基于关系的属性。

我希望 Lombok 仍然为我生成一切。所以我会告诉 Lombok 在生成时忽略 User 实体中的属性 rolesRole 实体中的属性 users hashCode()equals()。所以你的代码如下所示:

public class User extends BaseEntity {

    private String firstName;

    private String lastName;

    ...
    @EqualsAndHashCode.Exclude
    private Set<Role> roles;

    ...
}

public class Role extends BaseEntity {

    ...    
    private String name;

    ...
    @EqualsAndHashCode.Exclude
    private Set<User> users;

    ...

}

为什么注解叫EqualsAndHashCode?因为 hashCode()equals() 应始终同步 更改以履行所谓的 hashCode - equals 合同。

【讨论】:

    猜你喜欢
    • 2016-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-02
    • 2020-06-20
    • 2016-11-07
    • 1970-01-01
    相关资源
    最近更新 更多