Динамическая защита Spring c RBA C с ролями в рамках - PullRequest
0 голосов
/ 04 мая 2020

Предположим, что classi c Управление доступом на основе ролей (RBA C) , с ролями, такими как ROLE_USER, ROLE_ADMIN, et c., Как изначально поддерживается Spring Security. Теперь представьте, что у нас есть домен с указанными c ролями объекта домена.

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

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

    @Column(nullable = false, length = 50)
    private String firstName;

    @Column(nullable = false, length = 50)
    private String lastName;

    @Column(nullable = false, length = 20, unique = true)
    private String username;

    @Column(nullable = false, length = 100, unique = true)
    private String email;

    @Column(nullable = false)
    private String password;

    @Column(nullable = false)
    private boolean enabled;


    // Our classic authorities for RBAC
    @EqualsAndHashCode.Exclude
    @ToString.Exclude
    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinTable(name = "user_roles",
            joinColumns = @JoinColumn(name = "user_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id"))
    private Set<Role> roles = new HashSet<>();


    // Project membership with project specific role
    @OneToMany(mappedBy = "user")
    @EqualsAndHashCode.Exclude
    @ToString.Exclude
    private Set<ProjectMember> memberships = new HashSet<>();
}

Каждый пользователь может быть участником проекта с ролью безопасности в рамках проекта. ProjectMember представляет таблицу соединения между User и Project с дополнительным атрибутом authority / role.

@Entity
@Table(name = "project_members")
@AllArgsConstructor
@NoArgsConstructor
@Data
@IdClass(ProjectMember.ProjectMemberId.class)
public class ProjectMember {

    @Id
    @ManyToOne
    @JoinColumn(name = "project_id")
    private Project project;

    @Id
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;

    // Member/User's authority WITHIN the associated board
    @ManyToOne(optional = false)
    @JoinColumn(name = "authority_id", referencedColumnName = "id")
    private Authority authority;

    @Embeddable
    @AllArgsConstructor
    @NoArgsConstructor
    @Data
    public static class ProjectMemberId implements Serializable {

        protected Long project;
        protected Long user;
    }
}

Класс Authority похож на Role, просто на сущность JPA, содержащую id и name с соответствующими геттерами / сеттерами, где name хранится в виде строкового перечисления с именем RoleName или AuthorityName.

При создании реализации UserDetails вы установить GrantedAuthority соответственно роли безопасности. Для ролей с префиксом classi c ROLE_* проблем нет.

String[] userRoles = user.getRoles().stream()
                .map(Role::getName)
                .map(RoleName::toString)
                .toArray(String[]::new);

AuthorityUtils.createAuthorityList(userRoles);

Но как справиться с объектами домена, указанными c ролями, такими как они, описывающими членство пользователя / проекта? Должен ли я добавить их в список GrantedAuthority UserDetails? Мой первый подход состоял в том, чтобы работать с дополнительным префиксом, таким как ROLE_PROJECT_{PROJECT_ID}_*, и сохранять его как обычную строку. Использование будет: @PreAuthorize("hasRole('ROLE_PROJECT_' + #id + '_MAINTAINER')")

Это рекомендуемый подход? Или я должен написать собственное выражение безопасности для моего подкласса UserDetails 'с его дополнительными полями? Любые дополнительные подходы?

PS: я знаю, что мог бы использовать простой Список контроля доступа (ACL) , но даже я бы дал разрешение в этом списке через динамические c роли вместо имен пользователей для предотвращения сотен записей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...