Рубрики
Без рубрики

Добавление Ролей и Привилегий В приложение Reddit

Простые роли и привилегии, добавленные в приложение, ограничение по расписанию для стандартных пользователей и базовый экран администратора.

Автор оригинала: baeldung.

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 Collection roles;

    ...
}

Обратите внимание, что отношения между пользователями и ролями являются гибкими “многие ко многим”.

Далее мы определим Роль и Привилегии сущности. Для получения полной информации об этой реализации ознакомьтесь с этой статьей о строительстве .

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

Обратите внимание, что Роли в системе:

  1. ROLE_USER : для обычных пользователей (роль по умолчанию) – у них есть ограничение на количество записей, которые они могут запланировать в день
  2. ROLE_SUPER_USER : нет ограничений по расписанию
  3. ROLE_ADMIN : дополнительные параметры администратора

5. Принципал

Далее, давайте интегрируем эти новые привилегии в нашу основную реализацию:

public class UserPrincipal implements UserDetails {
    ...

    @Override
    public Collection getAuthorities() {
        List authorities = 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 List getUsersList() {
    return service.getUsersList();
}

И реализация уровня обслуживания:

@Transactional
public List getUsersList() {
    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 List getRolesList() {
    return service.getRolesList();
}

И уровень обслуживания:

@Transactional
public List getRolesList() {
    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);
}

Наконец – простой интерфейс:

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 {
        Set privieleges = 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. Заключение

В этой новой части тематического исследования мы добавили в наше приложение несколько простых артефактов безопасности – роли и привилегии. С этой поддержкой мы создали две простые функции – ограничение по расписанию для стандартных пользователей и простой администратор для пользователей с правами администратора.