Googleでログインお勉強メモ3:Spring Security OAuth2/OpenID Connect Client
はじめに
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 | → | 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 | ← | 200 OK (text/html) | ||
Google の ログイン画面を表示 | ||||
7 | Browser | → ← |
ユーザー認証に関わるなんだかんだのハンドシェイク | |
8 | Browser | → | GET https://accounts.google.com/signin/oauth/consent | |
9 | Browser | → | 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 | → | 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 | ← | 200 OK (ID Tokenを取得) | |
13 | Spring | → | GET https://www.googleapis.com/oauth2/v3/certs | |
14 | Spring | ← | 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