Proljetna sigurnost - uloge i privilegije

1. Pregled

Ovaj se članak nastavlja na seriju Registracija s proljetnom zaštitom s pogledom na to kako pravilno implementirati Uloge i povlastice.

2. KorisnikUloga i Privilegija

Prvo, krenimo od naših entiteta. Imamo tri glavna entiteta:

  • the Korisnik
  • the Uloga - ovo predstavlja ulogu korisnika na visokoj razini u sustavu; svaka uloga imat će niz privilegija na niskoj razini
  • the Privilegija - predstavlja nisku razinu, granuliranu privilegiju / autoritet u sustavu

Evo korisnik:

@Entity javna klasa Korisnik {@Id @GeneratedValue (strategy = GenerationType.AUTO) private Long id; private String firstName; private String lastName; privatni String e-mail; privatna lozinka za niz; omogućeno privatno logičko polje; privatni logički tokenExpired; @ManyToMany @JoinTable (name = "users_roles", joinColumns = @JoinColumn (name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn (name = "role_id", referencedColumnName = "id")) ; }

Kao što vidite, korisnik sadrži uloge, ali i nekoliko dodatnih detalja koji su potrebni za pravilan mehanizam registracije.

Dalje - evo Uloga:

@ Uloga javne klase entiteta {@Id @GeneratedValue (Strategy = GenerationType.AUTO) private Long id; privatni naziv niza; @ManyToMany (mappedBy = "role") korisnici privatne kolekcije; @ManyToMany @JoinTable (name = "privilegiji_uloga", joinColumns = @JoinColumn (name = "role_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn (name = "privilege_id", referencedColumnName = "id")) ; }

I konačno privilegija:

@Entity javna klasa Privilege {@Id @GeneratedValue (strategija = GenerationType.AUTO) private Long id; privatni naziv niza; @ManyToMany (mappedBy = "privileges") uloge privatne kolekcije; }

Kao što vidite, razmatramo i korisničku ulogu kao i odnose privilegiranih uloga dvosmjerno.

3. Postavljanje privilegija i uloga

Dalje - usredotočimo se na rano postavljanje privilegija i uloga u sustavu.

To ćemo povezati s pokretanjem aplikacije i koristit ćemo ApplicationListener na ContextRefreshedEvent za učitavanje naših početnih podataka na startu poslužitelja:

@Component javna klasa SetupDataLoader implementira ApplicationListener {boolean alreadySetup = false; @Autowired privatni UserRepository userRepository; @Autowired privatni RoleRepository roleRepository; @Autowired privatni PrivilegeRepository privilegeRepository; @Autowired privatni PasswordEncoder passwordEncoder; @Override @Transactional javna praznina naApplicationEvent (ContextRefreshedEvent event) {if (alreadySetup) return; Privilegije readPrivilege = createPrivilegeIfNotFound ("READ_PRIVILEGE"); Privilege writePrivilege = createPrivilegeIfNotFound ("WRITE_PRIVILEGE"); Popis adminPrivileges = Arrays.asList (readPrivilege, writePrivilege); createRoleIfNotFound ("ROLE_ADMIN", adminPrivileges); createRoleIfNotFound ("ROLE_USER", Arrays.asList (readPrivilege)); Uloga adminRole = roleRepository.findByName ("ROLE_ADMIN"); Korisnik korisnik = novi korisnik (); user.setFirstName ("Test"); user.setLastName ("Test"); user.setPassword (passwordEncoder.encode ("test")); user.setEmail ("[e-pošta zaštićena]"); user.setRoles (Arrays.asList (adminRole)); user.setEnabled (true); userRepository.save (korisnik); alreadySetup = true; } @Transactional Privilege createPrivilegeIfNotFound (ime niza) {Privilege privilegija = privilegeRepository.findByName (ime); if (privilegija == null) {privilegija = nova privilegija (ime); privilegeRepository.save (privilegija); } povrati privilegiju; } @Transactional Role createRoleIfNotFound (Ime niza, privilegije zbirke) {Role role = roleRepository.findByName (name); if (role == null) {role = nova uloga (ime); role.setPrivileges (privilegije); roleRepository.save (uloga); } povratna uloga; }}

Pa, što se događa tijekom ovog jednostavnog postavnog koda? Ništa komplicirano:

  • mi stvaramo privilegije
  • mi stvaramo uloge i dodjeljujemo im privilegije
  • stvaramo korisnika i dodjeljujemo mu ulogu

Primijetite kako koristimo većSetup zastava do odredite treba li instalaciju pokrenuti ili ne. To je jednostavno zato što, ovisno o tome koliko ste konteksta konfigurirali u svojoj aplikaciji, ContextRefreshedEvent može se više puta otpustiti. A mi želimo da se postavljanje izvrši samo jednom.

Dvije kratke bilješke ovdje - prvo, o terminologiji. Koristimo Privilegij - uloga izrazi ovdje, ali u proljeće su to malo drugačiji. U proljeće se naša privilegija naziva Uloga, a također i kao (odobreno) tijelo - što je pomalo zbunjujuće. Nije problem u provedbi, naravno, ali svakako vrijedi napomenuti.

Drugo - ovim Proljetnim ulogama (naše Privilegije) potreban je prefiks; prema zadanim postavkama taj je prefiks "ROLE", ali ga je moguće promijeniti. Ovdje ne koristimo taj prefiks, samo da bismo pojednostavili stvari, ali imajte na umu da će to biti potrebno ako ga izričito ne mijenjate.

4. Prilagođeno UserDetailsService

Sada - provjerimo postupak provjere autentičnosti.

Vidjet ćemo kako dohvatiti korisnika u okviru našeg običaja UserDetailsServicei kako mapirati pravi skup ovlasti iz uloga i privilegija koje je korisnik dodijelio:

@Service ("userDetailsService") @Transactional javna klasa MyUserDetailsService implementira UserDetailsService {@Autowired private UserRepository userRepository; @Autowired privatna usluga IUserService; @Autowired privatne poruke MessageSource; @Autowired privatni RoleRepository roleRepository; @Override public UserDetails loadUserByUsername (String email) baca 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")) ))); } vrati novi org.springframework.security.core.userdetails.User (user.getEmail (), user.getPassword (), user.isEnabled (), true, true, true, getAuthorities (user.getRoles ())); } privatna kolekcija getAuthorities (uloge kolekcije) {return getGrantedAuthorities (getPrivileges (uloge)); } privatni popis getPrivileges (uloge zbirke) {privilegija popisa = novi ArrayList (); Zbirka popisa = novi ArrayList (); za (Uloga uloge: uloge) {collection.addAll (role.getPrivileges ()); } za (Privilege item: collection) {privileges.add (item.getName ()); } vratiti povlastice; } privatni popis getGrantedAuthorities (privilegije popisa) {Ovlasti popisa = novi ArrayList (); za (Privilegije niza: privilegije) {powers.add (novo SimpleGrantedAuthority (privilegija)); } vlasti za povratak; }}

Zanimljivo je što se ovdje slijedi kako se Privilegiji (i uloge) preslikavaju na entitete GrantedAuthority.

Ovo mapiranje čini cjelokupnu sigurnosnu konfiguraciju vrlo fleksibilan i moćan - možete miješati i podudarati uloge i privilegije po potrebi, i na kraju će se pravilno mapirati u vlasti i vratiti natrag u okvir.

5. Korisnik Registracija

Na kraju - pogledajmo registraciju za novog korisnika.

Vidjeli smo kako postavljanje ide prema stvaranju korisnika i dodjeljuje mu uloge (i privilegije) - pogledajmo sada kako to treba učiniti tijekom registracije novog korisnika:

@Override public User registerNewUserAccount (UserDto accountDto) baca EmailExistsException {if (emailExist (accountDto.getEmail ())) {throw new EmailExistsException ("Postoji račun s tom adresom e-pošte:" + accountDto.getEmail ()); } Korisnik korisnik = novi korisnik (); 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"))); vratiti spremište.save (korisnik); }

U ovoj jednostavnoj implementaciji pretpostavljamo da je standardni korisnik registriran, pa ROLE_USER dodijeljena mu je uloga.

Naravno, složenija logika može se lako implementirati na isti način - bilo da ima višestruko kodirane metode registracije, ili dopuštajući klijentu da pošalje vrstu korisnika koji je registriran.

6. Zaključak

U ovom smo uputstvu ilustrirali kako implementirati uloge i privilegije s JPA-om za sustav poduprt s Spring Security.

The puna provedba ovog vodiča za registraciju u Spring Security možete pronaći u projektu GitHub - ovo je projekt zasnovan na Mavenu, pa bi ga trebalo lako uvesti i pokrenuti kakav jest.