Реализовать сервер авторизации oauth2 и собственный метод аутентификации, используя «устаревший» spring-security-oauth2.

Java

предисловие

Недавно был проведен рефакторинг проекта, содержащегомного клиентов, такие как браузеры, мобильные приложения, мобильные общедоступные учетные записи WeChat и т. д., по замыслу босса, ониУ каждого своя фоновая служба, но некоторые общественные услуги извлекаются, чтобы сделатьосновные услугиОн совместно используется фоновыми службами каждого клиента, а также используетсяspring cloud + consul + feignреализоватьОбнаружение регистрации службы и вызовы между службами.

Босс решил напрямую изменить предыдущее управление безопасностью и полномочиями и решил использовать метод oauth2 для управления контролем полномочий между службами, что, вероятно, означает, что каждая клиентская служба переходит кВернуть access_token после запроса базовой службы на успешную аутентификацию., и приносить этот access_token каждый раз при вызове интерфейса службы в будущем, а базовая служба получает информацию аутентификации в соответствии с access_token.

В этой статье будет представлена ​​реализация сервера авторизации oauth2 с использованиемspring-security-oauth2Реализовать сервер авторизации oauth2, который будет отвечать заПолучить информацию об аутентификации клиента,Выполнить аутентификациюпроцесс, авторизация после успехаВыпустить access_token. Что касается того, как клиент вызывает и получает access_token, это не входит в задачу данной статьи, я напишу об этом в следующей статье~

Введение в oauth2

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

Во-первых, полный процесс oauth2 должен включать следующие части:

  • Сервер ресурсов, сервер ресурсов, третья сторона, например информация пользователя WeChat, защищенная WeChat.
  • Владелец ресурса, владелец ресурса, например, пользователи WeChat
  • Сервер авторизации, сервер авторизации, третья сторона, такая как WeChat, после согласия пользователя авторизовать нас, открытая платформа, предоставляющая нам доступ к серверам ресурсов
  • Клиент, клиент, такой как разработчики WeChat, пользователи WeChat, после входа в WeChat, разработчики WeChat «побуждают» щелкнуть сервер авторизации запроса, чтобы авторизовать наших разработчиков, а разработчики WeChat могут перейти на сервер ресурсов для получения некоторой информации. о пользователях.

oauth2 определяет четыре метода авторизации:

  • Обычно используется режим кода авторизации (код авторизации). Например, пользователи WeChat сначала входят в WeChat, запрашивают страницу авторизации, а после согласия на авторизацию дают код разработчику WeChat. С помощью этого кода разработчик может затем получите access_token, а затем запросите сервер ресурсов для получения информации о пользователе.
  • Упрощенный режим (неявный)
  • Режим пароля (учетные данные владельца ресурса) обычно не используется в сторонней аутентификации. Например, пользователи WeChat напрямую вводят имя пользователя и пароль WeChat разработчику, а затем разработчик передает имя пользователя и пароль WeChat для получения разрешения. WeChat конечно нельзя, это делается, но так как мы звоним между сервисами в одной компании, то можно использовать этот паттерн.
  • Учетные данные клиента (учетные данные клиента) используются для некоторых служб проверки подлинности, не связанных с пользователями.

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

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

После введения предыстории и теории давайте начнем использовать поддержку oauth2, предоставляемую Spring, для достижения следующего:

Устарело на spring-security-oauth2

Как упоминалось выше, эта статья в основном посвящена части oauth2, то есть процессу реализации сервера авторизации Authorization Server. Сначала я отправился в знаменитыйspring securityчтобы увидеть, как он реализует сервер авторизации для oauth2, и получил безжалостный результат, последнийspring securityмодуль ужеНе предоставляет реализацию сервера авторизации.

фактическиspring security oauth2Модуль реализует сервер авторизации oauth2, но официальный сказалspring security oauth2Все модули устарели, мигрируйте наspring securityМодуль предоставляется, и руководство по миграции также предоставляется «интимно»

нажмите

