Предисловие
При использовании SpringCloud для создания распределенной системы с архитектурой микросервиса OAuth2.0 является отраслевым стандартом для сертификации. Spring Security OAuth2 также предоставляет полный набор решений для поддержки использования OAuth2.0 в среде Spring Cloud/Spring Boot, обеспечивая нестандартные компоненты. Однако в процессе разработки мы обнаружим, что, поскольку компоненты Spring Security OAuth2 особенно всеобъемлющие, это делает очень неудобным расширять или нелегко указать решение для расширения, например::
Столкнувшись с этими сценариями, ожидается, что многие люди, которые не знакомы с Spring Security OAuth2, не смогут начать. Основываясь на приведенных выше требованиях сценария, как элегантно интегрировать логин кода проверки SMS и логин для сторонних и как считать элегантно интегрированным? Есть следующие требования:
Основываясь на вышеуказанных требованиях к проектированию, мы подробно расскажем, как разработать набор интегрированных компонентов аутентификации входа в систему для удовлетворения вышеуказанных требований в статье.
Прочитайте эту статью.
Идеи
Давайте посмотрим на процесс аутентификации Spring Security OAuth2:
В этом процессе не так много точек входа, и идея интегрированного входа заключается в следующем:
После получения доступа к этому процессу вы можете элегантно интегрировать сторонний логин.
выполнить
После представления идей, следующий код показывает, как их реализовать:
Первый шаг - определить перехватчик для перехвата запросов входа в систему
/** * @author liqiu * @date 2018-3-30 **/ @componentpublic class IntegrationAuthenticationFilter Extends GenericFilterBean реализует ApplicationContextAware {Private Static Liting Auth_type_parm_name = "Auth_type"; частная статическая конечная строка OAuth_token_url = "/OAuth/Token"; частная коллекция <SterationAuthenticator> аутентификаторы; Private ApplicationContext ApplicationContext; Частный запрос Matcher requestMatcher; public IntegrationAuthenticationFilter () {this.RequestMatcher = new OrRequestMatcher (новый AntPathRequestMatcher (oauth_token_url, «Get»), новый antpathrequestmatcher (oauth_token_url, «post»)); } @Override public void dofilter (ServletRequest ServletRequest, ServletResponse ServletResponse, FilterChain FilterChain) Throws IOException, ServletException {httpserVletRequest = (httpservletrequest) servletrequest; Httpservletresponse response = (httpservletresponse) Servletresponse; if (requestmatcher.matches (request)) {// установить интегрированную информацию в систему интеграции. IntegrationAuthentication.setAuthtype (request.getParameter (auth_type_parm_name)); IntegrationAuthentication.setAuthParameters (request.getParameterMap ()); IntegrationAuthenticationContext.Set (IntegrationAuthentication); try {// Предварительная обработка this.prepare (IntegrationAuthentication); FilterChain.dofilter (запрос, ответ); // пост-обработка этого. } наконец {IntegrationAuthenticationContext.Clear (); }} else {filterchain.dofilter (запрос, ответ); }} / *** preprocessing* @param IntegrationAuthentication* / private void Prepare (IntegrationAuthentication IntegrationAuthentication) {// Ленивая загрузка аутентикатора if (this. ApplicationContext.getBeansOftype (IntegrationAuthenticator.class); if (IntegrationAuthenticATormap! = null) {this.Authenticators = IntegrationAuthentIcatormap.values (); }}} if (this.authenticators == null) {this.authenticators = new ArrayList <> (); } for (IntegrationAuthenticator Authenticator: Authenticators) {if (Authenticator.support (IntegrationAuthentication)) {Authenticator.prepare (IntegrationAuthentication); }}} / *** Пост-обработка* @param IntegrationAuthentication* / private void Complete (IntegrationAuthentication IntegrationAuthentication) {for (IntegrationAuthenticator Authentication: Authentications) {if (Authenticator.Support (IntegrationAuthentication) {Authentication.complete (IntegrationAuthentication); }}} @Override public void setApplicationContext (ApplicationContext ApplicationContext) Throws BeanSexception {this.ApplicationContext = ApplicationContext; }}В этом классе две части работы в основном завершены: 1. Получить текущий тип аутентификации в соответствии с параметрами, 2. Вызовите различные интеграции.
Шаг 2: Поместите перехватчик в цепь перехвата
/** * @author liqiu * @date 2018-3-7 **/ @configuration @enableauthorizationserverpublic class AutorrizationserverConfiguration Extends AutorrizationServerConfigurerAdapter {@Autowired Private RedisconnectionFactory RedisconnectionFactory; @Autowired Private Authentication Manager AuthenticationManager; @Autowired Private IntegrationUserDetailsService IntegrationUserDetailsService; @Autowired private webresponsexceptiontranslator webresponsexceptiontranslator; @Autowired Private IntegrationAuthenticationFilter IntegrationAuthenticationFilter; @Autowired частная база данных Databasecableclientdetailsservice resisclientdetailsservice; @Override public void configure (ClientDetailsServiceConfigurer клиенты) бросает исключение {// todo repist clients детализируйте клиенты. } @Override public void configure (AuthorizationServerendPointSconfigurer Endpoints) {конечные точки .tokenstore (new Redistokenstore (RedisconnectionFactory)) //. Accesstokenconverter (jwtaccesstokenconverter ().). .reuserefreshtokens (false) .userdetailsservice (IntegrationUserDetailsService); } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.allowFormAuthenticationForClients() .tokenKeyAccess("isAuthenticated()") .checkTokenAccess("permitAll()") .addTokenEndpointAuthenticationFilter(integrationAuthenticationFilter); } @Bean public passwordEncoder passwordEncoder () {return new bcryptpasswordencoder (); } @Bean public jwtaccesstokenconverter jwtaccesstokenconverter () {jwtaccesstokenconverter jwtaccesstokenconverter = new jwtaccesstokenconverter (); jwtaccesstokenconverter.setsigningKey ("cola-cloud"); вернуть jwtaccesstokenconverter; }}Поместите перехватчик в цепочку аутентификации, позвонив в безопасность. .AddTokenEnpointAuthenticationFilter (IntegrationAuthenticationFilter); метод
Шаг 3: Обработайте информацию пользователя в соответствии с типом аутентификации
@ServicePublic Class IntegrationUserDetailsService реализует userdetailsservice {@autowired private upmclient upmclient; частный список <IntegrationAuthenticator> аутентикаторы; @Autowired (обязательно = false) public void setIntegrationAuthenticators (list <IntegrationAuthenticator> Authenticators) {this.Authenticators = Authenticators; } @Override public user lokuserbyusername (string username) выбрасывает usernamenotfoundexception {IntegrationAuthentication IntegrationAuthentication = IntegrationAuthenticationContext.get (); // Судят, является ли это интегрированным входом в систему if (IntegrationAuthentication == null) {IntegrationAuthentication = new IntegrationAuthentication (); } IntegrationAuthentication.SetUSERNAME (имя пользователя); Uservo uservo = this.authenticate (IntegrationAuthentication); if (uservo == null) {бросить новый usernamenotfoundexception («Имя пользователя или ошибка пароля»); } User user = new user (); Beanatils.copyproperties (Uservo, пользователь); this.setauthorize (пользователь); вернуть пользователь; } / ** * Установить информацию о авторизации * * @param пользователь * / public void setAuthorize (пользователь пользователя) {Authorize Authorize = this.upmClient.getAuthorize (user.getId ()); user.setRoles (Authorize.getRoles ()); user.setResources (Authorize.getResources ()); } private USERVOUTENTICATION (интеграция интеллектуального интеграции) {if (this.Authenticators! = null) {for (IntegrationAuthenticator Authenticator: Authenticators) {if (Authenticator.Support (IntegrationAuthentication)) {return Authenticator oThipentIcation (IntegrationAuthentication); }} return null; }}Вот интегрированная организация. Метод аутентификации будет вызван в методе ugloserbyusername. В методе аутентификации текущий тип аутентификации контекста будет называть различную интеграцию -аутютикатор для получения пользовательской информации. Давайте посмотрим, как обрабатываются имя пользователя и пароль по умолчанию:
@Component @primarypublic class usernamepasswordAuthenticator расширяет AbstractPreparableIntegrationAuthenticator {@Autowired Private UCClient UCClient; @Override Public Uservo Authentication (IntegrationAuthentication IntegrationAuthentication) {return ucclient.finduserbyusername (IntegrationAuthentication.getUsername ()); } @Override public void Prepare (IntegrationAuthentication IntegrationAuthentication) {} @Override Public Boolean Support (IntegrationAuthentication IntegrationAuthentication) {return stringUtils.isempty (IntegrationAuthentication.getAuthtype ()); }}UsernamepasswordAuthenticator будет обрабатывать только тип аутентификации по умолчанию без указанного типа аутентификации. Этот класс в основном получает пароли через имя пользователя. Далее, давайте посмотрим, как обрабатывать логин кода проверки изображения:
/*** Аутентификация интегрированной проверки кода* @author liqiu* @date 2018-3-31 **/ @componentpublic class vervificationcodeintegrationauthenticator exters usernamepasswordauthenticator {private final Static String verification_code_type = "vc"; @Autowired private vccclient vccclient; @Override public void Prepare (IntegrationAuthentication IntegrationAuthentication) {String vctoken = IntegrationAuthentication.getAuthParameter ("vc_token"); String vccode = IntegrationAuthentication.getAuthParameter ("vc_code"); // проверка проверки Coderesult <boolean> result = vccclient.validate (vctoken, vccode, null); if (! result.getData ()) {бросить новый oauth2exception ("ошибка кода проверки"); }} @Override Public Boolean Support (IntegrationAuthentication IntegrationAuthentication) {return vervification_code_auth_type.equals (IntegrationAuthentication.getAuthtype ()); }}VerificationCodeIntegrationAuthenticator Унаследует usernamepasswordAuthenticator, поскольку ему нужно только проверить, является ли код проверки правильным в методе подготовки, и получил ли пользователь его с помощью имени пользователя и пароля. Тем не менее, тип аутентификации является «VC» до того, как он может быть обработан. Давайте посмотрим, как обрабатывается логин кода SMS -проверки:
@Componentpublic class smsintegrationauthenticator расширяет AbstractPreparableInteGrationAuthenticator реализует ApplicationEventPublisherAware {@Autowired Private UCClient UCClient; @Autowired private vccclient vccclient; @Autowired private passwordEncoder passwordEncoder; Private ApplicationEventPublisher ApplicationEventPublisher; Приватная окончательная статическая строка sms_auth_type = "sms"; @Override Public USERVOUTICANTICATION (IntegrationAuthentication IntegrationAuthentication) {// Получить пароль, фактическим значением является код проверки String Password = IntegrationAuthentication.getAuthParameter ("пароль"); // Получить имя пользователя, фактическим значением является номер мобильного телефона string username = IntegrationAuthentication.getUSERNAME (); // публиковать события, вы можете прослушать события для автоматического регистрации пользователя this.ApplicationEventpublisher.publishevent (new smsauthenticatebeforeevent (IntegrationAuthentication)); // Запрос пользователя через номер мобильного телефона Uservo uservo = this.ucclient.finduserbyphonenumber (имя пользователя); if (uservo! = null) {// Установить пароль в качестве кода проверки uservo.setpassword (passwordencoder.encode (пароль)); // публиковать события, вы можете слушать события для уведомления сообщений this.applicationEventPublisher.publisheVent (новый SmsauthenticatesUccessevent (IntegrationAuthentication)); } return uservo; } @Override public void Prepare (IntegrationAuthentication IntegrationAuthentication) {String smstoken = IntegrationAuthentication.getAuthParameter ("sms_token"); String smscode = IntegrationAuthentication.getAuthParameter ("пароль"); String username = IntegrationAuthentication.getAuthParameter ("имя пользователя"); Результат <boolean> result = vccclient.validate (smstoken, smscode, username); if (! result.getData ()) {бросить новое oauth2exception («ошибка кода проверки или истек»); }} @Override Public Boolean Support (IntegrationAuthentication IntegrationAuthentication) {return sms_auth_type.equals (IntegrationAuthentication.getAuthtype ()); } @Override public void setApplicationEventpublisher (ApplicationEventPublisher ApplicationEventPublisher) {this.ApplicationEventPublisher = ApplicationEventPublisher; }}SmsintegrationAuthenticator будет предварительно обработать зарегистрированный код проверки SMS, чтобы определить, является ли он незаконным. Если это незаконно, это напрямую прертит вход в систему. Если предварительная обработка передается, пользовательская информация будет получена через номер мобильного телефона при получении информации пользователя, а пароль будет сброшен для прохождения последующей проверки пароля.
Суммировать
В этом решении основное использование цепочки ответственности и шаблона проектирования адаптера для решения проблемы интегрированного входа, улучшает масштабируемость и не загрязняет исходный код пружины. Если вы хотите унаследовать другие логины, вам нужно только реализовать пользовательский интеграция.
Адрес проекта: https://gitee.com/leecho/cola-cloud
Локальная загрузка: cola-cloud_jb51.rar
Выше всего содержание этой статьи. Я надеюсь, что это будет полезно для каждого обучения, и я надеюсь, что все будут поддерживать Wulin.com больше.