3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SpringBootアプリケーションでAuth0を試してみた【kotlin】

Last updated at Posted at 2021-07-29

世界の全てをKotlinで

最近Windows Serviceをkotlin nativeで書いてみたりとかアホみたいな事をやっている程度にkotlinを愛してやまない私としては、取り敢えずkotlinでも(やっぱり)楽勝でAuth0を使えました、という内容で記事を書いてみる事にする。Auth0のサイトで丁寧にJava+SpringBootの説明があるから楽勝なのは当たり前なんだが。

この記事は、アマギフ目当てでエンジニアフェスタ2021に参加している。

かいつまんで言うと、素敵記事を書いた2名に25000円分のアマギフくれるって話っぽいのよね。で、現在参加者11名だって。これダーツで決めるなら俺もワンチャンあんだけどな。

まずはAuth0の無料トライアルでダッシュボードの表示

ダッシュボードからアプリケーションの作成をする

メニューのApplicationsを選択して、右上のCreate applicationボタンをクリック。

image.png

出来ました、と。
image.png

次からの手順で作るファイルはGithubに置いてます

設定ファイル(application.yml)だけ編集すれば使える形。
これを使う人は Auth0側の設定 までジャンプしてどうぞ。

じゃあ作ってみよう

取り敢えずbuild.gradle

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ファイル

image.png

こんな感じ。
kotlinのソースが4ファイル、htmlと設定ファイル。

SpringBootのメイン

MainActivator.kt
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をつけてやるのがコツ。

ログアウトハンドラ

LogoutHandler.kt
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になってるだけ。

ログインとログアウトが出来るようにする

SecurityConfig.kt
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をマッピング

MainController.kt
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を配置。

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側の設定

ダッシュボードのSettingsの画面を開く。
image.png

ここの"Domain", "Client ID", "Client Secret" をapplication.ymlに記述する。

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 URIsAllowed Callback URLsAllowed Logout URLsにそれぞれ、以下のURLを追加する。

以下の画面で怒られるのは、大体ここのURLの設定ミス。
image.png

早速動かしてみる

./gradlew bootRunで起動。
image.png

起動したら、ブラウザで http://localhost:8080 にアクセスする。

image.png
という事で、ログインリンクを踏んでみると。

image.png

取り合えずGoogleの認証が使えのか。ふむふむ。
早速試しにGoogleアカウントでログインしてみる。
image.png

ログイン画面の見た目を触ってみる

メニューからUniversal Loginで、LoginタブからCustomize Login Pageのスイッチをオンにする。
image.png

下のHTMLを触れるようになるので、28行目と58行目を編集する。

  • 28行目
    var language; → var language='ja';
  • 58行目
    logo:にロゴ画像のURLを設定

※今回はいらすとやさんの画像をお借りしてEasyUploaderさんに画像を乗っけさせてもらった。
image.png

Save Changesで保存。
image.png

日本語になってロゴも変わった。
image.png

やってみた感想

正直、この記事を書く方が時間が掛かったくらいにハマリどころが無かった。
導入方法はQuick Startに丁寧に書かれているので、躓くポイントも特にない。
JavaをKotlinにしたのは殆どIntelli Jがやってくれたので私は何もしていない気がするが、逆に言えば殆ど何もしなくても信頼できる認証をお手軽に組み込めるという事でいいんじゃないかな。多分。

デフォルトでブルートフォースアタックとかボットやスクリプトの攻撃に対してバリア貼ってくれてるのが嬉しい。

ちょっと機能を見てたら、Magic linkとか確認コード送ったりとかSMSもあるみたい。
image.png

こんなん自分トコで実装して評価してとかやってたらアホみたいに予算が飛んでいくからね。「認証機能に1人月かかります」とか見積もり出されたら、「こういうものがあるようなので研究してみてください」と言える程度には理解できたような気がする私なのでした。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?