Этот документ содержит руководство по переносу клиентов и серверов ресурсов OAuth 2.0 с Spring Security OAuth 2.x на Spring Security 5.2.x. Поскольку Spring Security не обеспечивает поддержку сервера авторизации, перенос сервера авторизации Spring Security OAuth выходит за рамки этого документ.

Общая идея заключается в том, что мы сделали spring-security-oauth2 устаревшим, который обеспечивает реализацию сервера авторизации oauth2, но мы не предоставляем сервер авторизации oauth2 в новом решении.

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

Вы можете посмотреть историю сервера авторизации spring oauth2

stackoverflow.com/questions/5…

В заключении, нам нужно реализовать сервер авторизации oauth2, и временно нужно использовать устаревший модуль без других новых технологийspring-security-oauth, соответствующий код также будет иметьзачеркнутый

Конфигурация информации о клиенте

Клиент здесь — это концепция в oauth2, похожая на информацию об appid и appsecret в разработке WeChat.В наших потребностях это относится к фоновым службам, соответствующим различным пользовательским клиентам, таким как сеть, мобильное приложение и мобильная учетная запись WeChat. После настройки их информации соответствующая им фоновая служба может разрешить серверу запрашивать информацию авторизации, такую ​​как access_token, с их информацией.

spring-security-oauthМодули обеспечивают удобную аннотацию@EnableAuthorizationServerи адаптерAuthorizationServerConfigurerAdapterДавайте настроим Сервер авторизации с помощью следующего кода:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients
            .inMemory()
            // 配置web客户端
            .withClient("web")
            .secret(passwordEncoder.encode("**"))
            // web端通过password形式认证
            .authorizedGrantTypes("password")
            .and()
            // 配置微信公众号客户端
            .withClient("wx_subscription")
            .secret(passwordEncoder.encode("**"))
            // 微信公众号客户端需要通过微信提供的code进行认证,所有自定义了认证方式,需要后面的支持
            .authorizedGrantTypes("wx_subscription")
    }
}

Поскольку нет необходимости в динамической модификации, она настраивается непосредственно в памяти.

Через приведенную выше конфигурацию пружина предоставила нам следующие вещи:

  • Конечная точка авторизации аутентификации oauth2
  • Генерация, выпуск и хранение токенов после аутентификации

Но как весна основана наpassword,wx_subscriptionКод для аутентификации необходимо настроить в следующем диспетчере аутентификации.

Конфигурация диспетчера аутентификации

закончить первымpasswordФорма аутентификации, все еще в классе адаптера выше, настраиваетAuthorizationServerEndpointsConfigurerКонечная точка диспетчера аутентификации для этого диспетчера аутентификацииauthenticationManagerс нашим обычнымspring securityДиспетчер аутентификации, настроенный в это время, может быть одним и тем же.

public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        // 设置这个开启password授权方式,通过spring security暴露
        endpoints.authenticationManager(authenticationManager);
    }
}

о вspring securtiyвыставляется в конфигеAuthenticationManager:

@Configuration
@EnableWebSecurity
class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    //.. 省略用户名密码等认证配置
    
    /**
         * 暴露认证管理器给授权服务器使用
         * @return
         * @throws Exception
         */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

контрольная работа

После приведенной выше конфигурации, если нет проблем с конфигурацией аутентификации, такой как ваше имя пользователя и пароль, вы можете запустить проект для тестирования следующего oauth2.passwordЯвляется ли метод аутентификации простым в использовании?

@Test
void testWebClient() {
    ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
    resource.setClientId("web");
    resource.setClientSecret("**");
    resource.setAccessTokenUri("http://**/oauth/token");// Oauth2.0 服务端链接
    resource.setScope(Arrays.asList(""));// 读写权限
    resource.setUsername("**");
    resource.setPassword("**");
    resource.setGrantType("password");// Oauth2.0 使用的模式 为密码模式
    AccessTokenRequest atr = new DefaultAccessTokenRequest();
    OAuth2RestTemplate template = new OAuth2RestTemplate(resource, new DefaultOAuth2ClientContext(atr));
    ResourceOwnerPasswordAccessTokenProvider provider = new ResourceOwnerPasswordAccessTokenProvider();
    template.setAccessTokenProvider(provider);
    System.out.println(template.getAccessToken());
}

