signin

Google Sign-In

1. Het maken van een Google Sign-In

O.a. Google biedt een service aan waarbij op een veilige manier kan worden ingelogd. Dit is veiliger dan de traditionele manier waarbij de beheerder van de website het wachtwoord en gebruikersnaam ontvangt. Normaal gesproken zou de beheerder van een website dit direct moeten encrypten zodat de gegevens niet zichtbaar worden. Dit is echter lang niet altijd het geval. Ook zijn er websites die nog niet zijn omgezet naar https en de logingegevens dus open en bloot over het internet sturen. In feite is dat niet meer van deze tijd. Tegenwoordig is een https certificaat gratis via https://letsencrypt.org/ .

De techniek die Google gebruikt heet OAuth (Open Authorisation). OAuth maakt gebruik van tokens, waardoor vertrouwelijke gegevens als een gebruikersnaam of wachtwoord niet afgegeven hoeven te worden. Elk token geeft slechts toegang tot specifieke gegevens van één website voor een bepaalde duur. Hierna kan eventueel opnieuw toegang worden gevraagd.

In deze cursus ga je leren hoe je Google Sign-In kunt toepassen in een Servlet of Maven project.

2. Het maken van een token

Het eerste wat je dient te doen is het maken van een token bij Google. Hiervoor ga je naar https://code.google.com/apis/console/ . Log in met je edictum account. Als je bent ingelogd zie je de API-bibliotheek.

https://images.computational.nl/galleries/signin/2017-02-17_14-26-35.png

Je zult nu eerst een project moeten maken in de organisatie edictum.

https://images.computational.nl/galleries/signin/2017-02-17_14-32-44.png

Geef het project de naam van je website en zorg ervoor dat je geen emails krijgt door de vragen zoals in onderstaande afbeelding te beantwoorden.

https://images.computational.nl/galleries/signin/2017-02-17_14-35-06.png

De volgende stap is het aanmaken van inloggegevens. Deze kun je links aanklikken.

https://images.computational.nl/galleries/signin/2017-02-17_14-50-25.png

Om een OAuth sleutel te maken ga je eerst naar het OAuth-toestemmingsscherm en vul je de naam in van het project.

https://images.computational.nl/galleries/signin/2017-02-17_14-58-27.png

Klik hierna op opslaan.

Hierna ga je naar de tab Inloggegevens en ga je het Id maken door te klikken op Client-ID OAuth.

https://images.computational.nl/galleries/signin/2017-02-17_14-52-49.png

Kies hierna voor Webapp.

https://images.computational.nl/galleries/signin/2017-02-17_15-00-12.png

Nu moet je wat belangrijke gegevens invullen. Je vult opnieuw de naam in van het id (er zijn binnen je project meerdere id's mogelijk), het pad van de geauthoriseerde javascripts - localhost - en wat er moet gebeuren als een gebruiker is ingelogd. Deze sturen we in onderstaande afbeelding naar de root van je project maar het zou ook een welcome-page kunnen zijn of iets dergelijks.

https://images.computational.nl/galleries/signin/2017-02-17_15-10-07.png

Als je het goed hebt gedaan krijg je nog een melding met het Id en het clientgeheim. Hierover later meer.

https://images.computational.nl/galleries/signin/2017-02-17_15-14-02.png

3. De index.jsp opbouwen

We zullen nu de code voor de index.jsp op gaan bouwen. Maak een nieuwe index.jsp en verwijder de index.html. De index.jsp ziet er als volgt uit:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
    </body>
</html>

Allereerst voegen we een javascript toe:

<script src="https://apis.google.com/js/platform.js" async defer></script>

Dit is de Google library om in te loggen.

Vervolgens maken we een metatag met het id zoals je hebt gemaakt in het API beheer. Je hebt hiervoor de Client-ID nodig. Deze vind je in de bij de inloggegevens van het API-beheer. Het handigste is om even de JSON (javascript) file te downloaden en daarna vanuit het downloadmapje naar Netbeans te slepen.

https://images.computational.nl/galleries/signin/2017-02-17_19-34-42.png

Kopieer daarna de Client Id vanuit de JSON file in de metatag zoals hieronder is getoond. De gehele jsp ziet er als volgt uit:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <meta name="google-signin-client_id" content="< jouw id >.apps.googleusercontent.com">
        <script src="https://apis.google.com/js/platform.js" async defer></script>
        <title>Google Sign In</title>
    </head>
    <body>
    </body>
</html>

Als laatste voegen we de volgende knop toe:

https://images.computational.nl/galleries/signin/2017-02-17_19-52-37.png

Door de volgende code in de body te plaatsen:

<div class="g-signin2" data-theme="dark" data-width="240" data-height="40" data-longtitle="true" data-onsuccess="sendToBackendServer"></div>

Er zijn bij deze knop verschillende thema's mogelijk.

Start nu de servlet en probeer uit of de applicatie wil inloggen. Log in met je Edictum account. Je moet dan het volgende zien:

https://images.computational.nl/galleries/signin/2017-02-17_19-55-53.png

Hierna weet je dat het werkt

4. Gegevens versturen naar de achterliggende servlet

Om gegevens te kunnen versturen naar de achterliggende servlet dien je eerst het volgende script aan de index.jsp toe te voegen. Plaats dit vlak voor de afsluitende </body> tag.

<script>
    function sendToBackendServer(googleUser) {
        var id_token = googleUser.getAuthResponse().id_token;
        var xhr = new XMLHttpRequest();
        xhr.open('POST', 'login?idtoken=' + id_token);
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.send();
        };
    }
</script>

De variabele xhr staat voor XML Http Request en valt onder de taal XML. Als je hier meer over wilt weten: Op W3Schools staat hierover een tutorial.

Maak vervolgens een servlet genaamd LoginServlet. Hierin plaats je in de methode processRequest de volgende code:

protected void processRequest(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    // Needed to correct CLIENTID != idTokenString
    final String idTokenString = request.getParameter("idtoken");
    final String CLIENTID = "< your id >.apps.googleusercontent.com";
    // Verifier that checks that the token has the proper issuer and audience
    GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(new NetHttpTransport(), new JacksonFactory())
            .setAudience(Collections.singletonList(CLIENTID)).build();

    try {
        // Needed to correct CLIENTID != idTokenString
        GoogleIdToken idToken = verifier.verify(idTokenString);
        HttpSession session = request.getSession();
        if (idToken != null) {
            Payload payload = idToken.getPayload();

            // Get profile information from payload
            String email = payload.getEmail();
            boolean emailVerified = payload.getEmailVerified();
            String name = (String) payload.get("name");
            String pictureUrl = (String) payload.get("picture");
            String familyName = (String) payload.get("family_name");
            String givenName = (String) payload.get("given_name");
            String hosteddomain = (String) payload.getHostedDomain();

            //set the session attributes
            session.setAttribute("email", email);
            session.setAttribute("emailverified", emailVerified);
            session.setAttribute("username", name);
            session.setAttribute("firstname", givenName);
            session.setAttribute("lastname", familyName);
            session.setAttribute("picture", pictureUrl);
            session.setAttribute("domain", hosteddomain);
        } else {
            session.setAttribute("message", "Invalid ID token");
        }
    } catch (GeneralSecurityException ex) {
        Logger.getLogger(LoginBack.class.getName()).log(Level.SEVERE, null, ex);
    }

}

De @WebServlet annotation laat je verwijzen naar login.

@WebServlet(name = "Login", urlPatterns = {"/login"})

Merk op dat deze servlet geen forward heeft. Omdat het een XMLHttpRequest request is hoeft dat ook niet. Google zal nu automatisch aangeven dat je bent ingelogd.

https://images.computational.nl/galleries/signin/2017-02-24_13-46-38.png

Je kunt echter nu niet uitloggen. Hiervoor zijn weer verschillende oplossingen mogelijk. Wij hebben een welcome.jsp gemaakt en laten het script daar naar verwijzen. Breid het script als volgt uit:

<script>
    function sendToBackendServer(googleUser) {
        var id_token = googleUser.getAuthResponse().id_token;
        var xhr = new XMLHttpRequest();
        xhr.open('POST', 'login?idtoken=' + id_token);
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.send();

        xhr.onreadystatechange = function () {
            if (this.readyState === 4 && this.status === 200) {
                window.location.replace("welcome.jsp");
            }
        };
    }
</script>

Zodra als het request is afgerond zal de onreadystatechange functie doorverwijzen naar welcome.jsp. Deze file hebben wij als volgt gevuld (je kunt natuurlijk volledig naar eigen inzicht doen):

<!DOCTYPE html>
<html>
    <head>
        <meta name="google-signin-client_id" content="581157098373-is4pj3ot097d1gmi8js78p9vfcs9eqnq.apps.googleusercontent.com">
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.0.1/css/foundation.min.css">

        <script src="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.0.1/js/vendor/jquery.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.0.1/js/foundation.min.js"></script>
        <title>Welcome</title>


    </head>
    <body>
        <div class="row" style="margin-top: 50px">
            <div class="small-4 medium-4 medium-centered large-4 large-centered  columns">
                <table style="text-align: left">
                    <caption>Welcome ${username}</caption>
                    <tr>
                        <th>Data fields</th>
                        <th>Value</th>
                    </tr>
                    <tr>
                        <td>Username</td>
                        <td>${username}</td>
                    </tr>
                    <tr>
                        <td>Email</td>
                        <td>${email}</td>
                    </tr>
                    <tr>
                        <td>First name</td>
                        <td>${firstname}</td>
                    </tr>
                    <tr>
                        <td>Last name</td>
                        <td>${lastname}</td>
                    </tr>
                    <tr>
                        <td>Picture</td>
                        <td><img src="${picture}"</td>
                    </tr>
                    <tr>
                        <td>Domain</td>
                        <td>${domain}</td>
                    </tr>
                    <tr>
                        <td>Sign out</td>
                        <td><a onclick="signOut();">Sign out</a></td>
                    </tr>
                </table>
            </div>
            <script>
                function signOut() {
                    var auth2 = gapi.auth2.getAuthInstance();
                    auth2.signOut().then(function () {

                        var xhr = new XMLHttpRequest();
                        xhr.open('POST', 'logout?logout=true');
                        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
                        xhr.send();

                        xhr.onreadystatechange = function () {
                            if (this.readyState === 4 && this.status === 200) {
                                window.location.replace("index.jsp");
                            }
                        };
                    });
                }

                function onLoad() {
                    gapi.load('auth2', function () {
                        gapi.auth2.init();
                    });
                }
            </script>
            <script src="https://apis.google.com/js/platform.js?onload=onLoad" async defer></script>
            <script src="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.0.1/js/vendor/what-input.min.js"></script> 
            <script>
                $(document).foundation();
            </script>
    </body>
</html>

We hebben hierbij het Foundation framework gebruikt.

5. Voorbeeldproject

Google.zip