카테고리 없음

Spring 웹 mvc + Thymeleaf ModelAttribute 객체의 목록 편집

기록만이살길 2021. 2. 21. 19:17
반응형

Spring 웹 mvc + Thymeleaf ModelAttribute 객체의 목록 편집

1. 질문(문제점):

사용자를 편집 할 수있는 양식이 있고 사용자에게 권한 유형의 개체 목록 인 역할이 있습니다. 확인란 (선택 사항)을 사용하여 사용자가 가질 역할을 설정할 수 있기를 원하지만 모르겠습니다. thymeleaf에서 양식을 구현하는 방법과 주어진 역할을 가진 사용자 개체를 컨트롤러에 전달하는 방법.

내 사용자

import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;

import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
public class User implements UserDetails {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Column(name="username", nullable = false, unique = true)
    @NotBlank @Size(min=5, message = "Не менeе 5 знаков")
    private String username;

    @NotBlank @Size(min=5, message = "Не менeе 5 знаков")
    @Column(name = "password")
    private String password;

    @Column(name = "enabled")
    private boolean enabled;

    @Column(name = "name")
    private String name;

    @Column(name = "surname")
    private String surname;

    @Column(name = "email", nullable = false, unique = true)
    private String email;

    @ManyToMany(cascade = {
            CascadeType.PERSIST,
            CascadeType.MERGE,
            CascadeType.DETACH,
            CascadeType.REFRESH
    }, fetch = FetchType.EAGER)
    @JoinTable(
            name = "users_authorities",
            joinColumns = @JoinColumn(name = "user_id"),
            inverseJoinColumns = @JoinColumn(name = "authority_id")
    )
    private Set<Authority> authorities = new HashSet<>();

    public User(String username, String password, boolean enabled,
                String name, String surname, String email) {
        this.username = username;
        this.password = password;
        this.enabled = enabled;
        this.name = name;
        this.surname = surname;
        this.email = email;
    }

    public void addAuthority(Authority authority) {
        this.authorities.add(authority);
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

}



내 편집 사용자 양식 컨트롤러입니다.

@GetMapping("/users/{id}/edit")
public String editUser(@PathVariable("id") Long id, Model model) {

    model.addAttribute("user", userService.findById(id));
    model.addAttribute("allAuthorities", authorityService.findAll());

    return "users/edit-user";
}



내 편집 사용자 양식보기입니다.

<body>
<form th:method="PUT" th:action="@{/admin/users/{id}(id=${user.getId()})}" th:object="${user}">
    <input type="hidden" th:field="*{id}" id="id">

    <label for="username">Username: </label>
    <input type="text" th:field="*{username}" id="username" placeholder="username">
    <br><br>

    <div sec:authorize="hasRole('ROLE_ADMIN')">
        <label for="enabled">Enabled </label>
        <input type="checkbox" name="enabled" th:field="*{enabled}" id="enabled">
        <br><br>
    </div>

    <label for="name">Name: </label>
    <input type="text" th:field="*{name}" id="name" placeholder="Name">
    <br><br>

    <label for="surname">Surname: </label>
    <input type="text" th:field="*{surname}" id="surname" placeholder="Surname">
    <br><br>

    <label for="email">Email: </label>
    <input type="text" th:field="*{email}" id="email" placeholder="Email">
    <br><br>

    <div th:each="auth:${allAuthorities}">
        <label>
            <span th:text="${auth.authority}"></span>
            <input type="checkbox" name="authorities" th:checked="${user.authorities.contains(auth)}">
        </label>
    </div>

    <input type="submit" value="Edit">
</form>
</body>



컨트롤러를 넣어, 내 양식에서 데이터를 가져옵니다.

@PutMapping("/users/{id}")
public String editUser(@PathVariable("id") Long id,
                       @ModelAttribute("user") User user,
                       @RequestParam("authorities") List<Authority> authorities) {
    user.setId(id);
    userService.update(user);

    return "redirect:/admin/users";
}



그리고 필요한 경우 내 Authority 클래스입니다.

@Entity
@Table(name = "authorities")
@Data
@NoArgsConstructor
public class Authority implements GrantedAuthority {
    @Id
    private Long id;