Настройка пользовательских методов аутентификации, таких как официальная учетная запись WeChat.

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

Но мы иногда аутентифицируемся не только по имени пользователя и паролю, но и по номеру мобильного телефона + проверочный код; официальный код аккаунта WeChat для аутентификации. Здесь мы настраиваем конфигурацию процесса аутентификации кода через общедоступную учетную запись WeChat.

public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {@Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        // 设置这个开启password授权方式
        endpoints.authenticationManager(authenticationManager);
        // 增加自定义授权方式,这里可以增加新的认证方式,只要自定义TokenGranter即可
        endpoints.tokenGranter(
            new CompositeTokenGranter(
                Arrays.asList(endpoints.getTokenGranter(), customTokenGranter(endpoints))
            )
        );
    }
}

О настройкеTokenGranterконфигурация, здесь, потому чтоAuthorizationServerEndpointsConfigurerНекоторые внутренние методы , используют отражение для вызова необходимых методов и используютПрокси для ленивой загрузки, иначе будут какие-товопросы жизненного цикла

private TokenGranter customTokenGranter(AuthorizationServerEndpointsConfigurer endpoints) {
    // 代理延迟加载
    return new TokenGranter() {
        private TokenGranter delegate;
        // 第一次使用时才创建
        @Override
        public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
            if (delegate == null) {
                try {
                    // private方法,需要使用getDeclaredMethod
                    Method tokenServicesMethod = endpoints.getClass().getDeclaredMethod("tokenServices"),
                    requestFactoryMethod = endpoints.getClass().getDeclaredMethod("requestFactory");
                    tokenServicesMethod.setAccessible(true);
                    requestFactoryMethod.setAccessible(true);
                    AuthorizationServerTokenServices tokenServices = (AuthorizationServerTokenServices) tokenServicesMethod.invoke(endpoints);
                    OAuth2RequestFactory requestFactory = (OAuth2RequestFactory) requestFactoryMethod.invoke(endpoints);
                    // 这里也加入了我们的认证管理器authenticationManager,所有后续通过code进行微信认证的过程也在spring security的AuthenticationManager中完成
                    delegate = new WxSubscriptionCodeTokenGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory);
                }catch (Exception e){
                    logger.error("创建自定义授权方式失败", e);
                    return null;
                }
            }
            return delegate.grant(grantType, tokenRequest);
        }
    };
}

оWxSubscriptionCodeTokenGranterРеализация базовой эталонной пружины обеспечиваетResourceOwnerPasswordTokenGranterКласс можно реализовать, а метод аутентификации поменять на нашwx_subscriptionВот и все, я не буду вдаваться в подробности о коде вставки здесь.

На данный момент настройка сервера авторизации пользовательского метода аутентификации oauth2 завершена.

Суммировать

  • Метод аутентификации oauth2 используется между распределенными сервисами, чтобы избежать передачи информации об аутентификации пользователя.Этот метод не кажется слишком много сейчас.Преимущества и недостатки можно обдумать и обсудить.
  • Создание ленивой загрузки может быть выполнено через прокси, избегая некоторых сложных вызовов зависимостей метода жизненного цикла.

В этой статье лишь кратко рассказывается, как сервер авторизации использует Spring Security oauth2 в стандарте oauth2, и в будущем может быть написано о реализации сервера ресурсов и клиента.

Приглашаем всех критиковать и обсуждать~

Ссылаться на

О процессе аутентификации oauth2Блог Woo Woo.cn на.com/Invincible Code Farmer/…

Устаревание spring-security-oauth2stackoverflow.com/questions/5…

Документация для spring-security-oauth2проекты.spring.IO/spring-brush-ECU…