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

OSCHINA-MIRROR/wujishu-SpringCloud-OAuth2-SpringSecurity-Frame

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

2019.07.02

Среда: SpringBoot 2.1.0.RELEASE jdk1.8 SpringCloud Greenwich.SR1 consul для обнаружения и регистрации служб redis

Завершенные работы:

 1. Проверка целостности информации клиента до запроса
 2. Собственные исключения для возврата
 3. Обработчик недостаточной авторизации
 4. Обработчик истечения срока действия токена
 5. Собственные данные для возврата токена
 6. Определение прав доступа на основе URI запроса
 7. Собственные вход и выход
 8. Вход с использованием имени пользователя и пароля, а также вход по номеру телефона
 9. Интеграция Swagger с OAuth2 
 10. Обработка прав доступа в Feign

Недавно начал разработку проекта управления правами доступа с использованием SpringCloud. В результате был создан единый модуль безопасности SpringSecurity OAuth2.

  1. Среда: SpringBoot 2.1.0.RELEASE jdk1.8 SpringCloud Greenwich.SR1 consul для обнаружения и регистрации служб
  2. Разделение проекта:
    1. fp-commons jar
    2. fp-gateway网关
    3. fp-resource-manager用户信息管理资源服务器
    4. fp-authorization-server OAuth2认证服务器
    5. fp-resource-feign feign调用

Примечание: В разделе "Разделение проекта" есть несколько текстовых описаний на китайском языке, которые не были переведены. Они остаются без изменений в соответствии с правилами перевода.3. Проектная структура 1. Настройка网关 pom.xml: Введение зависимостей; добавление регистрации слушателя на стартовом уровне @EnableDiscoveryClient и внедрение DiscoveryClientRouteDefinitionLocator application.xml: spring-main-allow-bean-definition-overriding: true (позволяет перезаписывать бины с одинаковыми именами) spring.application.name: SpringCloud-consul-gateway (установка имени приложения обязательно) конфигурация consul в bootstrap.yml (загружается раньше, чем application) prefer-ip-address: true (использование ip для регистрации, некоторые сети могут иметь проблемы с использованием имени хоста для получения регистрации) 2. fp-commons jar公用 pom.xml: Введение зависимостей; добавление некоторых общих методов 3. fp-authorization-server (в git был добавлен упрощённый версия OAuth2 сервера аутентификации, только с генерацией токена, без защиты ресурсов и четырьмя способами получения токена (password, authorization_code, refresh_token, client_credentials)) Необходимо только настроить AuthorizationServerConfigurerAdapter сервер аутентификации и WebSecurityConfigurerAdapter конфигурацию SpringSecurity, два файла, чтобы реализовать генерацию токена. Если нет защищённых ресурсов, не нужно использовать ResourceServerConfigurerAdapter конфигурацию сервера ресурсов. AuthorizationServerConfigurerAdapter аутентификационный адаптер имеет три основных метода: 1. AuthorizationServerSecurityConfigurer: настройка безопасности конечной точки токена (Token Endpoint) (здесь я добавил фильтр для обработки запросов с полными данными о клиентах) 2. ClientDetailsServiceConfigurer: настройка деталей клиента, детали клиента инициализируются здесь (здесь я создал MyClientDetailsService, который переопределяет метод loadClientByClientId интерфейса ClientDetailsService) Данные клиентов управляются через интерфейс ClientDetailsService, по умолчанию есть два способа: InMemoryClientDetailsServiceBuilder: запись в память JdbcClientDetailsServiceBuilder: запись в базу данных 3. AuthorizationServerEndpointsConfigurer: настройка конечных точек авторизации (authorization) и токенов (token) и служб токенов (token services) (здесь я создал MyUserDetailsService, переопределив метод loadUserByUsername интерфейса UserDetailsService) Пользовательские данные управляются через интерфейс UserDetailsService, по умолчанию есть два способа управления этими данными: InMemoryUserDetailsManager JdbcUserDetailsManager (с имеющейся структурой таблиц, которая должна быть создана в базе данных) В зависимости от нашего проекта, мы можем хотеть настроить некоторые функции: 1. Проверка целостности данных клиента перед запросом Для запросов с неполными данными можно сразу вернуть ответ на фронтенд, без необходимости прохождения дальнейших проверок Создаем новый фильтр ClientDetailsAuthenticationFilter и добавляем его в цепочку фильтров безопасности 2. Пользовательская обработка исключений По умолчанию исключения имеют следующий формат: { "error": "invalid_grant", "error_description": "Неверные учетные данные" } Мы хотим иметь следующий формат: { "code":401, "msg":"msg" } Создаем MyOAuth2WebResponseExceptionTranslator, реализуя интерфейс WebResponseExceptionTranslator Переопределяем метод ResponseEntity translate(Exception e); исключения, возникающие при аутентификации, здесь перехватываются, и мы можем здесь упаковать наши исключения в единую форму. Как это реализовать, зависит от проекта, здесь я просто скопировал реализацию DefaultWebResponseExceptionTranslator Определяем свой класс исключения OAuth2Exception MyOAuth2Exception Определяем сериализатор для нашего исключения MyOAuth2ExceptionJacksonSerializer Добавьте определенные исключения обработки в конфигурацию AuthorizationServerEndpointsConfigurer для сервера авторизации. Результат: { "code": 400, "msg": "error="invalid_request", error_description="Отсутствует тип запроса"" }
3. Обработчик отказа доступа По умолчанию формат ответа на отказ доступа выглядит так: { "error": "access_denied", Yöntem: "Доступ запрещен" } Мы ожидаем следующий формат: { "code":401, "msg":"msg" } Создайте новый класс MyAccessDeniedHandler, реализующий интерфейс AccessDeniedHandler, для установки возвращаемой информации. Добавьте его в конфигурацию ResourceServerConfigurerAdapter. http.exceptionHandling().accessDeniedHandler(accessDeniedHandler); 4. Обработчик истечения токена

Исправленный текст:

         "error": "invalid_grant",
          "error_description": "Неверные учетные данные"
      }
      Мы хотим иметь следующий формат:
      {
          "code":401,
          "msg":"msg"
      }
   Создаем MyOAuth2WebResponseExceptionTranslator, реализуя интерфейс WebResponseExceptionTranslator
  Переопределяем метод ResponseEntity<OAuth2Exception> translate(Exception e); исключения, возникающие при аутентификации, здесь перехватываются, и мы можем здесь упаковать наши исключения в единую форму.  Как это реализовать, зависит от проекта, здесь я просто скопировал реализацию DefaultWebResponseExceptionTranslator
  Определяем свой класс исключения OAuth2Exception MyOAuth2Exception
  Определяем сериализатор для нашего исключения MyOAuth2ExceptionJacksonSerializer   Добавьте определенные исключения обработки в конфигурацию AuthorizationServerEndpointsConfigurer для сервера авторизации.
           Результат:
          {
              "code": 400,
              "msg": "error=\"invalid_request\", error_description=\"Отсутствует тип запроса\""
          }    
 3.  Обработчик отказа доступа
    По умолчанию формат ответа на отказ доступа выглядит так:
    {
        "error": "access_denied",
        "error_description": "Доступ запрещен"
    }
    Мы ожидаем следующий формат:
    {
       "code":401,
       "msg":"msg"
    }
    Создайте новый класс MyAccessDeniedHandler, реализующий интерфейс AccessDeniedHandler, для установки возвращаемой информации.
    Добавьте его в конфигурацию ResourceServerConfigurerAdapter.
    http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
    4.  Обработчик истечения токена          По умолчанию формат ответа на истечение токена выглядит так:
    {
        "error": "invalid_token",
        "error_description": "Недействительный токен доступа: 78df4214-8e10-46ae-a85b-a8f5247370a"
    }
    Мы ожидаем следующий формат:
    {
       "code": 403,
       "msg": "msg"
    }
    Создайте новый класс MyTokenExceptionEntryPoint, реализующий интерфейс AuthenticationEntryPoint.
    Вставьте его в конфигурацию ResourceServerConfigurerAdapter.
       @Override
       public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
           resources.authenticationEntryPoint(tokenExceptionEntryPoint); // обработчик истечения токена
           resources.resourceId("auth"); // устанавливает идентификатор ресурса, используя scope клиента для проверки наличия разрешений на ресурс
       }       
 5.  Пользовательский ответ с токеном
  По умолчанию формат ответа с токеном выглядит так:
         {
            "access_token": "1e93bc23-32c8-428f-a126-8206265e17b2",
            "token_type": "bearer",
            "refresh_token": "0f083e06-be1b-411f-98b0-72be8f1da8af",
            "expires_in": 3599,
            "scope": "auth api"
        }     
 Мы ожидаем пользовательский формат, например, добавив username:
       {
            "access_token": "1e93bc23-32c8-428f-a126-8206265e17b2",
            "token_type": "bearer",
            "refresh_token": "0f083e06-be1b-411f-98b0-72be8f1da8af",
            "expires_in": 3599,
            "scope": "auth api",
            "username": "username"
      }            
  Создайте пользовательский класс MyTokenEnhancer, реализующий интерфейс TokenEnhancer.
 Добавьте его в defaultTokenServices() аутентификационного сервиса.Необходимо учесть, что: если уже был сгенерирован токен без каких-либо настроек, необходимо удалить его из Redis, чтобы снова протестировать результат. В противном случае результат будет всегда неверным, так как токен не истекает и не будет перегенерирован.

(4) fp-resource-manager — сервер управления ресурсами pom.xml: зависимости OAuth2, Redis и т. д. Перенесите конфигурацию ресурсов из сервера аутентификации в проект ресурсов и внесите необходимые изменения:

  • Добавьте проверку состояния (health check);
  • Добавьте инжекцию Redis (важно, иначе не будет доступен токен для сравнения, и будет считаться, что токен недействителен; если пользовательские настройки UserDetails были настроены, убедитесь, что файл с настройками также был скопирован в проект ресурсов, иначе Redis не сможет десериализовать данные). Теперь можно использовать токен для защиты ресурсов.
  1. Ограничение доступа на основе URI запроса Обычно мы контролируем доступ с помощью аннотации @PreAuthorize("hasRole('ROLE_USER')") и настройки HttpSecurity для указания требуемых прав. Здесь мы контролируем доступ на основе URI запроса и можем использовать аннотации для управления доступом. Сначала создайте собственный менеджер доступа MySecurityAccessDecisionManager, наследуя интерфейс AccessDecisionManager и переопределяя метод decide, а также копируя два оставшихся метода из AbstractAccessDecisionManager (ключевые моменты для реализации управления доступом с помощью аннотаций).Права доступа пользователя уже настроены на сервере аутентификации.
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
    String requestUrl = ((FilterInvocation) object).getRequest().getMethod() + ((FilterInvocation) object).getRequest().getRequestURI();
    // System. out. println("requestUrl>>" + requestUrl);
    // Права доступа текущего пользователя
    Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
    // System. out. println("authorities=" + authorities);
    for (GrantedAuthority grantedAuthority : authorities) {
        if (grantedAuthority.getAuthority().equals(requestUrl)) {
            return;
        }
        if (grantedAuthority.getAuthority().equals("ROLE_ADMIN")) {
            return;
        }
    }
    throw new AccessDeniedException("Нет доступа");
}

Здесь проверяется соответствие для определения наличия или отсутствия доступа к ресурсу. В конфигурации ресурсного сервера перезаписывается проверка прав доступа, загружая нашу пользовательскую проверку прав доступа.

.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
    @Override
    public <O extends FilterSecurityInterceptor> O postProcess(O o) {
        o.setAccessDecisionManager(accessDecisionManager);
        return o;
    }
})

6.1. Оптимизация проверки прав доступа в пункте 6. Сначала добавляется уровень получения URI, затем проверяется, какие права доступа требуются для этого URI. Можно указать несколько прав доступа, которые должны выполняться одновременно. Затем проверка прав доступа выполняется через интерфейс FilterSecurityInterceptor.Создается пользовательский класс MyFilterInvocationSecurityMetadataSource, реализующий интерфейс FilterInvocationSecurityMetadataSource.

private AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
    Set<ConfigAttribute> set = new HashSet<>();
    String requestUrl = ((FilterInvocation) object).getRequest().getMethod() + ((FilterInvocation) object).getRequest().getRequestURI();
    System.out.println("requestUrl >> " + requestUrl);
    // Здесь можно получить данные для сравнения из базы данных, памяти или Redis. В данном случае данные закреплены, но в будущем будут оптимизированы.
    String url = "GET/auth/**";
    if (antPathMatcher.match(url, requestUrl)) {
        SecurityConfig securityConfig = new SecurityConfig("ROLE_ADMIN");
        set.add(securityConfig);
    }
    if (ObjectUtils.isEmpty(set)) {
        return SecurityConfig.createList("ROLE_LOGIN");
    }
    return set;
}

Изменяется оригинальный класс MySecurityAccessDecisionManager, чтобы вместо получения URL он получал необходимые права доступа. Остальные проверки остаются без изменений. Регистрируется MyFilterInvocationSecurityMetadataSource в перезаписанном методе.

.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
    @Override
    public <O extends FilterSecurityInterceptor> O postProcess(O o) {
        o.setAccessDecisionManager(accessDecisionManager);
        return o;
    }
})
  1. Пользовательская настройка входа и выхода Настройка выхода: Выход из системы делает токен недействительным.Для этого достаточно отправить запрос на ConsumerTokenServices (по умолчанию используется встроенный выход из системы), используя access_token. После запроса старый токен становится недействительным.
@DeleteMapping("/logout")
public ResponseVo logout(String accessToken) {
    if (consumerTokenServices.revokeToken(accessToken)) {
        return new ResponseVo(200, "Выход из системы выполнен успешно");
    } else {
        return new ResponseVo(500, "Ошибка при выходе из системы");
    }
}

