Spring?Security如何為用戶示例添加角色詳解
前言
在這個 Spring Security 教程中,我很樂意與您分享如何通過在 Java Web 應用程序中為用戶添加角色來實現授權——從數據庫設計到實體類;從單元測試到在用戶注冊中添加默認角色;以 Web 形式更新用戶的角色。
技術:Spring Web MVC、Spring Data JPA、Hibernate 框架、Spring Security、Spring Boot Test、JUnit 5、AssertJ、Thymeleaf 和 MySQL 數據庫。
基本上,我們需要在數據庫中有 3 個表,如下所示:
一個用戶可以有一個或多個角色,一個角色可以分配給一個或多個用戶,因此用戶和角色表之間的實體關系是多對多的。users_roles是實現這種關系的中間表。
1. 用戶和角色實體類和存儲庫的代碼
將User 實體類編碼如下:
package net.codejava; import java.util.HashSet; import java.util.Set; import javax.persistence.*; @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true, length = 45) private String email; @Column(nullable = false, length = 64) private String password; @Column(name = "first_name", nullable = false, length = 20) private String firstName; @Column(name = "last_name", nullable = false, length = 20) private String lastName; @ManyToMany(fetch = FetchType.EAGER) @JoinTable( name = "users_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id") ) private Set<Role> roles = new HashSet<>(); public void addRole(Role role) { this.roles.add(role); } // getters and setters are not shown for brevity }
并像這樣對Role 實體類進行編碼:
package net.codejava; import javax.persistence.*; @Entity @Table(name = "roles") public class Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(nullable = false, length = 45) private String name; public Role() { } public Role(String name) { this.name = name; } public Role(Integer id, String name) { this.id = id; this.name = name; } public Role(Integer id) { this.id = id; } @Override public String toString() { return this.name; } // getters and setters are not shown for brevity }
如您所見,User類有一組角色,但Role類沒有任何對 User 的引用。默認情況下, @ManyToMany關系上沒有級聯操作——這意味著更新User對象不會更改關聯的Role對象。
2. 單元測試——創(chuàng)建角色
接下來,讓我們編寫以下測試類,用于將一些Role對象持久化到數據庫中:
package net.codejava; import static org.assertj.core.api.Assertions.assertThat; import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.test.annotation.Rollback; @DataJpaTest @AutoConfigureTestDatabase(replace = Replace.NONE) @Rollback(false) public class RoleRepositoryTests { @Autowired private RoleRepository repo; @Test public void testCreateRoles() { Role user = new Role("User"); Role admin = new Role("Admin"); Role customer = new Role("Customer"); repo.saveAll(List.of(user, admin, customer)); List<Role> listRoles = repo.findAll(); assertThat(listRoles.size()).isEqualTo(3); } }
運行testCreateRoles()方法,我們最終將根據 3 個角色將 3 個新行插入到角色表中:用戶、管理員和客戶。
3. 單元測試——給用戶添加角色
要測試向用戶添加角色,請使用以下初始代碼創(chuàng)建UserRepositoryTests類:
package net.codejava; import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; import org.springframework.test.annotation.Rollback; @DataJpaTest @AutoConfigureTestDatabase(replace = Replace.NONE) @Rollback(false) public class UserRepositoryTests { @Autowired private TestEntityManager entityManager; @Autowired private UserRepository userRepo; @Autowired private RoleRepository roleRepo; // test methods go here... }
以下是第一個測試方法的代碼片段,它保留了一個沒有任何角色的用戶對象:
@Test public void testCreateUser() { User user = new User(); user.setEmail("ravikumar@gmail.com"); user.setPassword("ravi2020"); user.setFirstName("Ravi"); user.setLastName("Kumar"); User savedUser = userRepo.save(user); User existUser = entityManager.find(User.class, savedUser.getId()); assertThat(user.getEmail()).isEqualTo(existUser.getEmail()); }
以下測試方法創(chuàng)建一個具有管理員角色的新用戶:
@Test public void testAddRoleToNewUser() { Role roleAdmin = roleRepo.findByName("Admin"); User user = new User(); user.setEmail("mikes.gates@gmail.com"); user.setPassword("mike2020"); user.setFirstName("Mike"); user.setLastName("Gates"); user.addRole(roleAdmin); User savedUser = userRepo.save(user); assertThat(savedUser.getRoles().size()).isEqualTo(1); }
以下測試將通過添加兩個角色 User 和 Customer 來更新現有用戶:
@Test public void testAddRoleToExistingUser() { User user = userRepo.findById(1L).get(); Role roleUser = roleRepo.findByName("User"); Role roleCustomer = new Role(3); user.addRole(roleUser); user.addRole(roleCustomer); User savedUser = userRepo.save(user); assertThat(savedUser.getRoles().size()).isEqualTo(2); }
運行這些測試方法,您將看到插入到users和users_roles表中的行。角色表不受影響。
4. 為注冊用戶設置默認角色
用戶注冊中的一個常見場景是為新注冊的用戶設置默認角色,例如用戶或客戶角色。以下是服務層的示例代碼片段:
package net.codejava; @Service public class UserService { @Autowired private UserRepository userRepo; @Autowired RoleRepository roleRepo; @Autowired PasswordEncoder passwordEncoder; public void registerDefaultUser(User user) { Role roleUser = roleRepo.findByName("User"); user.addRole(roleUser); userRepo.save(user); } }
以及控制器層的代碼:
package net.codejava; @Controller public class AppController { @Autowired private UserService service; @PostMapping("/register") public String processRegister(User user) { service.registerDefaultUser(user); return "register_success"; } }
如您所見,這非常簡單——感謝 Spring Data JPA 和 Hibernate 框架,極大地簡化了數據訪問層的編碼。
5. 在 Web 表單中為用戶分配角色
現在,我將向您展示如何使用 Web 用戶界面編寫編輯用戶功能的代碼,我們可以在其中更改分配給用戶的角色。
首先,在UserService 類中實現如下方法:
public List<User> listAll() { return userRepo.findAll(); }
在控制器類中:
@GetMapping("/users") public String listUsers(Model model) { List<User> listUsers = service.listAll(); model.addAttribute("listUsers", listUsers); return "users"; }
此處理程序方法將顯示從數據庫中檢索到的用戶列表。并將以下相關代碼放入視圖頁面(HTML):
<table class="table table-striped table-bordered"> <thead class="thead-dark"> <tr> <th>User ID</th> <th>E-mail</th> <th>First Name</th> <th>Last Name</th> <th>Roles</th> <th></th> </tr> </thead> <tbody> <tr th:each="user: ${listUsers}"> <td th:text="${user.id}">User ID</td> <td th:text="${user.email}">E-mail</td> <td th:text="${user.firstName}">First Name</td> <td th:text="${user.lastName}">Last Name</td> <td th:text="${user.roles}">Roles</td> <td><a th:href="/@{'/users/edit/' + ${user.id}}">Edit</a></td> </tr> </tbody> </table>
它將在 URL http://localhost.../users 處顯示用戶列表,如下所示:
在此用戶列表頁面上,我們可以單擊編輯超鏈接來編輯用戶。所以像這樣編寫處理程序方法:
@GetMapping("/users/edit/{id}") public String editUser(@PathVariable("id") Long id, Model model) { User user = service.get(id); List<Role> listRoles = service.listRoles(); model.addAttribute("user", user); model.addAttribute("listRoles", listRoles); return "user_form"; }
并在服務類中實現以下兩個方法:
public User get(Long id) { return userRepo.findById(id).get(); } public List<Role> listRoles() { return roleRepo.findAll(); }
在視圖層,為編輯用戶表單編寫如下代碼:
<form th:action="@{/users/save}" th:object="${user}" method="post" style="max-width: 600px; margin: 0 auto;"> <input type="hidden" th:field="*{id}" /> <div class="m-3"> <div class="form-group row"> <label class="col-4 col-form-label">E-mail: </label> <div class="col-8"> <input type="email" th:field="*{email}" class="form-control" required /> </div> </div> <div class="form-group row"> <label class="col-4 col-form-label">Password: </label> <div class="col-8"> <input type="password" th:field="*{password}" class="form-control" required minlength="6" maxlength="10"/> </div> </div> <div class="form-group row"> <label class="col-4 col-form-label">First Name: </label> <div class="col-8"> <input type="text" th:field="*{firstName}" class="form-control" required minlength="2" maxlength="20"/> </div> </div> <div class="form-group row"> <label class="col-4 col-form-label">Last Name: </label> <div class="col-8"> <input type="text" th:field="*{lastName}" class="form-control" required minlength="2" maxlength="20" /> </div> </div> <div class="form-group row"> <label class="col-4 col-form-label">Roles: </label> <div class="col-8"> <th:block th:each="role: ${listRoles}"> <input type="checkbox" th:field="*{roles}" th:text="${role.name}" th:value="${role.id}" class="m-2" /> </th:block> </div> </div> <div> <button type="submit" class="btn btn-primary">Update</button> </div> </div> </form>
此頁面中最重要的是顯示角色列表并檢查分配給當前用戶的角色的代碼:
<th:block th:each="role: ${listRoles}"> <input type="checkbox" th:field="*{roles}" th:text="${role.name}" th:value="${role.id}" class="m-2" /> </th:block>
然后編輯用戶表單將如下所示:
這里很酷的是,Thymeleaf 會根據分配給用戶的角色自動顯示選擇的角色。此外,您可以在此處簡單地選中/取消選中角色來更新用戶的角色。
并編寫處理表單提交的處理程序方法,如下所示:
@PostMapping("/users/save") public String saveUser(User user) { service.save(user); return "redirect:/users"; }
以及服務層的相關代碼:
public void save(User user) { userRepo.save(user); }
這是一些關于在 Spring Boot Web 應用程序中向用戶添加角色的代碼示例。我希望您發(fā)現這個書面教程對您有所幫助。
總結
到此這篇關于Spring Security如何為用戶示例添加角色的文章就介紹到這了,更多相關Spring Security用戶示例添加角色內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringCloud 服務網關路由規(guī)則的坑及解決
這篇文章主要介紹了SpringCloud 服務網關路由規(guī)則的坑及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07