[JAVA] I tried Google Sign-In with Spring Boot + Spring Security REST API

I made it possible to log in to the REST API server created with Spring Boot with Google Sign-In, so I will write down what I did.

The version of Spring Boot I'm using is 1.5.7. Most of what you're doing below may be possible with @ EnableOAuth2Sso, but that's not interesting, or that's it.

Preparation (REST API for authentication)

Before supporting Google Sign-In, change the default behavior of Spring Security to a REST feeling.

Reference site: http://www.baeldung.com/securing-a-restful-web-service-with-spring-security

Entry point

In a standard web application, if you access a secure resource in an unauthenticated state, it will automatically prompt you to authenticate, but since the REST service explicitly authenticates, in such a case, simply press 401. It will be returned.

RestAuthenticationEntryPoint.java


@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {

  @Override
  public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
  }
}

Returns 200 instead of 301 on successful login

By default, when login is successful, 301 is returned and the screen is guided to the screen after login, but in REST, 200 is simply returned. Create a ʻAuthenticationSuccessHandler that does not redirect by referring to the SavedRequestAwareAuthenticationSuccessHandler`.

RestSavedRequestAwareAuthenticationSuccessHandler.java


@Component
public class RestSavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

  private RequestCache requestCache = new HttpSessionRequestCache();

  @Override
  public void onAuthenticationSuccess(HttpServletRequest request,
                                      HttpServletResponse response, Authentication authentication) {
    SavedRequest savedRequest = requestCache.getRequest(request, response);

    if (savedRequest == null) {
      clearAuthenticationAttributes(request);
      return;
    }
    String targetUrlParameter = getTargetUrlParameter();
    if (isAlwaysUseDefaultTargetUrl()
      || (targetUrlParameter != null && StringUtils.hasText(request
      .getParameter(targetUrlParameter)))) {
      requestCache.removeRequest(request, response);
      clearAuthenticationAttributes(request);
      return;
    }

    clearAuthenticationAttributes(request);
  }
}

Returns 401 instead of 302 on login failure

Similarly, if login fails, simply return 401. You can use Spring's SimpleUrlAuthenticationFailureHandler as it is.

Just return 200 when logging out

Similarly, when logging out, it does not redirect and only returns 200. You can also use Spring's SimpleUrlLogoutSuccessHandler here.

SecurityConfig Create a Configuration in the state so far.

SecurityConfig.java


@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Autowired
  private RestAuthenticationEntryPoint authenticationEntryPoint;

  @Autowired
  private RestSavedRequestAwareAuthenticationSuccessHandler authenticationSuccessHandler;

  @Autowired
  private SimpleUrlAuthenticationFailureHandler authenticationFailureHandler;

  @Autowired
  private SimpleUrlLogoutSuccessHandler logoutSuccessHandler;

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
      .csrf().disable()
      .authorizeRequests()
        .antMatchers("/api/**").authenticated()
        .and()
      .exceptionHandling()
        .authenticationEntryPoint(authenticationEntryPoint)
        .and()
      .formLogin()
        .successHandler(authenticationSuccessHandler)
        .failureHandler(authenticationFailureHandler)
        .and()
      .logout()
        .logoutSuccessHandler(logoutSuccessHandler)
    ;
  }

  @Bean
  public SimpleUrlAuthenticationFailureHandler simpleUrlAuthenticationFailureHandler() {
    return new SimpleUrlAuthenticationFailureHandler();
  }

  @Bean
  public SimpleUrlLogoutSuccessHandler simpleUrlLogoutSuccessHandler() {
    return new SimpleUrlLogoutSuccessHandler();
  }
}

Implement Google Sign-In

Finally the main subject. Coco and Coco -auth) as a reference. From the web application (front side), ʻid_token that can be obtained at sign-in is implemented to POST to / loginwithx-www-form-urlencoded. The parameter name is google_id_token`.

Fix SecurityConfig

Make the formLogin () part of the created SecurityConfig # configure (HttpSecurity http) as follows.

SecurityConfig.java


      .formLogin()
        .passwordParameter("google_id_token")
        .successHandler(authenticationSuccessHandler)

I just added passwordParameter ("google_id_token"). You will now receive ʻid_token` as the password for authentication with your default username and password.

Implemented ʻAuthenticationProvider`

Now that you've received ʻid_token`, all you have to do is implement authentication.

GoogleIdAuthenticationProvider.java


@Component
public class GoogleIdAuthenticationProvider implements AuthenticationProvider {

  private final String clientId = "XXX.apps.googleusercontent.com";

