Как реализовать динамическое управление разрешениями в последней версии Spring Security?
Как реализовать динамическое управление разрешениями в последней версии Spring Security?

1. Идеи развития разрешений

Давайте сначала поговорим об идее разработки разрешений. После того, как мы спроектируем разрешения RBAC, особенно на уровне кода, у нас есть две идеи реализации:

  1. Непосредственно в интерфейсе/Сервисе Добавьте аннотацию Разрешения к методу слоя.,Преимущество этого подхода в том, что его просто реализовать.,Но есть проблема в том, что Разрешения жестко запрограммированы.,Что нужно каждому методу Разрешения заложены в коде.,Если вы захотите изменить его позже через страницу управления, это невозможно из,Чтобы изменить определенный метод, вы можете только изменить код.
  2. Описать связь между запросом и Разрешенияиз через базу данных.,Для каждого запроса необходимо то, что существуют. Разрешения существуют. Все базы данных. Конфигурация хорошая.,Когда приходит запрос,Динамический запрос,Затем определите, удовлетворено ли Разрешения.,Преимущество этого подхода в том, что он более гибкий.,Когда необходимо изменить связь между интерфейсом и Разрешениями в будущем,Доступ к нему можно получить через страницу управления в несколько кликов.,Проблема решена,Нет необходимости изменять код,Брат Сон раньше vhr Это то, что делает Китай.

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

2. Конкретная практика

2.1 Обзор старых решений

В vhr Songge реализует динамические разрешения, переписывая два класса.

Первый класс — это тот, который собирает метаданные разрешений:

Язык кода:javascript
копировать
@Component
public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        //...
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

В методе getAttributes на основе URL-адреса текущего запроса (который можно извлечь из параметра Object), а затем на основе конфигурации в таблице разрешений он анализирует, какие разрешения требуются текущему запросу, и возвращает их.

Кроме того, я также переписал устройство принятия решений. На самом деле, устройство принятия решений не нужно переписывать, это зависит от ваших собственных потребностей. Если устройство принятия решений, поставляемое с Spring Security, не может удовлетворить ваши потребности, вы можете написать устройство принятия решений. сам:

Язык кода:javascript
копировать
@Component
public class CustomUrlDecisionManager implements AccessDecisionManager {
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        //...
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

Метод решения принимает решения. Первый параметр может определить, какие разрешения есть у текущего пользователя. Третий параметр — какие разрешения требуются текущему запросу. Если у текущего пользователя нет необходимых разрешений, укажите его напрямую. Возникает исключение AccessDeniedException.

Наконец, через постпроцессор компонента BeanPostProcessor эти два класса конфигурации помещаются в перехватчик FilterSecurityInterceptor Spring Security:

Язык кода:javascript
копировать
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                @Override
                public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                    object.setAccessDecisionManager(customUrlDecisionManager);
                    object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource);
                    return object;
                }
            })
            .and()
            //...
}

Общая логика такова. Полный приведенный выше код друзья могут найти на https://github.com/lenve/vhr.

2.2 Новое решение

Однако приведенный выше код нельзя использовать в последней версии Spring Security 6 не потому, что срок действия класса истек, а потому, что класс был удален! Какой класс удалили? Фильтр Безопасность Перехватчик.

FilterSecurityInterceptor Этот фильтр использовался для обработки разрешений, но в новой версии Spring Security 6 этот перехватчик был заменен на AuthorizationFilter.

Честно говоря, новая версия решения на самом деле более разумна. Традиционное решение, похоже, имеет много нюансов разделения передней и задней части. Теперь мы движемся к более чистому разделению передней и задней части.

Поскольку FilterSecurityInterceptor больше не используется в новой версии, решение старой версии, очевидно, не будет работать. Решение новой версии на самом деле проще.

Хотя старые и новые методы письма различны, основная идея одна и та же.

Давайте посмотрим на конфигурацию новой версии:

Язык кода:javascript
копировать
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests(register -> register.anyRequest().access((authentication, object) -> {
                //Указывает на запрос из URL Соответствует ли адрес адресу базы данных?
                boolean isMatch = false;
                //Получаем текущий запрос из URL адрес
                String requestURI = object.getRequest().getRequestURI();
                List<MenuWithRoleVO> menuWithRole = menuService.getMenuWithRole();
                for (MenuWithRoleVO m : menuWithRole) {
                    if (antPathMatcher.match(m.getUrl(), requestURI)) {
                        isMatch = true;
                        //Указывает, что запрос изадрес найден
                        //Это роль, которую требует текущий запрос
                        List<Role> roles = m.getRoles();
                        //Получаем текущую роль вошедшего в систему пользователя
                        Collection<? extends GrantedAuthority> authorities = authentication.get().getAuthorities();
                        for (GrantedAuthority authority : authorities) {
                            for (Role role : roles) {
                                if (authority.getAuthority().equals(role.getName())) {
                                    //Указывает, что у текущего вошедшего в систему пользователя есть необходимые роли для текущего запроса
                                    return new AuthorizationDecision(true);
                                }
                            }
                        }
                    }
                }
                if (!isMatch) {
                    //Объясняем запрос URL Нет совпадения между адресом и адресом базы данных.,Для такого рода запроса,Вам нужно только войти в систему, чтобы получить доступ к единой
                    if (authentication.get() instanceof AnonymousAuthenticationToken) {
                        return new AuthorizationDecision(false);
                    } else {
                        //Указывает, что пользователь прошел аутентификацию
                        return new AuthorizationDecision(true);
                    }
                }
                return new AuthorizationDecision(false);
            }))
            .formLogin(form -> 
            //...
            )
            .csrf(csrf -> 
            //...
            )
            .exceptionHandling(e -> 
            //...
            )
            .logout(logout ->
            //...
            );
    return http.build();
}

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

В обратном вызове метода доступа есть два параметра. Первый параметр — аутентификация. Очевидно, это объект пользователя, который успешно вошел в систему. Отсюда мы можем извлечь разрешения текущего пользователя.

Второй объект параметра на самом деле представляет собой RequestAuthorizationContext, из которого можно извлечь текущий объект запроса HttpServletRequest, а затем извлечь URL-адрес текущего запроса, а затем на основе информации в таблице разрешений можно определить, какие разрешения требуется текущий запрос, а затем в сочетании с аутентификацией. Просто сравните разрешения текущего пользователя, извлеченные из .

Если у текущего вошедшего в систему пользователя есть разрешения, необходимые для запроса, верните new AuthorizationDecision(true);,В противном случае верните new AuthorizationDecision(false); Вот и все.

Фактически, независимо от того, какой фреймворк, если вы можете освоить 70% одной версии, независимо от того, как она будет обновляться в будущем, вы сможете быстро приступить к работе!

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose