Keselamatan Musim Semi - Peranan dan Keistimewaan

1. Gambaran keseluruhan

Artikel ini meneruskan siri Pendaftaran dengan Spring Security dengan melihat cara melaksanakan Peranan dan Keistimewaan dengan betul .

2. Pengguna Peranan dan Privilege

Pertama, mari kita mulakan dengan entiti kita. Kami mempunyai tiga entiti utama:

  • yang pengguna
  • yang Peranan - ini mewakili peranan peringkat tinggi pengguna dalam sistem; setiap peranan akan mempunyai satu set hak istimewa tahap rendah
  • yang Privilege - mewakili peringkat rendah, keistimewaan berbutir / kuasa dalam sistem

Inilah pengguna :

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

Seperti yang anda lihat, pengguna mengandungi peranan, tetapi juga beberapa butiran tambahan yang diperlukan untuk mekanisme pendaftaran yang betul.

Seterusnya - inilah peranannya :

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

Dan akhirnya hak istimewa :

@Entity public class Privilege { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; @ManyToMany(mappedBy = "privileges") private Collection roles; }

Seperti yang anda lihat, kami mempertimbangkan kedua-dua Peranan Pengguna dan juga hubungan Hak Peranan yang mempunyai banyak arah .

3. Tetapkan Keistimewaan dan Peranan

Seterusnya - mari fokus untuk melakukan beberapa persediaan awal mengenai Keistimewaan dan Peranan dalam sistem.

Kami akan mengaitkannya dengan permulaan aplikasi dan kami akan menggunakan ApplicationListener on ContextRefreshedEvent untuk memuatkan data awal kami pada permulaan pelayan:

@Component public class SetupDataLoader 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 Privilege createPrivilegeIfNotFound(String name) { Privilege privilege = privilegeRepository.findByName(name); if (privilege == null) { privilege = new Privilege(name); privilegeRepository.save(privilege); } return privilege; } @Transactional 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; } }

Jadi, apa yang berlaku semasa kod persediaan mudah ini? Tidak ada yang rumit:

  • kami mencipta keistimewaan
  • kami mencipta peranan dan memberikan hak istimewa kepada mereka
  • kami membuat pengguna dan memberikan peranan kepadanya

Perhatikan bagaimana kita menggunakan bendera sudahSetup untuk menentukan apakah persediaan perlu dijalankan atau tidak . Ini hanya kerana, bergantung pada berapa banyak konteks yang telah anda konfigurasikan dalam aplikasi anda - ContextRefreshedEvent mungkin diaktifkan beberapa kali. Dan kami hanya mahu penyediaan dilaksanakan sekali.

Dua nota ringkas di sini - pertama, mengenai terminologi . Kami menggunakan istilah Privilege - Role di sini, tetapi pada musim bunga, ini sedikit berbeza. Pada musim bunga, Keistimewaan kami disebut sebagai Peran, dan juga sebagai (diberikan) wewenang - yang sedikit membingungkan. Bukan masalah untuk pelaksanaannya, tetapi pasti perlu diperhatikan.

Kedua - Peranan Musim Semi ini (Keistimewaan kami) memerlukan awalan ; secara lalai, awalan itu adalah "ROLE", tetapi boleh diubah. Kami tidak menggunakan awalan itu di sini, hanya untuk memastikan perkara-perkara mudah, tetapi perlu diingat bahawa jika anda tidak mengubahnya secara eksplisit, itu akan diperlukan.

4. Perkhidmatan UserDetails Custom

Sekarang - mari lihat proses pengesahan.

Kami akan melihat bagaimana mendapatkan pengguna dalam UserDetailsService tersuai kami , dan bagaimana memetakan set pihak berkuasa yang betul dari peranan dan keistimewaan yang telah diberikan oleh pengguna:

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

Perkara yang menarik untuk diikuti di sini ialah bagaimana Hak Istimewa (dan Peranan) dipetakan kepada entiti GrantedAuthority.

Pemetaan ini menjadikan keseluruhan konfigurasi keselamatan sangat fleksibel dan kuat - anda boleh mencampurkan dan memadankan peranan dan keistimewaan sebutir yang diperlukan, dan pada akhirnya, mereka akan dipetakan dengan betul kepada pihak berkuasa dan dikembalikan ke kerangka kerja.

5. Pendaftaran Pengguna

Akhirnya - mari kita lihat pendaftaran untuk pengguna baru.

Kami telah melihat bagaimana persediaan membuat Pengguna dan memberikan Peranan (dan Hak Istimewa) kepadanya - sekarang mari kita lihat bagaimana perkara ini perlu dilakukan semasa pendaftaran pengguna baru:

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

Dalam pelaksanaan sederhana ini, kami menganggap bahawa pengguna standard didaftarkan, jadi peranan ROLE_USER diberikan kepadanya.

Sudah tentu, logik yang lebih kompleks dapat dengan mudah dilaksanakan dengan cara yang sama - sama ada dengan mempunyai beberapa kaedah pendaftaran dengan kod keras, atau dengan membenarkan pelanggan menghantar jenis pengguna yang sedang didaftarkan.

6. Kesimpulannya

Dalam tutorial ini, kami menggambarkan cara melaksanakan Peranan dan Keistimewaan dengan JPA, untuk sistem Spring Security yang disokong.

The pelaksanaan penuh Pendaftaran ini dengan tutorial Spring Keselamatan boleh didapati dalam projek GitHub - ini adalah projek berasaskan Maven, jadi ia harus mudah untuk import dan berjalan kerana ia adalah.