2017-08-15 3 views
2

Ich versuche, Google-Anmeldung in eine vorhandene Spring-Sicherheitsanwendung zu integrieren. Das Ziel besteht darin, einen Google-Anmelde-Button zu haben, über den sich ein Nutzer zusammen mit dem Standard-Login mit der Kombination Benutzername/Passwort anmelden kann.Google-Anmeldung mit Java Spring-Sicherheitsanwendung

auf der Führung aus, dass Google (https://developers.google.com/identity/sign-in/web/backend-auth) sieht es sieht aus wie alles, was ich tun muß, ist (hat, dass derzeit nur das Login und die Passwort-Eingabefelder) das Anmeldeformular erweitere mit einem zusätzlichen Feld „id_token“ und senden Sie es an den Server.

Wäre es eine gute Sicherheitsvorkehrung? Ich suchte im Internet und bin überrascht, dass ich keine ähnlichen Implementierungen im Internet finden kann.

+0

Sie müssen sich um einige Probleme kümmern. Wie würden Sie lokale Benutzer für Spring Sec Application und externe Benutzer von Google verwalten? Wie werden Principals von Google in den Principal von Spring Sec übersetzt? Das sind die zwei, die ich von ganzem Herzen unterscheiden kann. – lazyneuron

+0

"Wie würden Sie lokale Benutzer für Spring Sec Application und externe Benutzer von Google verwalten?" Ich werde sie genauso verwalten, einen neuen Benutzer zu erstellen, wenn der nicht bereits existiert. "Wie werden Principals von Google in Spring Sec Principal übersetzt?" Ich muss dem Google-Benutzer eine Option zur Verfügung stellen, um den Spring sec-Benutzer zu binden, indem er eine gültige Login/Passwort-Kombination angibt (für die der Spring-Benutzer registriert wurde). –

Antwort

0

So wandte sich die richtige Antwort nicht aus den vorhandenen Auth Filter/Anbieter erstreckt, sondern die Definition/das Hinzufügen eines weiteren {Token-Authentifizierung Klasse + Token Auth Filter + Token Auth-Anbieter (Provider ist eine Art optional)}

1

hier ist mein nehmen auf die erforderlichen Federsicherheitskomponenten:

Filter:

import org.springframework.security.authentication.AuthenticationManager; 
import org.springframework.security.core.Authentication; 
import org.springframework.security.core.AuthenticationException; 
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; 
import org.springframework.util.Assert; 

import javax.servlet.ServletException; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import java.io.IOException; 

public class GoogleIdAuthenticationFilter extends AbstractAuthenticationProcessingFilter { 
    private static final long serialVersionUID = 1L; 
    private String tokenParamName = "googleIdToken"; 

    /** 
    * Creates an instance which will authenticate against the supplied 
    * {@code AuthenticationManager} and which will ignore failed authentication attempts, 
    * allowing the request to proceed down the filter chain. 
    * 
    * @param authenticationManager  the bean to submit authentication requests to 
    * @param defaultFilterProcessesUrl the url to check for auth requests on (e.g. /login/google) 
    */ 
    public GoogleIdAuthenticationFilter(AuthenticationManager authenticationManager, String defaultFilterProcessesUrl) { 
     super(defaultFilterProcessesUrl); 
     Assert.notNull(authenticationManager, "authenticationManager cannot be null"); 
     setAuthenticationManager(authenticationManager); 
    } 

    @Override 
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { 
     String token = request.getParameter(tokenParamName); 

     if (token == null) { 
      return null; 
     } 

     if (this.logger.isDebugEnabled()) { 
      this.logger.debug("Google ID Token Authorization parameter found with value '" + token + "'"); 
     } 

     Object details = this.authenticationDetailsSource.buildDetails(request); 

     GoogleIdAuthenticationToken authRequest = new GoogleIdAuthenticationToken(token, details); 

     Authentication authResult = getAuthenticationManager().authenticate(authRequest); 

     if (this.logger.isDebugEnabled()) { 
      this.logger.debug("Authentication success: " + authResult); 
     } 

     return authResult; 
    } 

    public String getTokenParamName() { 
     return tokenParamName; 
    } 

    public void setTokenParamName(String tokenParamName) { 
     this.tokenParamName = tokenParamName; 
    } 
} 

Authentifizierungsanbieter:

import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken; 
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload; 
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier; 
import com.google.api.client.http.HttpTransport; 
import com.google.api.client.http.apache.ApacheHttpTransport; 
import com.google.api.client.json.JsonFactory; 
import com.google.api.client.json.jackson2.JacksonFactory; 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.context.support.MessageSourceAccessor; 
import org.springframework.security.authentication.*; 
import org.springframework.security.core.Authentication; 
import org.springframework.security.core.AuthenticationException; 
import org.springframework.security.core.SpringSecurityMessageSource; 
import org.springframework.security.core.userdetails.UserDetailsService; 
import org.springframework.security.core.userdetails.UsernameNotFoundException; 

import javax.annotation.Resource; 
import java.io.IOException; 
import java.security.GeneralSecurityException; 
import java.util.Collections; 

public class GoogleIdAuthenticationProvider implements AuthenticationProvider { 
    private static final Logger logger = LoggerFactory.getLogger(GoogleIdAuthenticationProvider.class); 

    private String clientId; 

    @Resource 
    private UserDetailsService userDetailsService; 

    private HttpTransport httpTransport = new ApacheHttpTransport(); 
    private JsonFactory jsonFactory = new JacksonFactory(); 

    protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); 

    @Override 
    public Authentication authenticate(Authentication authentication) throws AuthenticationException { 
     if (!supports(authentication.getClass())) { 
      if (logger.isDebugEnabled()) { 
       logger.debug(String.format("This authentication provider does not support instances of type %s", authentication.getClass().getName())); 
      } 
      return null; 
     } 

     GoogleIdAuthenticationToken googleIdAuthenticationToken = (GoogleIdAuthenticationToken) authentication; 

     if (logger.isDebugEnabled()) 
      logger.debug(String.format("Validating google login with token '%s'", googleIdAuthenticationToken.getCredentials())); 


     GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(httpTransport, jsonFactory) 
       .setAudience(Collections.singletonList(getClientId())) 
       .build(); 

     GoogleIdToken googleIdToken = null; 
     try { 
      googleIdToken = verifier.verify((String) googleIdAuthenticationToken.getCredentials()); 

      if (googleIdToken == null) { 
       throw new BadCredentialsException("Unable to verify token"); 
      } 
     } catch (IOException|GeneralSecurityException e) { 
      throw new BadCredentialsException("Unable to verify token", e); 
     } 

     Payload payload = googleIdToken.getPayload(); 

     // Get profile information from payload 
     String email = payload.getEmail(); 

     if (logger.isDebugEnabled()) { 
      logger.debug(String.format("Loading user details for email '%s'", email)); 
     } 
     UserDetails userDetails = null; 
     try { 
      userDetails = userDetailsService.loadUserByUsername(email); 

      if (!userDetails.isAccountNonLocked()) { 
       throw new LockedException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.locked", "User account is locked")); 
      } 

      if (!userDetails.isEnabled()) { 
       throw new DisabledException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.disabled", "User is disabled")); 
      } 

      if (!userDetails.isAccountNonExpired()) { 
       throw new AccountExpiredException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.expired", "User account has expired")); 
      } 
     } catch (UsernameNotFoundException e) { 
      // provision a new user? 
      throw e; 
     } 

     return new GoogleIdAuthenticationToken((String) googleIdAuthenticationToken.getCredentials(), userDetails.getUsername(), userDetails.getAuthorities(), authentication.getDetails()); 
    } 

    @Override 
    public boolean supports(Class<? extends Object> authentication) { 
     return (GoogleIdAuthenticationToken.class.isAssignableFrom(authentication)); 
    } 

    public String getClientId() { 
     return clientId; 
    } 

    public void setClientId(String clientId) { 
     this.clientId = clientId; 
    } 
} 

