1. Обзор
В этом выпуске мы введем простые роли и привилегии в наше приложение Reddit, чтобы затем иметь возможность делать некоторые интересные вещи , такие как ограничение количества сообщений, которые обычный пользователь может запланировать на Reddit ежедневно.
И поскольку у нас будет роль администратора – и неявно пользователь – администратор, – мы также добавим область управления администратором.
2. Сущности пользователей, ролей и привилегий
Во – первых, мы изменим сущность User – которую мы используем в нашей серии приложений Reddit – для добавления ролей:
@Entity public class User { ... @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "users_roles", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id")) private Collectionroles; ... }
Обратите внимание, что отношения между пользователями и ролями являются гибкими “многие ко многим”.
Далее мы определим Роль и Привилегии сущности. Для получения полной информации об этой реализации ознакомьтесь с этой статьей о строительстве .
3. Настройка
Затем мы запустим некоторые базовые настройки в начальной загрузке проекта, чтобы создать эти роли и привилегии:
private void createRoles() { Privilege adminReadPrivilege = createPrivilegeIfNotFound("ADMIN_READ_PRIVILEGE"); Privilege adminWritePrivilege = createPrivilegeIfNotFound("ADMIN_WRITE_PRIVILEGE"); Privilege postLimitedPrivilege = createPrivilegeIfNotFound("POST_LIMITED_PRIVILEGE"); Privilege postUnlimitedPrivilege = createPrivilegeIfNotFound("POST_UNLIMITED_PRIVILEGE"); createRoleIfNotFound("ROLE_ADMIN", Arrays.asList(adminReadPrivilege, adminWritePrivilege)); createRoleIfNotFound("ROLE_SUPER_USER", Arrays.asList(postUnlimitedPrivilege)); createRoleIfNotFound("ROLE_USER", Arrays.asList(postLimitedPrivilege)); }
И сделайте нашего тестового пользователя администратором:
private void createTestUser() { Role adminRole = roleRepository.findByName("ROLE_ADMIN"); Role superUserRole = roleRepository.findByName("ROLE_SUPER_USER"); ... userJohn.setRoles(Arrays.asList(adminRole, superUserRole)); }
4. Регистрация Стандартных Пользователей
Нам также нужно будет убедиться, что мы регистрируем стандартных пользователей с помощью реализации register New User() :
@Override public void registerNewUser(String username, String email, String password) { ... Role role = roleRepository.findByName("ROLE_USER"); user.setRoles(Arrays.asList(role)); }
Обратите внимание, что Роли в системе:
- ROLE_USER : для обычных пользователей (роль по умолчанию) – у них есть ограничение на количество записей, которые они могут запланировать в день
- ROLE_SUPER_USER : нет ограничений по расписанию
- ROLE_ADMIN : дополнительные параметры администратора
5. Принципал
Далее, давайте интегрируем эти новые привилегии в нашу основную реализацию:
public class UserPrincipal implements UserDetails { ... @Override public Collection extends GrantedAuthority> getAuthorities() { Listauthorities = new ArrayList (); for (Role role : user.getRoles()) { for (Privilege privilege : role.getPrivileges()) { authorities.add(new SimpleGrantedAuthority(privilege.getName())); } } return authorities; } }
6. Ограничение запланированных сообщений стандартными пользователями
Давайте теперь воспользуемся новыми ролями и привилегиями и ограничим обычных пользователей от планирования более, скажем, 3 новых статей в день – чтобы избежать спама Reddit.
6.1. Хранилище сообщений
Во – первых, мы добавим новую операцию в нашу реализацию PostRepository – для подсчета запланированных сообщений конкретным пользователем в определенный период времени:
public interface PostRepository extends JpaRepository{ ... Long countByUserAndSubmissionDateBetween(User user, Date start, Date end); }
5.2. Контроллер запланированной почты
Затем мы добавим простую проверку в оба метода schedule() и update Post() :
public class ScheduledPostRestController { private static final int LIMIT_SCHEDULED_POSTS_PER_DAY = 3; public Post schedule(HttpServletRequest request,...) throws ParseException { ... if (!checkIfCanSchedule(submissionDate, request)) { throw new InvalidDateException("Scheduling Date exceeds daily limit"); } ... } private boolean checkIfCanSchedule(Date date, HttpServletRequest request) { if (request.isUserInRole("POST_UNLIMITED_PRIVILEGE")) { return true; } Date start = DateUtils.truncate(date, Calendar.DATE); Date end = DateUtils.addDays(start, 1); long count = postReopsitory. countByUserAndSubmissionDateBetween(getCurrentUser(), start, end); return count < LIMIT_SCHEDULED_POSTS_PER_DAY; } }
Здесь происходит пара интересных вещей. Во – первых, обратите внимание, как мы вручную взаимодействуем с Spring Security и проверяем, есть ли у текущего пользователя, вошедшего в систему, привилегии или нет. Это не то, что вы делаете каждый день, но когда вам приходится это делать, API очень полезен.
Как логика в настоящее время стоит – если у пользователя есть POST_UNLIMITED_PRIVILEGE – они могут – удивлять – планировать, сколько бы они ни выбрали.
Однако, если у них нет такой привилегии, они смогут ставить в очередь максимум 3 сообщения в день.
7. Страница Пользователей Администратора
Далее – теперь, когда у нас есть четкое разделение пользователей, основанное на их роли, – давайте реализуем очень простое управление пользователями для администратора нашего небольшого приложения Reddit.
7.1. Отображение Всех Пользователей
Во-первых, давайте создадим базовую страницу со списком всех пользователей в системе:
Здесь API для перечисления всех пользователей:
@PreAuthorize("hasRole('ADMIN_READ_PRIVILEGE')") @RequestMapping(value="/admin/users", method = RequestMethod.GET) @ResponseBody public ListgetUsersList() { return service.getUsersList(); }
И реализация уровня обслуживания:
@Transactional public ListgetUsersList() { return userRepository.findAll(); }
Затем простой интерфейс:
Username | Roles | Actions |
---|
7.2. Изменение роли Пользователя
Далее, немного простой логики для управления ролями этих пользователей; давайте начнем с контроллера:
@PreAuthorize("hasRole('USER_WRITE_PRIVILEGE')") @RequestMapping(value = "/user/{id}", method = RequestMethod.PUT) @ResponseStatus(HttpStatus.OK) public void modifyUserRoles( @PathVariable("id") Long id, @RequestParam(value = "roleIds") String roleIds) { service.modifyUserRoles(id, roleIds); } @PreAuthorize("hasRole('USER_READ_PRIVILEGE')") @RequestMapping(value = "/admin/roles", method = RequestMethod.GET) @ResponseBody public ListgetRolesList() { return service.getRolesList(); }
И уровень обслуживания:
@Transactional public ListgetRolesList() { return roleRepository.findAll(); } @Transactional public void modifyUserRoles(Long userId, String ids) { List roleIds = new ArrayList (); String[] arr = ids.split(","); for (String str : arr) { roleIds.add(Long.parseLong(str)); } List roles = roleRepository.findAll(roleIds); User user = userRepository.findOne(userId); user.setRoles(roles); userRepository.save(user); }
Наконец – простой интерфейс:
Modify User Roles
8. Конфигурация безопасности
Наконец, нам нужно изменить конфигурацию безопасности, чтобы перенаправить пользователей-администраторов на эту новую отдельную страницу в системе:
@Autowired private AuthenticationSuccessHandler successHandler; @Override protected void configure(HttpSecurity http) throws Exception { http. ... .authorizeRequests() .antMatchers("/adminHome","/users").hasAuthority("ADMIN_READ_PRIVILEGE") ... .formLogin().successHandler(successHandler) }
Мы используем пользовательский обработчик успеха аутентификации, чтобы решить, где пользователь приземляется после входа в систему :
@Component public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler { @Override public void onAuthenticationSuccess( HttpServletRequest request, HttpServletResponse response, Authentication auth) throws IOException, ServletException { Setprivieleges = AuthorityUtils.authorityListToSet(auth.getAuthorities()); if (privieleges.contains("ADMIN_READ_PRIVILEGE")) { response.sendRedirect("adminHome"); } else { response.sendRedirect("home"); } } }
И чрезвычайно простая домашняя страница администратора adminHome.html :
Welcome, Bob
Display Users List
9. Заключение
В этой новой части тематического исследования мы добавили в наше приложение несколько простых артефактов безопасности – роли и привилегии. С этой поддержкой мы создали две простые функции – ограничение по расписанию для стандартных пользователей и простой администратор для пользователей с правами администратора.