春季基于权限的访问控制

时间:2018-11-22 09:25:38

标签: java spring spring-security rbac

im试图通过基于角色的访问控制来保护我的端点。我已经实现了整个结构以及CustomUserDetailService,但是我不确定应该如何对这些点实施这些规则,我一直在寻找一些不错的基于注释的评估,例如@PreAuthorize(hasRole('role'))。我的结构看起来如下:

权限:

@Entity
public class Permission implements GrantedAuthority {

@Id
private Long id;

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

@ManyToMany(mappedBy = "permissions",  fetch = FetchType.LAZY)
private Collection<Role> roles;

@Override
public String getAuthority() {
    return name;
}

角色:

@Entity
public class Role implements GrantedAuthority {

@Id @Column(name="ID" )
private Long id;

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

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(
        name = "role_x_permission",
        joinColumns = @JoinColumn(
                name = "role_id"),
        inverseJoinColumns = @JoinColumn(
                name = "permission_id"))
private List<Permission> permissions;

@Override
public String getAuthority() {
    return name;
}

用户:

@Entity(name = "User")
@Table(name = "USERS")
@Data
public class User {

@Id
private Long id;

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

@Column(name="PASSWORD"   , nullable=false , unique=false)
private String password;

@ManyToMany( fetch = FetchType.LAZY)
@JoinTable(name = "USER_ROLES",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> roles;

现在我已经定义了CustomUserDetailsS​​ervice:

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

@Autowired
private UserRepository userRepository;


@Override
@Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User applicationUser = userRepository.findByUsername(username);
    if (applicationUser.getId() == null) {
        throw new UsernameNotFoundException(username);
    }
    return new org.springframework.security.core.userdetails.User(applicationUser.getLogin(), applicationUser.getPassword(),
            getAuthorities(applicationUser.getRoles()));
}

@Transactional
public Collection<? extends GrantedAuthority> getUserAuthorities(String username) {
    User user = userRepository.findByUsername(username);

    return getAuthorities(user.getRoles());
}

private Collection<? extends GrantedAuthority> getAuthorities(
        Collection<Role> roles) {

    return getGrantedAuthorities(getPermissions(roles));
}

private List<String> getPermissions(Collection<Role> roles) {

    List<String> permissions = new ArrayList<>();
    List<Permission> collection = new ArrayList<>();
    for (Role role : roles) {
        collection.addAll(role.getPermissions());
    }
    for (Permission item : collection) {
        permissions.add(item.getName());
    }
    return permissions;
}

private List<GrantedAuthority> getGrantedAuthorities(List<String> permissions) {
    List<GrantedAuthority> authorities = new ArrayList<>();
    for (String permission : permissions) {
        authorities.add(new SimpleGrantedAuthority(permission));
    }
    return authorities;
}
}

然后我想用@PreAuthorize注释我的终结点

@PostMapping("/doSomething")
@PreAuthorize("hasRole('doSomething')")
public SomeEntity createComment(@RequestBody SomeEntity something) {
   ...
}

我有一个角色为USER的用户,该角色无权访问doSomething,但是看来@PreAuthorize("hasRole('doSomething')")无效。我不确定我做错了什么,可以请你指出我的错误吗?

此外,由于即时消息使用RBAC,因此此hasRole极具误导性,因为访问权限是基于权限的,而不是基于角色的。

使用RBAC方法授权访问端点的正确方法是什么?

1 个答案:

答案 0 :(得分:0)

您应该使用hasAuthority('doSomething')而不是hasRole('doSomething')

角色只是带有前缀Role_的权限。

所以hasRole('XXX')hasAuthority('ROLE_XXX')