token:

import org.springframework.security.authentication.AbstractAuthenticationToken; 
import org.springframework.security.core.GrantedAuthority; 

import java.util.ArrayList; 
import java.util.Collection; 

public class GoogleIdAuthenticationToken extends AbstractAuthenticationToken { 
    private String credentials; 
    private Object principal; 

    public GoogleIdAuthenticationToken(String token, Object details) { 
     super(new ArrayList<>()); 
     this.credentials = token; 
     setDetails(details); 
     setAuthenticated(false); 
    } 

    GoogleIdAuthenticationToken(String token, String principal, Collection<? extends GrantedAuthority> authorities, Object details) { 
     super(authorities); 
     this.credentials = token; 
     this.principal = principal; 
     setDetails(details); 
     setAuthenticated(true); 
    } 

    @Override 
    public Object getCredentials() { 
     return credentials; 
    } 

    @Override 
    public Object getPrincipal() { 
     return principal; 
    } 
} 

Nachdem in der Verstopfungs über Sie nur brauchen, um es zu "/ login/google" (oder was auch immer Sie konfiguriert haben) mit dem Token durch Google in der ‚googleIdToken zurück '(oder was auch immer Sie konfiguriert haben).

+0

Die Eigenschaft "clientId" des AuthenticationProvider welcher Wert hätte? Ich bin diesen Schritten gefolgt, aber ich weiß nicht, wie ich diesen Wert initialisieren soll. – idiazt

+0

Entschuldigung, ich habe gerade festgestellt, dass clientId die ID ist, die von der API-Konsole von Google für das Auth-Projekt angegeben wird. Ich werde deine Antwort abstimmen. – idiazt