1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/DaHuYuXiXi-qhy

Клонировать/Скачать
readme.md 25 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 02.06.2025 22:50 c922e07

Проект "Фантастическая рыба"

![img.png](Логотип проекта.png)

Этот проект является внешним заказом для автора, и на данный момент проект завершен на 80%. В будущем будут продолжены работы по улучшению модуля поиска и модуля периодических задач. В настоящее время исходный код серверной части открыт.

Ссылка на главную страницу проекта:

https://7fish.fun/qhy/

Адрес интерфейса серверной части проекта не публикуется из соображений конфиденциальности


Архитектура проекта

![](Схема архитектуры системы.png)


Основные требования к бизнес-процессам

![](Схема требований.jpg)


Основные сложности проекта

Центр аутентификации

Центр аутентификации использует модель RABC для управления правами доступа, не применяя протокол OAuth2 для аутентификации (в будущем планируется переработка на универсальный сервер аутентификации OAuth2). ![img_1.png](Схема модели RABC.png)

Центр аутентификации использует Spring Security для управления правами доступа. Процесс аутентификации осуществляется с помощью двух фильтров, представленных на следующем рисунке:

![img_2.png](Схема работы центра аутентификации.png)

Что касается управления правами доступа, то для этого необходимо ознакомиться с двумя стратегиями управления правами доступа в Spring Security.


Введение в управление правами доступа в Spring SecurityС технической точки зрения, функции управления правами доступа в Spring Security делятся на две категории:

  • Управление правами доступа на основе фильтров (FilterSecurityInterceptor)
  • Управление правами доступа на основе AOP (MethodSecurityInterceptor)

Управление правами доступа на основе фильтров используется для перехвата HTTP-запросов, после чего происходит проверка прав доступа по адресу запроса.

Управление правами доступа на основе AOP используется для управления правами доступа на уровне методов. При вызове метода AOP перехватывает вызов и проверяет, обладает ли текущий пользователь необходимыми правами доступа. Если права есть, метод выполняется, в противном случае вызов запрещается.

В данном проекте модуль центра аутентификации использует FilterSecurityInterceptor для перехвата HTTP-запросов и проверки прав доступа.

Самописный модуль управления правами доступа использует MethodSecurityInterceptor для проверки прав доступа на уровне методов.


Как модуль центра аутентификации использует FilterSecurityInterceptor для управления правами доступа

Основной схемой работы Spring Security является следующая: ![](Схема работы Spring Security.png)Схема работы FilterSecurityInterceptor: ![](Схема работы FilterSecurityInterceptor.png)

Класс FilterInvocationSecurityMetadataSource имеет по умолчанию реализацию в виде подкласса DefaultFilterInvocationSecurityMetadataSource, который хранит в себе отображение конфигурированных путей запросов и соответствующих им прав доступа:

        // Запросы, не требующие аутентификации администратора -- интерфейсы, доступные для вызова внешними микросервисами
        http.authorizeRequests().antMatchers("/authentication/**").permitAll();
        // Запросы администратора могут быть выполнены только суперадминистратором -- обычные администраторы не имеют права доступа к управлению
        http.authorizeRequests().antMatchers("/manager/**", "/operationLog/**").hasAnyRole(ManagerConstants.SUPER_MANAGER);
        // Все остальные запросы могут быть выполнены только обычными администраторами или суперадминистраторами
        http.authorizeRequests().antMatchers("/**").hasAnyRole(ManagerConstants.COMMON_MANAGER, ManagerConstants.SUPER_MANAGER);
public class DefaultFilterInvocationSecurityMetadataSource implements
        FilterInvocationSecurityMetadataSource {
    // Конкретные отображения сохраняются в следующем коллекционном объекте
    private final Map<RequestMatcher, Collection<ConfigAttribute>> requestMap;
    . . . .
}

