У меня есть простой сценарий, в котором есть сущности:
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@Data
public abstract class AuditModel implements Serializable {
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_at", nullable = false, updatable = false)
@CreatedDate
private Date createdAt;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "updated_at", nullable = false)
@LastModifiedDate
private Date updatedAt;
@Column(name = "created_by")
@CreatedBy
private String createdBy;
@Column(name = "modified_by")
@LastModifiedBy
private String modifiedBy;
}
Пользователь. java
@Data
@Entity(name = "users")
@NoArgsConstructor
@RequiredArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class User extends AuditModel{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="id", unique = true, nullable = false)
private Integer userId;
@NonNull
private String userFirstName;
@NonNull
private String userLastName;
@Email
@NonNull
private String userEmail;
@NonNull
private String userPassword;
@Column(name = "enabled")
private boolean userEnabled;
@NonNull
private String userFbLink;
@NonNull
private String userTwLink;
@NonNull
private String userLiLink;
@NonNull
@ManyToMany(fetch = FetchType.EAGER, cascade=CascadeType.MERGE)
@JoinTable(
name="user_role",
joinColumns={@JoinColumn(name="USER_ID", referencedColumnName="ID")},
inverseJoinColumns={@JoinColumn(name="ROLE_ID", referencedColumnName="ID")})
private List<Role> roles;
}
Роль сущности так же просто, как: Роль. java
@Data
@Entity(name = "roles")
@NoArgsConstructor
@RequiredArgsConstructor
@EqualsAndHashCode(callSuper = false, exclude = {"userList"})
public class Role extends AuditModel{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="id", unique = true, nullable = false)
private Integer roleId;
@NonNull
@Column(nullable = false, unique = true)
@NotEmpty
private String roleName;
@ManyToMany(mappedBy = "roles")
private List<User> userList;
}
Конфигурация Spring Security имеет вид:
SecurityConfiguration. java
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final UserDetailsServiceImpl userDetailsService;
@Autowired
public SecurityConfiguration(UserDetailsServiceImpl userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/assets/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
String [] permissibleResources = {"/register**", "/login**","/post_register", "/confirm**", "/reset", "/api/**"};
http.csrf().disable()
.authorizeRequests()
.antMatchers(permissibleResources).permitAll()
// START Add missing configs
.anyRequest()
.authenticated()
// END Add missing configs
.and()
.formLogin()
.loginPage("/login")
// username password
.usernameParameter("username")
.passwordParameter("password")
// success and failure handlers
.successHandler(appAuthenticationSuccessHandler())
.failureHandler(appAuthenticationFailureHandler())
.permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login?logout")
.invalidateHttpSession(true)
.clearAuthentication(true)
.permitAll()
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler())
.and()
.headers()
.defaultsDisabled()
.cacheControl()
;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(daoAuthenticationProvider());
}
@Bean
public AccessDeniedHandler accessDeniedHandler(){
return new AppAccessDeniedHandler();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public DaoAuthenticationProvider daoAuthenticationProvider(){
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(bCryptPasswordEncoder());
return provider;
}
@Bean
public PasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder(5);
}
// Auth success handler
@Bean
public AuthenticationSuccessHandler appAuthenticationSuccessHandler(){
return new AppAuthenticationSuccessHandler();
}
// Auth failure handler
@Bean
public AuthenticationFailureHandler appAuthenticationFailureHandler() {
ExceptionMappingAuthenticationFailureHandler failureHandler = new ExceptionMappingAuthenticationFailureHandler();
Map<String, String> failureUrlMap = new HashMap<>();
failureUrlMap.put(BadCredentialsException.class.getName(), AppAuthenticationFailureHandler.BAD_CREDENTIALS_URL);
failureUrlMap.put(AccountExpiredException.class.getName(), AppAuthenticationFailureHandler.EXPIRED_URL);
failureUrlMap.put(LockedException.class.getName(), AppAuthenticationFailureHandler.LOCKED_URL);
failureUrlMap.put(DisabledException.class.getName(), AppAuthenticationFailureHandler.DISABLED_URL);
failureHandler.setExceptionMappings(failureUrlMap);
return failureHandler;
}
}
все работало нормально, но пока я обновляю указанные c поля поля пользовательский объект со следующей формой:
<form th:action="@{/user/__${user.userId}__/update}" th:object="${user}" method="post">
<h5>Personal</h5>
<hr>
<input type="hidden" th:value="${user.userId}" th:field="*{userId}" /> <br>
<input type="text" th:field="*{userFirstName}" th:value="${user.userFirstName}" required /> <br>
<input type="text" th:field="*{userLastName}" th:value="${user.userLastName}" required /><br>
<h5>Social</h5>
<hr>
<input type="url" th:field="*{userFbLink}" th:value="${user.userFbLink}" /><br>
<input type="url" th:field="*{userTwLink}" th:value="${user.userTwLink}" /><br>
<input type="url" th:field="*{userLiLink}" th:value="${user.userLiLink}" /><br>
<button name="save-user" type="submit">Save</button>
</div>
</form>
через UserController. java задан ниже:
@Controller
@RequestMapping(value = "/user")
public class UserController {
private final UserService userService;
@Autowired
public UserController(
UserService userService
) {
this.userService = userService;
}
@PreAuthorize(value = "hasAuthority('USER')")
@GetMapping(value = "/{id}/edit")
public String editBio(
@PathVariable("id") Integer id,
Model model
){
User user = userService.findById(id);
String pageTitle = "Edit User " + user.getUserFirstName() ;
model.addAttribute("pageTitle", pageTitle);
model.addAttribute("user", user);
return "edit_user";
}
@PreAuthorize(value = "hasAuthority('USER')")
@PostMapping(value = "/{id}/update")
public String updateBio(
@PathVariable("id") Integer id,
@ModelAttribute("user") @Valid User user,
BindingResult result
){
if (result.hasErrors()) {
return "edit_user";
}
userService.save(user);
return "redirect:/user/" + id;
}
}
выполняет запрос как:
Hibernate: update users set created_by=?, modified_by=?, user_first_name=?, user_last_name=?, updated_at=?, enabled=?, user_fb_link=?, user_li_link=?, user_password=?, user_tw_link=?, where id=?
и
Hibernate: delete from user_role where user_id=?
Вставляет новые данные только в поля, указанные в форме обновления, все остальные поля становятся null
, даже пароль пользователя становится null
, а включенное поле становится false
. Роль пользователя также удаляется. Когда я выхожу и пытаюсь войти снова, я получаю ошибку disabled
. Пару дней go все работало нормально, без каких-либо странных действий. Я много искал и нашел @ DynamicUpdate с Spring Data JPA , но это мне никогда не помогало. Кто-нибудь, пожалуйста, помогите мне в этом случае.