    @Column(name = "authority")
    private String authority;

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

    public Authority(Long id, String authority) {
        this.id = id;
        this.authority = authority;
    }
}

사용자 개체와 별도로 역할 목록을 전달하려고하는데이 역시 작동하지 않고 잘못된 요청 오류가 발생합니다.

2. 해결방안:

문제를 해결하기 위해 엔티티 권한에 새로운 체크 필드를 추가했습니다.

@Entity
@Table(name = "authorities")
@Data
@NoArgsConstructor
public class Authority implements GrantedAuthority {
    @Id
    private Long id;

    @Column(name = "authority", nullable = false, unique = true)
    private String authority;

    @Transient
    private boolean checked;

    public Authority(Long id, String authority) {
        this.id = id;
        this.authority = authority;
    }
}

보기를 보내기 전에 사용자 개체의 권한 필드를 변경했습니다.

@GetMapping("/users/{id}/edit")
public String editUser(@PathVariable("id") Long id, Model model) {
    User user = userService.findById(id);
    List<Authority> allAuthorities = authorityService.findAll();

    for(Authority auth : allAuthorities) {
        if(user.getAuthorities().contains(auth)) {
            auth.setChecked(true);
        }
    }

    user.setAuthorities(allAuthorities);

    model.addAttribute("user", user);
    return "users/edit-user";
}

나는 또한 thymeleaf 템플릿을 변경했습니다.
<form th:method="PUT" th:action="@{/admin/users/{id}(id=${user.getId()})}" th:object="${user}">

    <label for="username">Username: </label>
    <input type="text" th:field="*{username}" id="username" placeholder="username">
    <br><br>

    <div sec:authorize="hasRole('ROLE_ADMIN')">
        <label for="enabled">Enabled </label>
        <input type="checkbox" name="enabled" th:field="*{enabled}" id="enabled">
        <br><br>
    </div>

    <label for="name">Name: </label>
    <input type="text" th:field="*{name}" id="name" placeholder="Name">
    <br><br>

    <label for="surname">Surname: </label>
    <input type="text" th:field="*{surname}" id="surname" placeholder="Surname">
    <br><br>

    <label for="email">Email: </label>
    <input type="text" th:field="*{email}" id="email" placeholder="Email">
    <br><br>

    <div th:each="auth, itemStat: ${user.authorities}">
        <label>
            <span th:text="${auth.authority}"></span>
            <input type="hidden"
                   th:field="*{authorities[__${itemStat.index}__].id}">
            <input type="hidden"
                   th:field="*{authorities[__${itemStat.index}__].authority}">
            <input type="checkbox" th:checked="${auth.checked}"
                   th:field="*{authorities[__${itemStat.index}__].checked}">
        </label>
    </div>

    <input type="submit" value="Edit">
</form>

내 풋 컨트롤러는 다음과 같습니다.

@PutMapping("/users/{id}")
public String editUser(@PathVariable("id") Long id,
                       @ModelAttribute("user") User user) {
    List<Authority> userAuthorities = user.getAuthorities();
    userAuthorities.removeIf(auth -> !auth.isChecked());

    user.setId(id);
    userService.update(user);

    return "redirect:/admin/users";
}

UserService의 저장 및 삭제 방법은 다음과 같습니다. (유용한 경우)
@Override
@Transactional
public User save(User user) {
    Optional<User> userFromDB = userRepository.findByUsername(user.getUsername());
    Optional<Authority> userRole;
    
    if(userFromDB.isPresent()) {
        return null;
    }

    userRole = authorityRepository.findByAuthority("ROLE_USER");
    user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));

    userRole.ifPresent(user::addAuthority);

    return userRepository.save(user);
}

@Override
@Transactional
public User update(User user) {
    Optional<User> userFromDB = userRepository.findById(user.getId());

    if(userFromDB.isPresent()){
        //Adding the password for successfully update user in DB
        user.setPassword(userFromDB.get().getPassword());
        return userRepository.save(user);
    }

    return null;
}

이것은 가장 우아한 해결책은 아니지만 아직 다른 해결책을 찾지 못했습니다.

65860212
반응형