Prefácio
Ao usar o SpringCloud para construir um sistema distribuído com arquitetura de microsserviço, o OAuth2.0 é o padrão da indústria para certificação. O Spring Security OAuth2 também fornece um conjunto completo de soluções para apoiar o uso do OAuth2.0 no ambiente de bota de nuvem/primavera da primavera, fornecendo componentes prontos para a caixa. No entanto, durante o processo de desenvolvimento, descobriremos que, como os componentes do Spring Security OAuth2 são particularmente abrangentes, isso torna muito inconveniente estender ou não é fácil especificar diretamente a solução de extensão, como:
Ao enfrentar esses cenários, espera -se que muitas pessoas que não estão familiarizadas com o Spring Security OAuth2 não possam começar. Com base nos requisitos de cenário acima, como integrar elegantemente o login do código de verificação de SMS e o login de terceiros e como ser considerado elegantemente integrado? Existem os seguintes requisitos:
Com base nos requisitos de design acima, apresentaremos em detalhes como desenvolver um conjunto de componentes de autenticação de login integrados para atender aos requisitos acima no artigo.
Leia este artigo que você precisa saber sobre o Sistema de Certificação OAuth2.0, Springboot, SpringSecurity, Spring Cloud e outros conhecimentos relacionados
Idéias
Vamos dar uma olhada no processo de autenticação do Spring Security OAuth2:
Nesse processo, não há muitos pontos de entrada, e a idéia de login integrado é a seguinte:
Depois de acessar esse processo, você pode basicamente integrar o login de terceiros elegantemente.
concluir
Depois de apresentar as idéias, o código a seguir mostra como implementá -lo:
O primeiro passo é definir o interceptador para interceptar solicitações de login
/** * @author liqiu * @date 2018-3-30 **/ @componentPublic Class IntegrationAthenticationFilter Estende o genericFilterBean implementa ApplicationContexTAWare {private Static final String auth_type_parm_name = "Auth_Type"; String final estática privada oauth_token_url = "/oauth/token"; Coleção particular <GreationAuthenticator> autenticadores; ApplicationContext ApplicationContext privado; Private RequestMatcher RequestMatcher; public integrationAuthenticationFilter () {this.RequestMatcher = new OrrequestMatcher (novo AntPathRequestMatcher (OAuth_Token_Url, "Get"), novo AntPathRequestMatcher (OAuth_Token_Url, "Post"); } @Override Public void Dofilter (servletRequest servletRequest, servletResponse servletResponse, filterchain FilterChain) lança IoException, servletexception {httpServletRequest request = (httpsPleTrequest) servletRequest; HttpServLeTResponse Response = (httpServletResponse) servletResponse; if (requestMatcher.matches (request)) {// Definir informação de login integrada IntegrationAthentication integrationAthentication = new IntegrationAuthentication (); integrationAuthentication.SetAuthType (request.getParameter (auth_type_parm_name); integrationAuthentication.setAuthParameters (request.getParameterMap ()); IntegrationAuthenticationContext.Set (integrationAuthentication); tente {// pré -processamento this.Prepare (integrationAuthentication); filterChain.dofilter (solicitação, resposta); // pós-processamento this.complete (integrationauthentication); } finalmente {integrationAuthenticationContext.clear (); }} else {filterChain.dofilter (solicitação, resposta); }} / *** pré -processamento* @param integrationAuthentication* / private void preparar (integração integração integraçãoAuthentication) {// carregamento preguiçoso autenticator if (this.authenticators == null) {synchronized (this) {map <string, integrationAthicatator> ApplicationContext.getBeansOftype (integrationAuthenticator.class); if (integrationauthenticatormap! = null) {this.authenticators = integrationAuthenticatorMap.values (); }}} if (this.authenticators == null) {this.authenticators = new ArrayList <> (); } para (IntegrationAuthenticator autenticator: autenticadores) {if (autenticator.support (integrationAuthentication)) {autenticator.prepare (integrationauthentication); }}} / *** pós-processamento* @param integrationAthentication* / private void completo (integração integração integrationAuthentication) {for (integrationAuthenticator Authentication: Authentications) {if (autenticator.support (integrationEntication)) {autentication.com (autenticator.support (integrationEntication)) {autentication.com; }}} @Override public void setApplicationContext (ApplicationContext ApplicationContext) lança beansexception {this.applicationContext = ApplicationContext; }}Nesta classe, duas partes do trabalho são concluídas principalmente: 1. Obtenha o tipo de autenticação atual de acordo com os parâmetros, 2. Ligue para diferentes integraçãoAuthenticator.
Etapa 2: Coloque o interceptador na cadeia de interceptação
/** * @author liqiu * @date 2018-3-7 **/ @configuration @EnableAuthorizationserververpublic Autorizações de classe A autorizações de figuração estende AutorizaçõeserververConfigureRAdApter {@Autowired RedisconnectionFactory RedisconectionAconização; @AUTOWIRED AUTHenticação privada Manager AuthenticationManager; @AUTOWIRED IntegrationUserDetailSService IntegrationUserDetailSService; @AUTOWIRED PRIVADO WEBRESPONSECPECIDOTRANSLADOR WebResponseExceptionTranslator; @AUTOWIRED IntegrationAthenticationFilter IntegrationAthenticationFilter; @AUTOWIRED DATABASECACHABLECLIENTDETAILSERVICE REDISCLIENTDETAILSSERVICE; @Override public void Configure (ClientDetailSServiceConFigurer Clients) lança exceção {// TODO Persist Customer Detalhes os clientes. } @Override public void Configure (AutorizaçõeservendPointSconfigurer endpoints) {Endpoints .TokenStore (New RedistokenStore (RedisconectionFactory)) // .AccessTokEnconverter (JWTaccessTokEnconverter () .authEnsManager (autenticação). .reuserefreshtokens (false) .UserDetailSService (integrationUserDetailSService); } @Override public void Configure (AutorizaçõeserverversorCurityConfigurer Security) lança Exceção {Security.allowFormAthenticationForCliients () .TokenKeyAccess ("isauthenticated ()") .CheckTokenActAnThentication ("Permitall ()") .AdToken (). } @Bean Public PasswordEncoder PasswordEncoder () {Return New BCryptPasswordEncoder (); } @Bean public jwtaccessTokEnconverter jwtaccessTokEnconverter () {jwtaccessTokEnconverter jwtaccessTokenconverter = new jwtaccestTokEnconverter (); jwtaccessTokEnconverter.SetSigningKey ("Cola-cloud"); devolver jwtaccessTokEnconverter; }}Coloque o interceptador na cadeia de autenticação chamando a segurança. .AddTokenEndPointAuthenticationFilter (integrationAuthenticationFilter); método.
Etapa 3: Processe as informações do usuário de acordo com o tipo de autenticação
@ServicePublic Class IntegrationUserDetailSService implementa UserDetailSService {@AUTOWIRED PRIVADO UPMCLIENT UPMCLIENT; Lista privada <GreationAuthenticator> autenticadores; @AUTOWIRED (requerir = false) public void setIntegrationAuthenticators (List <GreationAtHenticator> autenticadores) {this.authenticators = autenticadores; } @Override Public User loadUserByUserName (String UserName) lança UserNameNotFoundException {integrationAthentication integrationAthentication = integrationAuthenticationContext.get (); // julga se é um login integrado se (integrationAuthentication == null) {integrationAuthentication = new IntegrationAuthentication (); } integrationAuthentication.setUserName (nome de usuário); Uservo uservo = this.authenticate (integrationauthentication); if (uservo == null) {lança nova UserNameNotFoundException ("Nome de usuário ou erro de senha"); } Usuário do usuário = novo usuário (); Beanutils.copyProperties (USERVO, usuário); this.setAuthorize (usuário); devolver usuário; } / ** * Defina informações de autorização * * @param usuário * / public void setathorize (usuário do usuário) {Autorize Authorize = this.upmclient.getauthorize (user.getId ()); user.setRoles (Autorize.getRoles ()); user.setResources (Autorize.getResources ()); } private USERVO Authenticate (IntegrationAuthentication IntegrationAthentication) {if (this.authenticators! = null) {for (integrationauthenticator autenticator: autenticadores) {if (autenticator.suporpt (integrationAthentication)) {returnEnticator.authenticate }} retornar nulo; }}Aqui está uma integração -userDetailSService. O método de autenticação será chamado no método do nome do loadUserByusern. No método autenticado, o tipo de autenticação de contexto atual chamará diferentes integraçãoAuthenticator para obter informações do usuário. Vamos dar uma olhada em como o nome de usuário e a senha padrão são tratados:
@Componente @classe primáriapublic usernamePassWordAuthenticator estende abstratoPreparableIntegrationAuthenticator {@aUTowired Private UcClient UcClient; @Override Public UserVo Authentication (integrationAthentication integrationAthentication) {return ucclient.finduserbyusername (integrationAuthentication.getUserName ()); } @Override public void Prepare (IntegrationAthentication IntegrationAthentication) {} @Override Public Boolean Support (integração integração integraçãoAuthentication) {return stringutils.isEmpty (integrationAuthentication.getauthtype ()); }}UsernamePassWordAuthenticator lidará apenas com o tipo de autenticação padrão sem tipo de autenticação especificada. Esta classe obtém principalmente senhas através do nome de usuário. Em seguida, vamos dar uma olhada em como lidar com o login do código de verificação de imagem:
/*** Código de verificação integrado Autenticação* @author liqiu* @date 2018-3-31 **/ @ComponentPublic VerificationCodeIntegrationAuthenticator estende UsernamePassWordAtHenticator {Private Final Static String Verification_Code_Auth_Type = "VC"; @Autowired Private VCCClient VCCClient; @Override public void Prepare (integrationAuthentication integrationAthentication) {String vcToken = integrationAuthentication.GetauthParameter ("vc_token"); String vccode = integrationAuthentication.GetauthParameter ("vc_code"); // CodeResult de verificação de verificação <COOLEAN> resultado = vccclient.validate (vctoken, vccode, null); if (! Result.getData ()) {lança a nova OAuth2Exception ("Erro do código de verificação"); }} @Override Public Boolean Support (IntegrationAuthentication IntegrationAthentication) {return verification_code_auth_type.equals (integrationauthentication.getauthtype ()); }}O VerificationCodeIntegrationAuthenticator herda UsernamePasswordAuthenticator, pois só precisa verificar se o código de verificação está correto no método de preparação e se o usuário o obteve usando o nome de usuário e a senha. No entanto, o tipo de autenticação é "VC" antes que possa ser processado. Vamos dar uma olhada em como o login do código de verificação do SMS é tratado:
@ComponentPublic Classe SMSIntegrationAuthenticator estende abstratoPreparableIntegrationAuthenticator implementa a ApplicationEventPublisheRaWare {@AUTOWIRED PRIVADO UCCLIENT UCCLIENT; @Autowired Private VCCClient VCCClient; @AUTOWIRED PRATDERENCODER PRAVADA DE PRAVADA; Applicativo privado Application ApplicationEventPublisher; String estática final privada SMS_AUTH_TYPE = "SMS"; @Override Public UserVo Authentication (integrationAuthentication IntegrationAthentication) {// Get Senha, o valor real é o código de verificação String senha = integrationAuthentication.GetauthParameter ("Senha"); // Obter nome de usuário, o valor real é o número do telefone celular String userName = integrationAuthentication.getUserName (); // Publicar eventos, você pode ouvir eventos para registrar automaticamente o usuário this.ApplicationEventPublisher.publishEvent (novo smSauthenticateBeEvent (integrationAuthentication)); // Consulta User User através do número de telefone celular UserVo UserVo = this.ucclient.finduserbyphoneNumber (nome de usuário); if (uservo! = null) {// Defina a senha como o código de verificação uservo.setpassword (senhanencoder.encode (senha)); // Publicar eventos, você pode ouvir eventos para notificação de mensagens. } retornar USERVO; } @Override public void Prepare (integrationAuthentication IntegrationAthentication) {String smstoken = integrationAuthentication.GetaThParameter ("SMS_TOKEN"); String smscode = integrationAuthentication.GetauthParameter ("senha"); String userName = integrationAuthentication.GetauthParameter ("Nome de usuário"); Result <Boolean> resultado = vccclient.validate (smstoken, smscode, nome de usuário); if (! result.getData ()) {lança nova oauth2Exception ("erro de código de verificação ou expirado"); }} @Override Public Boolean Support (IntegrationAthentication integrationAthentication) {return sms_auth_type.equals (integrationAuthentication.getauthtype ()); } @Override public void setApplicationEventPublisher (ApplicationEventPublisher ApplicationEventPublisher) {this.ApplicationEventPublisher = ApplicationEventPublisher; }}O SMSINTEGRATIONATHENTICATOR pré-processará o código de verificação de SMS conectado para determinar se é ilegal. Se for ilegal, interromperá diretamente o login. Se o pré -processamento for passado, as informações do usuário serão obtidas através do número de telefone celular ao obter as informações do usuário e a senha será redefinida para passar a verificação subsequente de senha.
Resumir
Nesta solução, o principal uso da cadeia de responsabilidade e o padrão de design do adaptador para resolver o problema do login integrado, melhora a escalabilidade e não polui o código -fonte da primavera. Se você deseja herdar outros logins, só precisará implementar um integração personalizada.
Endereço do projeto: https://gitee.com/leecho/cola-cloud
Download local: cola-cloud_jb51.rar
O exposto acima é todo o conteúdo deste artigo. Espero que seja útil para o aprendizado de todos e espero que todos apoiem mais o wulin.com.