  @Override
  public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    String tokenString = (String) authentication.getCredentials();
    GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(UrlFetchTransport.getDefaultInstance(), JacksonFactory.getDefaultInstance())
      .setAudience(singletonList(clientId))
      .build();
    GoogleIdToken idToken;
    try {
      idToken = verifier.verify(tokenString);
      if (idToken == null) {
        throw new BadCredentialsException("Failed to verify token");
      }
    } catch (GeneralSecurityException|IOException e) {
      throw new AuthenticationServiceException("Failed to verify token", e);
    }

    GoogleIdToken.Payload payload = idToken.getPayload();
    String userId = payload.getSubject();
    String name = (String) payload.get("name");
    GoogleUser user = new GoogleUser(userId, name);
    List<GrantedAuthority> authorities = singletonList(new SimpleGrantedAuthority("ROLE_USER"));
    return new UsernamePasswordAuthenticationToken(user, tokenString, authorities);
  }

  @Override
  public boolean supports(Class<?> authentication) {
    return UsernamePasswordAuthenticationToken.class.equals(authentication);
  }
}

If you want to limit to a specific domain, you can do so by checking payload.getHostedDomain ().

The GoogleUser class is as follows.

GoogleUser.java


@Data
@RequiredArgsConstructor
@AllArgsConstructor
public class GoogleUser implements UserDetails {

  @NotNull
  @Setter(AccessLevel.NONE)
  private String userId;

  private String username;

  @Override
  public Collection<? extends GrantedAuthority> getAuthorities() {
    return null;
  }

  @Override
  public String getPassword() {
    return null;
  }

  @Override
  public boolean isAccountNonExpired() {
    return false;
  }

  @Override
  public boolean isAccountNonLocked() {
    return false;
  }

  @Override
  public boolean isCredentialsNonExpired() {
    return false;
  }

  @Override
  public boolean isEnabled() {
    return false;
  }
}

You are now ready for Google Sign-In.

Recommended Posts

I tried Google Sign-In with Spring Boot + Spring Security REST API
I tried "License OCR" with Google Vision API
I tried "Receipt OCR" with Google Vision API
I tried using the Google Cloud Vision API
I tried simple image processing with Google Colaboratory.
I tried Java8 + Spring Boot with GAE SE (and about its development environment)
I tried APN (remote notification) using Parse.com REST API
I tried to uncover our darkness with Chatwork API
I tried hitting the API with echonest's python client
I tried scraping with Python
Hello World with Google App Engine (Java 8) + Spring Boot + Gradle
I tried clustering with PyCaret
I tried connecting Raspberry Pi and conect + with Web API
I tried follow management with Twitter API and Python (easy)
I tried saving the DRF API request history with django-request
I tried the Google Cloud Vision API for the first time
I tried gRPC with Python
I tried scraping with python
Hello World with Google App Engine (Java 11) + Spring Boot + Gradle
I tried hitting the Google API with Ruby and Python-Make the database a Spreadsheet and manage it with Google Drive
I tried to automate everything including Google OAuth with two-step verification
I tried ChatOps with Slack x API Gateway x Lambda (Python) x RDS
I tried using docomo speech recognition API and Google Speech API in Java
Operate Nutanix with REST API Part 2
I tried to create Quip API
I tried the Naro novel API 2
I tried trimming efficiently with OpenCV
I tried machine learning with liblinear
I tried web scraping with python.
I tried moving food with SinGAN
I tried implementing DeepPose with PyTorch
I tried to touch Tesla's API
I tried face detection with MTCNN
I tried the Naruro novel API
I tried running prolog with python 3.8.2.
I tried SMTP communication with Python
I tried sentence generation with GPT-2
I tried learning LightGBM with Yellowbrick
Introducing Google Map API with rails
I tried face recognition with OpenCV
I tried using the checkio API
I tried to introduce a serverless chatbot linked with Rakuten API to Teams
I tried to delete bad tweets regularly with AWS Lambda + Twitter API
I tried to extract characters from subtitles (OpenCV: Google Cloud Vision API)
I tried to make "Sakurai-san" a LINE BOT with API Gateway + Lambda
I tried to get the authentication code of Qiita API with Python.
I tried to get the movie information of TMDb API with Python
I tried hitting Mastodon API with Ruby (Faraday) / Python (Pycurl) / PHP (Curl)
I tried updating Google Calendar with CSV appointments using Python and Google APIs
I tried multiple regression analysis with polynomial regression
I tried sending an SMS with Twilio
I tried using Amazon SQS with django-celery
Streaming speech recognition with Google Cloud Speech API
I tried to implement Autoencoder with TensorFlow
I tried linebot with flask (anaconda) + heroku
I tried to visualize AutoEncoder with TensorFlow
I tried to get started with Hy
I tried scraping Yahoo News with Python
I tried using YOUTUBE Data API V3
I tried using Selenium with Headless chrome
I tried factor analysis with Titanic data!