Spring Security - Rollen und Privilegien

Spring Security - Rollen und Privilegien

1. Überblick

In diesem Artikel wird die Reihe "Registrierung bei Spring Security" mit einem Blick auf die ordnungsgemäße Implementierung vonRoles and Privileges fortgesetzt.

Weitere Lektüre:

Einführung in Spring Security Expressions

Einfache und praktische Anleitung zu Spring Security Expressions.

Read more

Einführung in die Spring-Methodensicherheit

Eine Anleitung zur Sicherheit auf Methodenebene mit dem Spring Security Framework.

Read more

Spring Security - Nach der Anmeldung zur vorherigen URL umleiten

Ein kurzes Beispiel für die Umleitung nach der Anmeldung in Spring Security

Read more

2. UserRole undPrivilege

Beginnen wir zunächst mit unseren Entitäten. Wir haben drei Hauptentitäten:

  • dieUser

  • Role - Dies repräsentiert die übergeordneten Rollen des Benutzers im System. Jede Rolle verfügt über eine Reihe von Berechtigungen auf niedriger Ebene

  • Privilege - repräsentiert ein detailliertes Privileg / eine Berechtigung auf niedriger Ebene im System

Hier sindthe user:

@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 roles;
}

Wie Sie sehen, enthält der Benutzer die Rollen, aber auch einige zusätzliche Details, die für einen ordnungsgemäßen Registrierungsmechanismus erforderlich sind.

Weiter - hier sindthe role:

@Entity
public class Role {

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

    private String name;
    @ManyToMany(mappedBy = "roles")
    private Collection users;

    @ManyToMany
    @JoinTable(
        name = "roles_privileges",
        joinColumns = @JoinColumn(
          name = "role_id", referencedColumnName = "id"),
        inverseJoinColumns = @JoinColumn(
          name = "privilege_id", referencedColumnName = "id"))
    private Collection privileges;
}

Und schließlichthe privilege:

@Entity
public class Privilege {

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

    private String name;

    @ManyToMany(mappedBy = "privileges")
    private Collection roles;
}

Wie Sie sehen, berücksichtigen wir sowohl die Rolle Benutzer <→ als auch die Rolle <→ Berechtigungsbeziehungenmany-to-many bidirectional.

3. Berechtigungen und Rollen einrichten

Als Nächstes konzentrieren wir uns auf die frühzeitige Einrichtung der Berechtigungen und Rollen im System.

Wir werden dies mit dem Start der Anwendung verknüpfen undApplicationListener aufContextRefreshedEvent verwenden, um unsere anfänglichen Daten beim Serverstart zu laden:

@Component
public class InitialDataLoader implements
  ApplicationListener {

    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 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("[email protected]");
        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 privileges) {

        Role role = roleRepository.findByName(name);
        if (role == null) {
            role = new Role(name);
            role.setPrivileges(privileges);
            roleRepository.save(role);
        }
        return role;
    }
}

Was passiert also während dieses einfachen Setup-Codes? Nichts kompliziertes:

  • Wir schaffen die Privilegien

  • Wir erstellen die Rollen und weisen ihnen die Berechtigungen zu

  • Wir erstellen einen Benutzer und weisen ihm eine Rolle zu

Beachten Sie, wie wir einalreadySetup-Flag fürdetermine if the setup needs to run or not verwenden. Dies liegt einfach daran, dass abhängig von der Anzahl der in Ihrer Anwendung konfigurierten Kontexte dieContextRefreshedEvent möglicherweise mehrmals ausgelöst werden. Und wir möchten, dass das Setup nur einmal ausgeführt wird.

Zwei kurze Notizen hier -first, about terminology. Wir verwenden hier diePrivilege – Role-Begriffe, aber im Frühjahr unterscheiden sich diese geringfügig. Im Frühjahr wird unser Privileg als Rolle und auch als (erteilte) Autorität bezeichnet - was etwas verwirrend ist. Für die Umsetzung natürlich kein Problem, aber auf jeden Fall erwähnenswert.

Second – these Spring Roles (our Privileges) need a prefix; Standardmäßig lautet dieses Präfix "ROLLE", es kann jedoch geändert werden. Wir verwenden dieses Präfix hier nicht, nur um die Dinge einfach zu halten, aber denken Sie daran, dass es erforderlich sein wird, wenn Sie es nicht explizit ändern.

4. BenutzerdefinierteUserDetailsService

Schauen wir uns jetzt den Authentifizierungsprozess an.

Wir werden sehen, wie der Benutzer innerhalb unserer benutzerdefiniertenUserDetailsService abgerufen wird und wie die richtigen Berechtigungen aus den vom Benutzer zugewiesenen Rollen und Berechtigungen zugeordnet werden:

@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 getAuthorities(
      Collection roles) {

        return getGrantedAuthorities(getPrivileges(roles));
    }

    private List getPrivileges(Collection roles) {

        List privileges = new ArrayList<>();
        List collection = new ArrayList<>();
        for (Role role : roles) {
            collection.addAll(role.getPrivileges());
        }
        for (Privilege item : collection) {
            privileges.add(item.getName());
        }
        return privileges;
    }

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

Das Interessante daran ist, wie die Berechtigungen (und Rollen) GrantedAuthority-Entitäten zugeordnet werden.

Durch diese Zuordnung wird die gesamte Sicherheitskonfigurationhighly flexible and powerful - Sie können Rollen und Berechtigungen so detailliert wie nötig mischen und zuordnen. Am Ende werden sie den Behörden korrekt zugeordnet und an das Framework zurückgegeben.

5. User Registrierung

Schauen wir uns zum Schluss die Registrierung für einen neuen Benutzer an.

Wir haben gesehen, wie das Setup den Benutzer erstellt und ihm Rollen (und Berechtigungen) zuweist. Schauen wir uns nun an, wie dies bei der Registrierung eines neuen Benutzers erfolgen muss:

@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);
}

In dieser einfachen Implementierung wird davon ausgegangen, dass ein Standardbenutzer registriert wird, sodass ihm die RolleROLE_USERzugewiesen wird.

Natürlich kann eine komplexere Logik auf dieselbe Weise einfach implementiert werden - entweder durch mehrere fest codierte Registrierungsmethoden oder indem der Client den Typ des zu registrierenden Benutzers senden kann.

6. Fazit

In diesem Lernprogramm wurde veranschaulicht, wie Rollen und Berechtigungen mit JPA für ein von Spring Security unterstütztes System implementiert werden.

Diefull implementation dieses Lernprogramms zur Registrierung bei Spring Security finden Sie inthe GitHub project - dies ist ein Maven-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein.