04 February 2017

Spring Security with Google SignIn



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.

No comments:

Post a Comment