🎃

Googleでログインお勉強メモ3:Spring Security OAuth2/OpenID Connect Client

2021/09/27に公開

はじめに

Googleでログインする方法はいくつかあります。これは Spring Security OAuth2/OpenID Connect Client を使って実装をお試ししたメモです

Spring Security OAuth2/OpenID Connect Client

以下のことは書いてません

❌ Google Identity Services(g_id_signin)

❌Google Sign-In(g-signin2)

❌ macOS以外の実装

❌ Google One Tapについて

❌ JavaScriptでの実装

❌ Kotlin(Java)以外の実装

参考

環境

フロントはJavaScriptを使わずHTMLで構成する
バックエンドはSpring Boot & Kotlin で実装する

  • macOS Big Sur
  • Kotlin
  • Spring Boot

準備

Spring Bootプロジェクト作成

spring initializr で以下の設定でプロジェクトを作成する

  • Project
    • Gradle Project
  • Language
    • Kotlin
  • Dependencies
    • Spring Web
    • Spring Security
    • OAuth2 Client
    • Thymeleaf

クライアントID取得

Google Cloud Platform で クライアント ID, クライアント シークレットを取得する

以下の設定をする

  • 承認済みのリダイレクトURI には http://localhost:8080/login/oauth2/code/google を設定する

実装

1. Sign-In With Googleボタンを配置する

Front - HTML

ボタンをクリックしたらuserinfoに遷移するだけ

resources/templates/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
    <body>
        <button onclick="location.href='./userinfo'">Googleでログイン</button>
    </body>
</html>

application.properties

Googleでログインする色々な処理をやってくれるドライバが標準であるのでそれを使います。

  • client-id, client-secret は先程取得したクライアントID, クライアント シークレットを設定します
  • デバック用でRestTemplateのデバックログ出力するようにします(必須ではありません)
spring.security.oauth2.client.registration.google.client-id=CLIENT ID
spring.security.oauth2.client.registration.google.client-secret=CLIENT SECRET

logging.level.org.springframework.web.client.RestTemplate=DEBUG

Backend - Kotlin

上記 index.html を表示するコントローラを作成する

RootController.kt

import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping

@Controller
class RootController {
    @GetMapping("/")
    fun index(): String {
        return "index"
    }
}

Spring Securityの基本設定

  • oauth2Login()と指定するのがポイント

SecurityConfig.kt

import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter

@EnableWebSecurity
class SecurityConfig: WebSecurityConfigurerAdapter() {
    override fun configure(http: HttpSecurity) {
        http
            .authorizeRequests()
            .mvcMatchers("/").permitAll()
            .anyRequest().authenticated()
            .and()
            .oauth2Login()
    }
}

動作確認

この時点でGoogleでログインは動作します。(ログイン完了後のuserinfoページが無いのでエラーになります)

2. ログイン後ページを実装する

ログインしたユーザー情報を表示するページを実装します

Front - HTML

resources/templates/userinfo.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>UserInfo</title>
</head>
<body>
    <div>subject(User ID):<span th:text="${subject}"></span></div>
    <div>email:<span th:text="${email}"></span></div>
    <div>emailVerified:<span th:text="${emailVerified}"></span></div>
    <div>fullName:<span th:text="${fullName}"></span></div>
    <div>picture:<span th:text="${picture}"></span></div>
    <div>locale:<span th:text="${locale}"></span></div>
    <div>familyName:<span th:text="${familyName}"></span></div>
    <div>givenName:<span th:text="${givenName}"></span></div>

    <a href="/">Back</a>
</body>
</html>

Backend - Kotlin

コントローラに@AuthenticationPrincipalつけたOidcUser型の引数をつけるとSpring SecurityがそこにOpenID Connectで取得したユーザー情報をインジェクションしてくれます。

UserInfoController.kt

import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.security.oauth2.core.oidc.user.OidcUser
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping

@Controller
class UserInfoController {
    @GetMapping("userinfo")
    fun userInfo(
        @AuthenticationPrincipal user: OidcUser,
        model: Model
    ): String {
        model.addAttribute("subject", user.subject)
        model.addAttribute("email", user.email)
        model.addAttribute("emailVerified", user.emailVerified)
        model.addAttribute("fullName", user.fullName)
        model.addAttribute("picture", user.picture)
        model.addAttribute("locale", user.locale)
        model.addAttribute("familyName", user.familyName)
        model.addAttribute("givenName", user.givenName)
        return "userinfo"
    }
}

動作確認

Googleでログインすると Backend の UserInfoController.userinfo()が実行された後 userinfo.html に遷移する

3. OpenID Connect フロー

通信ログからフローを確認する

  • Browser = Chrome
  • App = Demo Application
  • Spring = Spring Security OAuth2/OpenID Connect Client
No
Googleでログイン をクリック
1 Browser App GET http://localhost:8080/userinfo
Spring が Intercept
2 Browser Spring 302 Redirect
3 Browser Spring GET http://localhost:8080/oauth2/authorization/google
4 Browser Spring 302 Redirect
5 Browser Google GET https://accounts.google.com/o/oauth2/v2/auth
? response_type = code
& client_id = CLIENT ID
& scope = openid%20profile%20email
& state = aLyFZaLUCjSDXow9bgBHQ6j57Km65o_xRzAAQirQBos%3D
& redirect_uri = http://localhost:8080/login/oauth2/code/google
& nonce = WHjyWFrgf1TEI8MlW2Iwl0qin-zinN-NKulDoIZSLlc
6 Google Google 200 OK (text/html)
Google の ログイン画面を表示
7 Browser
Google ユーザー認証に関わるなんだかんだのハンドシェイク
8 Browser Google GET https://accounts.google.com/signin/oauth/consent
9 Browser Google 302 Redirect (codeを取得)
10 Browser Spring GET http://localhost:8080/login/oauth2/code/google
? state = aLyFZaLUCjSDXow9bgBHQ6j57Km65o_xRzAAQirQBos%3D
& code = 4%2F0AX4XfWjqfcX41J8dvHfO_ZcN51mkhCFwKWKyo6L85v2dxVQNFL75KI4jQibh9RfnrJVbfQ
& scope = email+profile+openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email
& authuser = 0
& prompt = consent
11 Spring google POST https://www.googleapis.com/oauth2/v4/token
grant_type = [authorization_code]
code = [4/0AX4XfWjqfcX41J8dvHfO_ZcN51mkhCFwKWKyo6L85v2dxVQNFL75KI4jQibh9RfnrJVbfQ]
redirect_uri = [http://localhost:8080/login/oauth2/code/google]}
12 Spring google 200 OK (ID Tokenを取得)
13 Spring google GET https://www.googleapis.com/oauth2/v3/certs
14 Spring google 200 OK (JWT検証をするための公開鍵を取得)
ID Token(JWT)を検証している(と思われる)
15 Browser Spring 302 Redirect
16 Browser App GET http://localhost:8080/userinfo
17 Browser App 200 OK
UserInfo画面を表示

おつかれさまでした

  • Spring Security OAuth2/OpenID Connect ClientはGoogleのAPIを使わずSpringのドライバを使う
  • OpenID Connectでやり取りする
  • 実装は非常に簡単

Discussion