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 个表,如下所示:

Spring Security 为用户示例添加角色_第1张图片

一个用户可以有一个或多个角色,一个角色可以分配给一个或多个用户,因此用户角色表之间的实体关系是多对多的。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 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. 单元测试——创建角色

 

接下来,让我们编写以下测试类,用于将一些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 listRoles = repo.findAll();
         
        assertThat(listRoles.size()).isEqualTo(3);
    }
     
}

运行testCreateRoles()方法,我们最终将根据 3 个角色将 3 个新行插入到角色表中:用户、管理员和客户。


3. 单元测试——给用户添加角色

要测试向用户添加角色,请使用以下初始代码创建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("[email protected]");
    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());
     
}

以下测试方法创建一个具有管理员角色的新用户:

@Test
public void testAddRoleToNewUser() {
    Role roleAdmin = roleRepo.findByName("Admin");
     
    User user = new User();
    user.setEmail("[email protected]");
    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);      
}

运行这些测试方法,您将看到插入到usersusers_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 listAll() {
    return userRepo.findAll();
}

在控制器类中:

@GetMapping("/users")
public String listUsers(Model model) {
    List listUsers = service.listAll();
    model.addAttribute("listUsers", listUsers);
     
    return "users";
}

此处理程序方法将显示从数据库中检索到的用户列表。并将以下相关代码放入视图页面(HTML):

User ID E-mail First Name Last Name Roles
User ID E-mail First Name Last Name Roles Edit

它将在 URL http://localhost.../users 处显示用户列表,如下所示:

Spring Security 为用户示例添加角色_第2张图片

在此用户列表页面上,我们可以单击编辑超链接来编辑用户。所以像这样编写处理程序方法:

@GetMapping("/users/edit/{id}")
public String editUser(@PathVariable("id") Long id, Model model) {
    User user = service.get(id);
    List 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 listRoles() {
    return roleRepo.findAll();
}

在视图层,为编辑用户表单编写如下代码:

此页面中最重要的是显示角色列表并检查分配给当前用户的角色的代码:



然后编辑用户表单将如下所示:

Spring Security 为用户示例添加角色_第3张图片

这里很酷的是,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 应用程序中向用户添加角色的代码示例。我希望您发现这个书面教程对您有所帮助。

相关 Spring Security 教程:

  • Spring Security 忘记密码教程
  • Spring Security 限制登录尝试示例
  • Spring Security OTP 电子邮件教程
  • 使用 JPA、Hibernate 和 MySQL 进行 Spring Security 身份验证
  • Spring Security 基于角色的授权教程
  • Spring Security 自定义登录和注销
  • 如何使用 Spring Security 获取登录用户的详细信息
  • Spring Security:如果用户已经登录,则阻止用户返回登录页面
  • Spring Security 身份验证成功处理程序示例
  • Spring Security 身份验证失败处理程序示例
  • Spring Security 注销成功处理程序示例
  • 身份验证过滤器示例之前的 Spring Security

 

其他 Spring Boot 教程:

  • 如何创建 Spring Boot Web 应用程序(带有 JSP/ThymeLeaf 的 Spring MVC)
  • 使用 Spring MVC 的 Spring Boot CRUD 示例 - Spring Data JPA - ThymeLeaf - Hibernate - MySQL
  • Spring Boot Hello World RESTful Web 服务教程
  • Spring Boot Thymeleaf 表单处理教程
  • Spring Data JPA 分页和排序示例
  • Spring Boot 错误处理指南
  • Spring Boot 日志记录基础

附件:

SpringBootRegistrationLoginAddRoles.zip
[示例 Spring Boot 项目] 97 KB

你可能感兴趣的:(spring,java,junit)