Настройка входа: По умолчанию адрес запроса токена "oauth/token". Мы можем настроить его в AuthorizationServerEndpointsConfigurer. Также можно обернуть запрос в дополнительный слой, чтобы выполнить дополнительные действия перед запросом токена, например, проверку CAPTCHA.

Детали смотрите в файле TokenController.

7.1. Оптимизация входа - добавление CAPTCHA

Добавление CAPTCHA при входе. CAPTCHA действительна в течение минуты. После успешного входа или достижения максимального времени CAPTCHA становится недействительной. CAPTCHA сохраняется в Redis с ключом в формате "username_code" и устанавливается время жизни.

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

  1. Генерация кода с именем пользователя и сохранение его в Redis с установкой времени жизни.

  2. Проверка CAPTCHA перед генерацией токена.

  3. Другие способы реализации входа по имени пользователя и паролю, а также по номеру телефона

Вход по номеру телефона с использованием кода подтверждения.Создание класса MyAuthenticationToken, наследуемого от AbstractAuthenticationToken. Создание класса MyPhoneAuthenticationToken (для входа с использованием кода подтверждения), наследуемого от MyAuthenticationToken. Создание абстрактного класса MyAbstractUserDetailsAuthenticationProvider, реализующего AuthenticationProvider. Создание класса MyPhoneAuthenticationProvider (для входа с использованием кода подтверждения), наследуемого от MyAbstractUserDetailsAuthenticationProvider, где используется MyPhoneAuthenticationToken и вводятся параметры. Этот класс отвечает за проверку кода подтверждения. Здесь настраиваются различные сообщения об ошибках (обрабатываются в обработчике исключений `MyLoginAuthFailureHandler`). - Измените `MyUserDetailsService` на абстрактный класс, используя шаблон метода: `protected abstract AuthUser getUser(String var1);` для получения данных из различных источников. - Создайте класс `MyUsernameUserDetailsService`, наследуя от `MyUsernameUserDetailsService`, чтобы предоставлять данные для старого способа входа. - Создайте класс `MyPhoneUserDetailsService`, наследуя от `MyUsernameUserDetailsService`, чтобы предоставлять данные для нового способа входа по SMS-коду. - Создайте класс `MyLoginAuthSuccessHandler` для обработки успешного входа, используя этот метод для проверки информации о клиенте и возврата токена. - Создайте класс `MyPhoneLoginAuthenticationFilter` для фильтрации входа по SMS-коду, перехватывая URL входа и вводя данные в `MyPhoneAuthenticationToken`. - Измените `MySecurityOAuth2Config`, так как интерфейс `MyUserDetailsService` был изменен, и теперь он не может предоставлять данные для старого способа входа.Вместо этого используйте MyUsernameUserDetailsService для предоставления данных. - Основное изменение: измените конфигурацию MySecurityConfig, чтобы настроить два источника данных, настроить обработчик успешного входа, настроить конфигурацию и добавить MyPhoneLoginAuthenticationFilter в цепочку фильтров. - Способ входа по SMS-коду завершен, способ входа по имени пользователя и паролю можно настроить по аналогичному процессу. - 8 января: оптимизация формата возврата пользовательских исключений, добавление обработчика ошибок входа и возврат ошибок входа. - 9 января: интеграция Swagger с OAuth2 - В pom.xml добавьте зависимости Swagger. - В application добавьте конфигурацию Swagger. - Добавьте конфигурационный файл SwaggerConfig. - Есть два способа: закомментированный способ добавляет Authorization для отправки токена. - Второй способ: добавьте поле для ввода токена для проверки токена при входе. - Конфигурация Swagger для входа: настройте входные URL в application. - MySecurityResourceServerConfig: разрешите доступ. - Тестовый адрес: http://192.168.3.14:8001/manager/swagger-ui.html#/ - 10 января: обработка прав доступа в Feign - Создайте службу fp-resource-feign. - В pom.xml добавьте зависимости Feign. - Feign имеет интерфейс RequestInterceptor, для которого можно реализовать простую реализацию FeignConfig, добавляющую токен в запрос для авторизации при вызове служебных интерфейсов.

Комментарии ( 0 )

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

Введение

Основанные на SpringCloud-OAuth2-SpringSecurity-Redis некоторые пользовательские операции Развернуть Свернуть
Отмена

Обновления

Пока нет обновлений

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/wujishu-SpringCloud-OAuth2-SpringSecurity-Frame.git
git@api.gitlife.ru:oschina-mirror/wujishu-SpringCloud-OAuth2-SpringSecurity-Frame.git
oschina-mirror
wujishu-SpringCloud-OAuth2-SpringSecurity-Frame
wujishu-SpringCloud-OAuth2-SpringSecurity-Frame
master