It is long time again. This time I think write some article about
Spring security with
Google Signing .Last week I have worked project which need to integrate spring security with google sign in. So I think this would help developers who using spring security. This diagram shows the use case I am going to resolve.
First step is to Add Google login button to web site. For that
this documentation shows how to add google login button to website. If you follow up this steps you can add this kind of button to your website.
After you sign-in using gmail account it will return google auth token to browser.
In your
sign.jsp page you need to add web form with hidden token field.
1
2
3
4
| <form id="custom_form"
action="<c:url value='j_spring_security_check' />" method='POST'>
<input id="formToken" type='hidden' name='token'/>
</form>
|
Next
spring-security.xml should be configured this way.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| <sec:http auto-config="true" use-expressions="true">
<sec:intercept-url pattern="/login" access="isAnonymous()" />
<sec:intercept-url pattern="/logout" access="isAnonymous()" />
<sec:intercept-url pattern="/access_denied" access="isAnonymous()" />
<sec:intercept-url pattern="/*" access="hasRole('ROLE_ADMIN')" />
<sec:form-login login-page="/login" authentication-failure-url="/access_denied"
password-parameter="token"
default-target-url="/home"/>
<sec:logout logout-success-url="/logout"/>
</sec:http>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider ref="authenticationProvider">
</sec:authentication-provider>
</sec:authentication-manager>
<bean class="dev.innova.util.CustomAuthenticationProvider" id="authenticationProvider"/>
|
Then the last part is create custom Authentication Provider to validate user google auth token.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
| import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import dev.innova.util.UserAccount;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Value("${client.id}")
private String CLIENT_ID;
private static final Logger logger = LogManager.getLogger(CustomAuthenticationProvider.class.getName());
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String token = (String) authentication.getCredentials();
NetHttpTransport transport = new NetHttpTransport();
JsonFactory mJFactory = new GsonFactory();
boolean validUser = false;
GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, mJFactory)
.setAudience(Collections.singletonList(CLIENT_ID))
.build();
UserAccount userAccount = new UserAccount();
try {
GoogleIdToken idToken = verifier.verify(token);
if (idToken != null) {
GoogleIdToken.Payload payload = idToken.getPayload();
String email = payload.getEmail();
String pictureUrl = (String) payload.get("picture");
String userId = payload.getSubject();
boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
String name = (String) payload.get("name");
userAccount.setEmailAddress(email);
userAccount.setImageUrl(pictureUrl);
validUser = isValidUser(email);
logger.info("User Details Name [{}]",name);
logger.info("User Details userId [{}]",userId);
logger.info("User Details emailVerified [{}]",emailVerified);
logger.info("User Details Image [{}]",pictureUrl);
} else {
logger.info("Invalid ID token.");
}
}catch (IllegalArgumentException exx){
logger.error("Failed {}", exx.getMessage());
}catch (GeneralSecurityException e) {
logger.error("Failed {}", e.getMessage());
} catch (IOException e) {
logger.error("Failed {}", e.getMessage());
}
if (validUser) {
logger.info("Valid User Found");
List<GrantedAuthority> grantedAuths = new ArrayList<>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
Authentication auth = new UsernamePasswordAuthenticationToken(userAccount, token, grantedAuths);
return auth;
} else {
throw new BadCredentialsException("Username not found.");
}
}
@Override
public boolean supports(Class<?> authentication) {
boolean equals = authentication.equals(UsernamePasswordAuthenticationToken.class);
return equals;
}
/**
* Mock Method to validate Email
* @param email
* @return
*/
private boolean isValidUser(String email){
return true;
}
}
|
Then we can create Custom
UserDetails to save custom data. Which will help you to save user image, extra details.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
| import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
public class UserAccount implements UserDetails {
private String userId;
private String userName;
private String emailAddress;
private String imageUrl;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return userName;
}
@Override
public boolean isAccountNonExpired() {
return false;
}
@Override
public boolean isAccountNonLocked() {
return false;
}
@Override
public boolean isCredentialsNonExpired() {
return false;
}
@Override
public boolean isEnabled() {
return false;
}
}
|
And that is it.You can login user with google account and use spring security to secure.