Google Sign-In with Spring Security (no Spring Boot)

Google Sign-In with Spring Security (no Spring Boot) Spring Boot is a useful library when you need to do something quick and standard. I already described how easy is to use Google Sign-In in an application based on Spring Boot. It works great for standard use cases, but when you are trying explore regions outside the box, it becomes more and more difficult. I have recently reached that point in one of my apps and I would like to use it as an opportunity to show you how to do the same without Spring Boot. Just with Spring Security.

login no bootSpring Boot is a useful library when you need to do something quick and standard. I already described how easy is to use Google Sign-In in an application based on Spring Boot. It works great for standard use cases, but when you are trying explore regions outside the box, it becomes more and more difficult. I have recently reached that point in one of my apps and I would like to use it as an opportunity to show you how to do the same without Spring Boot. Just with Spring Security.

 

Libraries in Gradle build configuration

Similarly to the version with Spring Boot, two libraries are needed besides Spring Security: Spring Security OAuth2 and Spring Cloud Security.

dependencies {
compile("org.springframework.security:spring-security-core:4.2.0.RELEASE")
compile("org.springframework.security.oauth:spring-security-oauth2:2.0.11.RELEASE")
compile("org.springframework.cloud:spring-cloud-security:1.1.3.RELEASE")
}

Of course if you have Spring Boot Starter Security anyway, it will also work. Adding Spring Security Core to the build configuration is not needed in that case.

 

Google SSO filter

Previously most of the boiler plate code was covered by the @EnableOAuth2Sso annotation. Without Spring Boot, that code you have to write yourself. This is how my WebSecurityConfiguration looks after the changes:

@SpringBootApplication
@EnableOAuth2Client
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

@Autowired
private OAuth2ClientContext oauth2ClientContext;

@Bean
@ConfigurationProperties("security.oauth2.client")

public AuthorizationCodeResourceDetails google() {
return new AuthorizationCodeResourceDetails();
}

@Bean
@ConfigurationProperties("security.oauth2.resource")

public ResourceServerProperties googleResource() {
return new ResourceServerProperties();
}

@Bean
public FilterRegistrationBean oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {

FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}

private Filter ssoGoogleFilter() {
OAuth2ClientAuthenticationProcessingFilter googleFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/google");
OAuth2RestTemplate googleTemplate = new OAuth2RestTemplate(google(), oauth2ClientContext);
googleFilter.setRestTemplate(googleTemplate);
googleFilter.setTokenServices(new UserInfoTokenServices(googleResource().getUserInfoUri(), google().getClientId()));
return googleFilter;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/index.html")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login/google")
.and()
.addFilterBefore(ssoGoogleFilter(), BasicAuthenticationFilter.class);
}

}

As you can see I the @EnableOAuth2Sso annotation was replaced with @EnableOAuth2Client that comes from the Spring Security OAuth2 library. It only does some basic configuration and it does not do the whole functionality provided by @EnableOAuth2Sso. The work delivered with the latter still has to be done. Three beans are created based on SSO configuration that will be described later. They are needed to create a security filter by ssoGoogleFilter(). The filter is added to the filter chain by calling the addFilterBefore method.

 

OAuth2 configuration

OAuth2 configuration in the application.yml file stays as it was which is:

security:
oauth2:
client:
clientId: aaaaaaaabbbbbbbbbbbbcccccccccc.apps.googleusercontent.com
clientSecret: 111122223333334444445555
accessTokenUri: https://www.googleapis.com/oauth2/v3/token
userAuthorizationUri: https://accounts.google.com/o/oauth2/auth
tokenName: oauth_token
authenticationScheme: query
clientAuthenticationScheme: form
scope: profile
resource:
userInfoUri: https://www.googleapis.com/userinfo/v2/me
preferTokenInfo: false

Notice that the paths in @ConfigurationProperties must match those from this file.

 

The final effect

After applying the code above to my application, a not logged in user can still access index.html. But when any other page is accessed e.g. tickets.html, Spring Security checks HttpSecurity configuration and based on it, tickets.html requires authentication. The user is not yet, authenticated so it is redirected to the login page which is set to /login/google by the loginPage method. Yes, I know, I have not mentioned about creating such page. It is because I have not created one. The security filter created inside ssoGoogleFilter takes a special treatment of requests to /login/google. Instead of going to this not existing page, the user is redirected to the Google Sign-In page for authentication based on the configuration from the application.yml file.

Cool, isn't it?