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.
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
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");
}
}
200
instead of 301
on successful loginBy 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);
}
}
401
instead of 302
on login failureSimilarly, if login fails, simply return 401
.
You can use Spring's SimpleUrlAuthenticationFailureHandler
as it is.
200
when logging outSimilarly, 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();
}
}
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
/ loginwith
x-www-form-urlencoded. The parameter name is
google_id_token`.
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.
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