Отношения между конфигурированными путями запросов и правами доступа, заданными с помощью WebSecurityConfigurerAdapter, обрабатываются подклассом ExpressionBasedFilterInvocationSecurityMetadataSource, после чего отображения сохраняются в коллекции requestMap.**Мы можем самостоятельно сохранять URL-адреса и необходимые для их доступа права в базе данных, что позволит реализовать динамическую проверку прав. Конкретно это можно сделать, реализовав интерфейс FilterInvocationSecurityMetadataSource и переопределив метод getAttribute, который будет по текущему URL-адресу запроса искать права в базе данных и возвращать их в виде объекта ConfigAttribute.**Это и есть основные принципы реализации проверки прав доступа на уровне HTTP-запросов с помощью FilterSecurityInterceptor в центре аутентификации.


Как следует проектировать универсальный плагин для управления правами доступа

Универсальный плагин для управления правами доступа обычно выполняет две основные функции:

  • Аутентификация
  • Управление правами доступа на уровне метода (или на уровне запроса, как показано в предыдущем разделе)

Давайте сначала рассмотрим, как можно использовать MethodSecurityInterceptor для управления правами доступа на уровне метода.

Как использовать MethodSecurityInterceptor для управления правами доступа на уровне метода

Процесс работы MethodSecurityInterceptor: ![](Схема работы MethodSecurityInterceptor для управления правами доступа.png)

Для включения функции управления правами доступа на уровне метода нам нужно использовать аннотацию @EnableGlobalMethodSecurity, которая добавляет в контейнер необходимые конфигурационные классы: ![](@EnableGlobalMethodSecurity добавляет конфигурационные классы.png)


Концептуальное проектирование метода управления правами доступа

![](Схема проектирования универсального плагина для управления правами доступа.png)

Усиление обычно осуществляется с помощью PointcutAdvisor, который состоит из Pointcut и Advice. Pointcut определяет, какие объекты и методы будут усилены, а MethodMatcher определяет, какие методы будут перехватываться.![](Схема Pointcut для управления правами доступа.png)Мы можем использовать SecurityMetaDataSource, чтобы контролировать, какие объекты и методы будут усилены с помощью MethodSecurityMetadataSourceAdvisor. В этом проекте для анализа методов и получения требуемых ими прав доступа используется пользовательский класс CustomMethodSecurityMetadataSource:

     /**
      * Если метод содержит аннотацию @RequestMapping, то имя метода используется как имя права доступа. </br>
      * Обратите внимание: не следует перехватывать контроллеры Swagger. </br>
      * Этот метод вызывается при инициализации бина как точки пересечения для фильтрации, и при выполнении метода
      * вызывается для получения прав доступа метода.
      */
     @Override
     public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
         if (AnnotationUtils.findAnnotation(method, RequestMapping.class) != null
                 && AnnotationUtils.findAnnotation(targetClass, Controller.class) != null
                 && AnnotationUtils.findAnnotation(method, ApiOperation.class) != null
                 // Если на контроллер добавлена эта аннотация, перехват не производится
                 && AnnotationUtils.findAnnotation(targetClass, IgnoreController.class) == null) {
             return List.of(new CustomConfigAttribute(method.getName()));
         }
         return null;
     }

Пользовательский класс CustomGlobalMethodSecurityConfiguration используется для замены стандартного класса автоматической конфигурации. Основная цель — добавление нашего CustomMethodSecurityMetadataSource и CustomVoter:

/**
 * Пропуск стандартного автоматического внедрения для GlobalMethodSecurityConfiguration, здесь требуется переопределение метода customMethodSecurityMetadataSource. </br>
 ``` * Регистрация отдельного MethodSecurityMetadataSource, аннотация загружается в конфигурацию, наследуемую от GlobalMethodSecurityConfiguration, что приводит к пропуску стандартной конфигурации GlobalMethodSecurityConfiguration. </br>
 * Вдохновлено: глубокое понимание Spring Security, глава 13.
 * @author Дядя Хаос
 * @create 2023/2/14 18:47
 */
