leftso 2369 0 2018-09-02 17:07:33

logo-cover-spring boot 入门 整合security jpa角色和权限的实现

1.概述

本文继续使用spring boot 和Spring Security系列进行注册,并着重于如何正确实现角色和权限
 

2. 用户 角色权限

首先,让我们从我们的实体开始。我们有三个主要实体:

  • User
  • Role -这代表在系统中的用户的高级别角色; 每个角色都将拥有一组低级权限
  • Privilege  -表示系统中一个低级别的,细小的特权/权限

User.java:
 

@Entity
public class User {
  
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
 
    private String firstName;
    private String lastName;
    private String email;
    private String password;
    private boolean enabled;
    private boolean tokenExpired;
 
    @ManyToMany
    @JoinTable( 
        name = "users_roles", 
        joinColumns = @JoinColumn(
          name = "user_id", referencedColumnName = "id"), 
        inverseJoinColumns = @JoinColumn(
          name = "role_id", referencedColumnName = "id")) 
    private Collection<Role> roles;
}

如您所见,用户包含角色,但也包含适当注册机制所需的一些其他详细信息。

接下来 - 这是Role.java

@Entity
public class Role {
  
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
 
    private String name;
    @ManyToMany(mappedBy = "roles")
    private Collection<User> users;
 
    @ManyToMany
    @JoinTable(
        name = "roles_privileges", 
        joinColumns = @JoinColumn(
          name = "role_id", referencedColumnName = "id"), 
        inverseJoinColumns = @JoinColumn(
          name = "privilege_id", referencedColumnName = "id"))
    private Collection<Privilege> privileges;   
}
最后Privilege:
@Entity
public class Privilege {
  
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
 
    private String name;
 
    @ManyToMany(mappedBy = "privileges")
    private Collection<Role> roles;
}
如您所见,我们正在考虑用户角色以及角色特权关系多对多双向

3.设置权限和角色

接下来 - 让我们专注于在系统中对权限和角色进行一些早期设置。

我们将把它与应用程序的启动绑定在一起,我们将在ContextRefreshedEvent上使用ApplicationListener在服务器启动时加载我们的初始数据:

@Component
public class InitialDataLoader implements
  ApplicationListener<ContextRefreshedEvent> {
 
    boolean alreadySetup = false;
 
    @Autowired
    private UserRepository userRepository;
  
    @Autowired
    private RoleRepository roleRepository;
  
    @Autowired
    private PrivilegeRepository privilegeRepository;
  
    @Autowired
    private PasswordEncoder passwordEncoder;
  
    @Override
    @Transactional
    public void onApplicationEvent(ContextRefreshedEvent event) {
  
        if (alreadySetup)
            return;
        Privilege readPrivilege
          = createPrivilegeIfNotFound("READ_PRIVILEGE");
        Privilege writePrivilege
          = createPrivilegeIfNotFound("WRITE_PRIVILEGE");
  
        List<Privilege> adminPrivileges = Arrays.asList(
          readPrivilege, writePrivilege);        
        createRoleIfNotFound("ROLE_ADMIN", adminPrivileges);
        createRoleIfNotFound("ROLE_USER", Arrays.asList(readPrivilege));
 
        Role adminRole = roleRepository.findByName("ROLE_ADMIN");
        User user = new User();
        user.setFirstName("Test");
        user.setLastName("Test");
        user.setPassword(passwordEncoder.encode("test"));
        user.setEmail("test@test.com");
        user.setRoles(Arrays.asList(adminRole));
        user.setEnabled(true);
        userRepository.save(user);
 
        alreadySetup = true;
    }
 
    @Transactional
    private Privilege createPrivilegeIfNotFound(String name) {
  
        Privilege privilege = privilegeRepository.findByName(name);
        if (privilege == null) {
            privilege = new Privilege(name);
            privilegeRepository.save(privilege);
        }
        return privilege;
    }
 
    @Transactional
    private Role createRoleIfNotFound(
      String name, Collection<Privilege> privileges) {
  
        Role role = roleRepository.findByName(name);
        if (role == null) {
            role = new Role(name);
            role.setPrivileges(privileges);
            roleRepository.save(role);
        }
        return role;
    }
}

