世界の全てをKotlinで
最近Windows Serviceをkotlin nativeで書いてみたりとかアホみたいな事をやっている程度にkotlinを愛してやまない私としては、取り敢えずkotlinでも(やっぱり)楽勝でAuth0を使えました、という内容で記事を書いてみる事にする。Auth0のサイトで丁寧にJava+SpringBootの説明があるから楽勝なのは当たり前なんだが。
この記事は、アマギフ目当てでエンジニアフェスタ2021に参加している。
かいつまんで言うと、素敵記事を書いた2名に25000円分のアマギフくれるって話っぽいのよね。で、現在参加者11名だって。これダーツで決めるなら俺もワンチャンあんだけどな。
まずはAuth0の無料トライアルでダッシュボードの表示
ダッシュボードからアプリケーションの作成をする
メニューのApplicationsを選択して、右上のCreate applicationボタンをクリック。
次からの手順で作るファイルはGithubに置いてます
設定ファイル(application.yml)だけ編集すれば使える形。
これを使う人は Auth0側の設定 までジャンプしてどうぞ。
じゃあ作ってみよう
取り敢えずbuild.gradle
buildscript {
ext.kotlin_version = '1.5.30-M1'
ext.springboot_version = '2.5.3'
repositories {
mavenCentral()
}
}
plugins {
id 'org.jetbrains.kotlin.jvm' version "$kotlin_version"
id 'org.springframework.boot' version "$springboot_version"
}
apply plugin: 'kotlin'
repositories {
mavenCentral()
}
dependencies {
implementation(
"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
, "org.springframework.boot:spring-boot-starter-web:$springboot_version"
, "org.springframework.boot:spring-boot-starter-oauth2-client:$springboot_version"
, "org.springframework.boot:spring-boot-starter-thymeleaf:$springboot_version"
, 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.0.4.RELEASE'
)
}
作るファイルは6ファイル
こんな感じ。
kotlinのソースが4ファイル、htmlと設定ファイル。
SpringBootのメイン
package monte
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer
@SpringBootApplication
open class MainActivator : SpringBootServletInitializer() {
override fun configure(application: SpringApplicationBuilder): SpringApplicationBuilder {
return application.sources(MainActivator::class.java)
}
companion object {
@JvmStatic
fun main(args: Array<String>) {
SpringApplication.run(MainActivator::class.java, *args)
}
}
}
クラスにopenをつけてやるのがコツ。
ログアウトハンドラ
package monte
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.core.Authentication
import org.springframework.security.oauth2.client.registration.ClientRegistration
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler
import org.springframework.stereotype.Controller
import org.springframework.web.servlet.support.ServletUriComponentsBuilder
import org.springframework.web.util.UriComponentsBuilder
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
@Controller
class LogoutHandler
@Autowired constructor(private val clientRegistrationRepository: ClientRegistrationRepository) :
SecurityContextLogoutHandler() {
override fun logout(
httpServletRequest: HttpServletRequest, httpServletResponse: HttpServletResponse,
authentication: Authentication
) {
super.logout(httpServletRequest, httpServletResponse, authentication)
httpServletResponse.sendRedirect(UriComponentsBuilder
.fromHttpUrl("${clientRegistration.providerDetails.configurationMetadata["issuer"]}v2/logout?client_id={clientId}&returnTo={returnTo}")
.encode()
.buildAndExpand(clientRegistration.clientId, ServletUriComponentsBuilder.fromCurrentContextPath().build().toString())
.toUriString())
}
private val clientRegistration: ClientRegistration
get() = clientRegistrationRepository.findByRegistrationId("auth0")
}
これはもう殆どサンプルそのまんま。javaがkotlinになってるだけ。
ログインとログアウトが出来るようにする
package monte
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.web.util.matcher.AntPathRequestMatcher
@Configuration
open class SecurityConfig(private val logoutHandler: LogoutHandler) : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http.oauth2Login()
.and().logout()
.logoutRequestMatcher(AntPathRequestMatcher("/logout"))
.addLogoutHandler(logoutHandler)
}
}
これもクラスにopenをつけるのがコツ。
ルートで押し返すhtmlをマッピング
package monte
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 MainController {
@GetMapping("/")
fun home(model: Model, @AuthenticationPrincipal principal: OidcUser?): String {
model.addAttribute("message", "ログインできたら表示")
principal?.let {
model.addAttribute("profile", it.claims);
}
return "hello"
}
}
最後にHTMLを作成
resources/templatesを掘って、hello.htmlを配置。
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<meta charset="UTF-8" />
<title>Tried Auth0</title>
</head>
<body>
<div sec:authorize="!isAuthenticated()">
<!-- ログインしてなければログインのリンクを表示-->
<a th:href="@{/oauth2/authorization/auth0}">Log In</a>
</div>
<div sec:authorize="isAuthenticated()">
<!-- ログインしてたらプロフィールとログアウトのリンクを表示-->
<h1 th:text="${message}"></h1>
<img th:src="${profile.get('picture')}" th:attr="alt=${profile.get('name')}"/>
<h2 th:text="${profile.get('name')}"></h2>
<p th:text="${profile.get('email')}"></p>
<a th:href="@{/logout}">Log Out</a>
</div>
</body>
</html>
Auth0側の設定
ここの"Domain", "Client ID", "Client Secret" をapplication.ymlに記述する。
spring:
security:
oauth2:
client:
registration:
auth0:
client-id: 【ここ】
client-secret: 【ここ】
scope:
- openid
- profile
- email
provider:
auth0:
# trailing slash is important!
issuer-uri: https://【ここ】/
GithubのOAuth2.0認証でもこんなの書いたな。
ほんでSettingsの画面をグルグルグルっと下にスクロールして、Application URIsのAllowed Callback URLsとAllowed Logout URLsにそれぞれ、以下のURLを追加する。
- Application URIs
-- http://localhost:8080/login/oauth2/code/auth0 - Allowed Logout URLs
-- http://localhost:8080
早速動かしてみる
起動したら、ブラウザで http://localhost:8080 にアクセスする。
取り合えずGoogleの認証が使えのか。ふむふむ。
早速試しにGoogleアカウントでログインしてみる。
ログイン画面の見た目を触ってみる
メニューからUniversal Loginで、LoginタブからCustomize Login Pageのスイッチをオンにする。
下のHTMLを触れるようになるので、28行目と58行目を編集する。
- 28行目
var language; → var language='ja'; - 58行目
logo:にロゴ画像のURLを設定
※今回はいらすとやさんの画像をお借りしてEasyUploaderさんに画像を乗っけさせてもらった。
やってみた感想
正直、この記事を書く方が時間が掛かったくらいにハマリどころが無かった。
導入方法はQuick Startに丁寧に書かれているので、躓くポイントも特にない。
JavaをKotlinにしたのは殆どIntelli Jがやってくれたので私は何もしていない気がするが、逆に言えば殆ど何もしなくても信頼できる認証をお手軽に組み込めるという事でいいんじゃないかな。多分。
デフォルトでブルートフォースアタックとかボットやスクリプトの攻撃に対してバリア貼ってくれてるのが嬉しい。
ちょっと機能を見てたら、Magic linkとか確認コード送ったりとかSMSもあるみたい。
こんなん自分トコで実装して評価してとかやってたらアホみたいに予算が飛んでいくからね。「認証機能に1人月かかります」とか見積もり出されたら、「こういうものがあるようなので研究してみてください」と言える程度には理解できたような気がする私なのでした。