@EnableGlobalMethodSecurity
@Configuration
public class CustomGlobalMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
```### Общие идеи проектирования аутентификации```- TokenLoginAuthFilter отвечает за перехват запросов, начинающихся с /login/**. Конкретный метод аутентификации определяется подклассом, по умолчанию используется UsernamePasswordLoginAuthFilter, который использует имя пользователя и пароль для аутентификации.
- AbstractTokenAuthenticationFilter перехватывает все оставшиеся запросы, кроме тех, которые начинаются с /noTokenAuth/**. Из заголовков запроса извлекается токен, который затем проверяется.
 - Процесс проверки реализован в методе verifyToken, который должен быть переопределен подклассом. По умолчанию этот метод переопределяется классом RpcTokenAuthenticationFilter, который использует userDetailsService для получения информации о текущем пользователе и AuthClient для запроса информации о правах доступа у центра аутентификации.

***

## Обработка распределенных транзакций в проекте

В данном проекте используются две основные схемы обработки распределенных транзакций:

- Распределенные транзакции с сильной или слабой согласованностью: решаются с использованием режима AT Seata. Основное применение  синхронное сохранение информации о новых пользователях в центре аутентификации.
![img.png](seata_обработка_распределенных_транзакций.png)
- Распределенные транзакции с конечной согласованностью: загрузка курсов (можно посмотреть исходный код микросервиса загрузки курсов).![](загрузка_курсов.png)

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

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

## Как создать собственную систему управления локальными файлами на основе SpringMVC

### Почему русскоязычные запросы возвращают  Yöntem 404?

Изначально файловый сервис был основан на протоколе S3 и использовал MinIO. Однако, поскольку клиент загружал архив с курсом, содержащий HTML-файлы курса, изображения курса (множество файлов), CSS и JS-файлы, а HTML-файлы использовали относительные пути для ссылок на ресурсы, было решено отказаться от использования MinIO. Вместо этого была создана простая локальная система управления статическими ресурсами с использованием возможностей SpringMVC.

По умолчанию SpringMVC использует SimpleUrlHandlerMapping в качестве HandlerMapping для статических ресурсов, а Handler для обработки запросов  ResourceHttpRequestHandler. Соответствующий HandlerAdaptor  HttpRequestHandlerAdapter.

Исправлено:
"Yöntem 404" на "404"

Также исправлено:
"Yöntem" на "404"По умолчанию эта цепочка обработки статических ресурсов работает корректно, но существует проблема с кодировкой пути запроса на русском языке. Это приводит к тому, что, несмотря на наличие файла, возвращается ошибка 404.
![img.png](AbtsractUrlHandlerMapping_первая_декодировка_пути.png)
![](декодировка_пути.png)
![img.png](по_умолчанию_используемая_кодировка.png)
Вторая декодировка пути:
![img.png](вторая_декодировка_пути.png)
![](кодировка_пути_основной_метод.png)Если декодированный путь снова закодировать, он снова станет некорректным. В данном случае проблема касается только кириллических символов, а не латинских.

#### Как решить проблему?

Для решения проблемы необходимо запретить PathResourceResolver от кодирования пути ресурса. Основная идея  создать собственный CustomPathResourceResolver и переопределить метод getResource:
```java
public class CustomPathResourceResolver extends PathResourceResolver {
   @Override
   protected Resource getResource(String resourcePath, Resource location) throws IOException {
       Resource resource = new FileSystemResource(location.getFile().getAbsolutePath() + File.separator + resourcePath);
       if (resource.isReadable()) {
           return resource;
       }
       return null;
   }
}

Создать собственный CustomFileResourceHttpRequestHandler для замены стандартного:

public class CustomFileResourceHttpRequestHandler extends ResourceHttpRequestHandler {
``````markdown
    @Override
    public void afterPropertiesSet() throws Exception {
        // Устанавливаем наш собственный ресурсный解析器
        setResourceResolvers(List.of(new CustomPathResourceResolver()));
        super.afterPropertiesSet();
        setUrlPathHelper(null);
    }
}

Создаем кастомный CustomFileHandlerMapping для замены стандартной реализации:

/**
 * Из-за проблемы двойного декодирования китайских символов в ResourceHttpRequestHandler, 
 * реализованного в SimpleUrlHandlerMapping, здесь создаем кастомный.
 * @author 大忽悠
 * @create 2023/2/28 10:15
 */