那么,在这个简单的设置代码中发生了什么?没有复杂的:

  • 我们正在创建这些权限
  • 我们正在创建角色并为其分配权限
  • 我们正在创建一个用户并为其分配一个角色

请注意我们如何使用alreadySetup标志来确定安装是否需要运行。这仅仅是因为,取决于您在应用程序中配置了多少上下文 - ContextRefreshedEvent可能会被多次触发。我们只希望安装程序执行一次。

这里有两个简短的注释 - 首先是术语。我们在这里使用权限 - 角色条款,但在Spring中,它们略有不同。在春季,我们的特权被称为角色,也被称为(授权)权力 - 这有点混乱。当然不是一个问题,但绝对值得注意。

其次 - 这些Spring角色(我们的特权)需要一个前缀 ; 默认情况下,该前缀是“ROLE”,但可以更改。我们在这里并没有使用这个前缀,只是为了保持简单,但请记住,如果您没有明确地改变它,那就是必需的。

4.自定义UserDetailsS​​ervice

现在让我们来看看认证过程。

我们将看到如何在我们自定义的UserDetailsS​​ervice中检索用户,以及如何从用户分配的角色和权限映射正确的权限集:

@Service("userDetailsService")
@Transactional
public class MyUserDetailsService implements UserDetailsService {
 
    @Autowired
    private UserRepository userRepository;
  
    @Autowired
    private IUserService service;
  
    @Autowired
    private MessageSource messages;
  
    @Autowired
    private RoleRepository roleRepository;
 
    @Override
    public UserDetails loadUserByUsername(String email)
      throws UsernameNotFoundException {
  
        User user = userRepository.findByEmail(email);
        if (user == null) {
            return new org.springframework.security.core.userdetails.User(
              " ", " ", true, true, true, true, 
              getAuthorities(Arrays.asList(
                roleRepository.findByName("ROLE_USER"))));
        }
 
        return new org.springframework.security.core.userdetails.User(
          user.getEmail(), user.getPassword(), user.isEnabled(), true, true, 
          true, getAuthorities(user.getRoles()));
    }
 
    private Collection<? extends GrantedAuthority> getAuthorities(
      Collection<Role> roles) {
  
        return getGrantedAuthorities(getPrivileges(roles));
    }
 
    private List<String> getPrivileges(Collection<Role> roles) {
  
        List<String> privileges = new ArrayList<>();
        List<Privilege> collection = new ArrayList<>();
        for (Role role : roles) {
            collection.addAll(role.getPrivileges());
        }
        for (Privilege item : collection) {
            privileges.add(item.getName());
        }
        return privileges;
    }
 
    private List<GrantedAuthority> getGrantedAuthorities(List<String> privileges) {
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (String privilege : privileges) {
            authorities.add(new SimpleGrantedAuthority(privilege));
        }
        return authorities;
    }
}

这里有趣的事情是特权(和角色)如何映射到GrantedAuthority实体。

这种映射使整个安全配置具有高度的灵活性和强大功能 - 您可以根据需要将角色和权限混合匹配,最终将它们正确映射到权限并返回到框架。

5. 用户注册

最后 - 让我们来看看注册一个新用户。

我们已经看到安装程序如何创建用户并为其分配角色(和特权) - 现在让我们来看看在注册新用户期间如何完成此操作:
 

@Override
public User registerNewUserAccount(UserDto accountDto) throws EmailExistsException {
  
    if (emailExist(accountDto.getEmail())) {
        throw new EmailExistsException
          ("There is an account with that email adress: " + accountDto.getEmail());
    }
    User user = new User();
 
    user.setFirstName(accountDto.getFirstName());
    user.setLastName(accountDto.getLastName());
    user.setPassword(passwordEncoder.encode(accountDto.getPassword()));
    user.setEmail(accountDto.getEmail());
 
    user.setRoles(Arrays.asList(roleRepository.findByName("ROLE_USER")));
    return repository.save(user);
}

在这个简单的实现中,我们假设一个标准用户正在被注册,所以ROLE_USER角色被分配给它。

当然,更复杂的逻辑可以通过相同的方式轻松实现 - 无论是通过多种硬编码注册方法,还是允许客户端发送正在注册的用户类型。

6,总结

在本教程中,我们演示了如何使用JPA为Spring boot Spring Security支持的系统实现角色和权限。

提示:源码下载