public class CustomFileHandlerMapping extends AbstractUrlHandlerMapping implements ApplicationContextAware{
    private final Map<String, Object> urlMap = new LinkedHashMap<>();
    private final CustomFileResourceHttpRequestHandler fileResourceHttpRequestHandler;
``````markdown
****

---

Если декодированный путь снова закодировать, он снова станет некорректным. В данном случае проблема касается только кириллических символов, а не латинских.

#### Как решить проблему?

Для решения проблемы необходимо запретить PathResourceResolver от кодирования пути ресурса. Основная идея  создать собственный CustomPathResourceResolver и переопределить метод getResource:
```java
public class CustomPathResourceResolver extends PathResourceResolver {
    @Override
    protected Resource getResource(String resourcePath, Resource location) throws IOException {
        Resource resource = new FileSystemResource(location.getFile().getAbsolutePath() + File.separator + resourcePath);
        if (resource.isReadable()) {
            return resource;
        }
        return null;
    }
}

Создать собственный CustomFileResourceHttpRequestHandler для замены стандартного:

public class CustomFileResourceHttpRequestHandler extends ResourceHttpRequestHandler {
``````markdown
    @Override
    public void afterPropertiesSet() throws Exception {
        // Устанавливаем наш собственный ресурсный解析器
        setResourceResolvers(List.of(new CustomPathResourceResolver()));
        super.afterPropertiesSet();
        setUrlPathHelper(null);
    }
}

Создаем кастомный CustomFileHandlerMapping для замены стандартной реализации:

/**
 * Из-за проблемы двойного декодирования китайских символов в ResourceHttpRequestHandler, 
 * реализованного в SimpleUrlHandlerMapping, здесь создаем кастомный.
 * @author Дай Юйхуа
 * @create 2023/2/28 10:15
 */
public class CustomFileHandlerMapping extends AbstractUrlHandlerMapping implements ApplicationContextAware{
    private final Map<String, Object> urlMap = new LinkedHashMap<>();
    private final CustomFileResourceHttpRequestHandler fileResourceHttpRequestHandler;
``````markdown
****# Обратите внимание

В этом проекте используется собственная разработка легковесного генератора кода Easy-Generator для создания таблиц и шаблонов кода для различных микросервисов. Включает использование общей инфраструктуры, предоставляемой легковесным генератором кода, таких как: единая обработка возвратных значений, глобальные исключения, усилители возвратных значений, конфигурация Swagger, автоматическая конфигурация модуля кэширования и т.д.

Поэтому для запуска этого проекта необходимо извлечь проект Easy-Generator и установить его локально.

Ссылка на репозиторий проекта Easy-Generator:

https://gitee.com/DaHuYuXiXi/easy-code

****

# Необходимые для решения требования- Реализация деградации для OpenFeign
- Внутренние вызовы OpenFeign, добавление заголовков запроса через интерцептор для проверки легальности внутренних вызовов, предотвращение нелегального доступа к внутренним SDK-интерфейсам
- Модуль управления логами -- необходимо собирать логи с сервера, обрабатывать их и отображать для проверки администратором
- Реализация общего модуля кэширования Redis
- Реализация общего модуля запросов ES
- Реализация модуля планировщика задач
- Хранение прав доступа в центре аутентификации должно быть в формате имя класса+имя метода, чтобы избежать ошибок авторизации из-за повторяющихся имен методов в разных классах. В CustomConfigAttribute также должны храниться имя класса+имя метода
- Реализация функции временного предложения на курсах с использованием пессимистического блокирования Redis или оптимистического блокирования

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/DaHuYuXiXi-qhy.git
git@api.gitlife.ru:oschina-mirror/DaHuYuXiXi-qhy.git
oschina-mirror
DaHuYuXiXi-qhy
DaHuYuXiXi-qhy
master