2025年5月3日土曜日

Keycloak の Admin UI の開発環境を整える

やりたいこと

Keycloak の Admin Console に上手く動かない箇所があった ので、自分で直すために環境づくりをする。

前提

  • Docker インストール済み
  • docker exec コマンドが使えること

開発環境の起動

今回はすべての作業をコンテナ上で行う。

必要に応じてバインドマウントするなり、ベアメタルで環境構築するなりしてください。

docker run -it --rm -p "8080:8080" -p "5174:5174" ubuntu:24.04

必要なパッケージのインストール

Keycloak のビルド・実行には Node.js と Java が必要なのでインストール。

その他、開発に必要なものとして curl, git, vim をインストールする。

いろいろ

apt update
DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends curl git vim

OpenJDK

apt install -y openjdk-17-jdk-headless

NodeJS & pnpm

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash
\. "$HOME/.nvm/nvm.sh"
nvm install 22
npm install -g pnpm

Keycloak のクローン

cd ~/
git clone --depth 1 https://github.com/keycloak/keycloak.git

開発用サーバーを起動

フロントエンドの開発用サーバーを起動

別ターミナルで開発用サーバーを実行。

docker exec -it 72cc1a87a306 bash
cd ~/keycloak/js/apps/admin-ui/
pnpm install
pnpm dev

これだけでは動作確認ができないので、後述の「バックエンドの開発用サーバー」と連携させる必要がある。

バックエンドの開発用サーバーを起動

別ターミナルで開発用サーバーを実行。

docker exec -it 72cc1a87a306 bash
cd ~/keycloak/js/apps/keycloak-server/
pnpm start --admin-dev

これで、先ほど起動した admin-ui の開発サーバーといい感じに連携して、 admin-ui のソース更新を反映してくれるようになる。

ここまでの動作確認

ブラウザで http://localhost:8080 にアクセスすると、 Keycloak のログイン画面になる admin/admin でログインできる。

フロントエンドの修正

試しに Welcome to keycloak の文字列を変更してみる。

vim ~/keycloak/js/apps/admin-ui/src/dashboard/Dashboard.tsx

ファイルを開いたら、 t("welcomeTo", { realmDisplayInfo }) となっている箇所を "Customized!!!!!" に変更する。

変更が反映され、 Welcome to Keycloak と書かれていた場所が Customized!!!!! になっているのがわかる。

以上。

参考資料

2025年4月29日火曜日

Keycloak で SAML のクライアント証明書を設定する

やりたいこと

こんなことをしたときに、

こんなことがありました。

Bug: SAML Client Signing Certificate Not Stored via Admin Console (26.1.3) · keycloak/keycloak · Discussion #39232 にワークアラウンドが記載されていたので試す。

前提

Spring Boot で SAML 認証をする(署名無しバージョン) - mikoto2000 の日記 を実施済み。

やったこと

  1. myrealm の適切なユーザーにロール admin を割り当てる(今回は mikoto ユーザーに admin ロールを割り当てた)

  2. keycloak が起動しているコンテナに接続

    docker exec -it f29b1a6f3f70 bash
  3. kcadm.sh にパスを通す

    export PATH=$PATH:/opt/keycloak/bin/
  4. mikotomyrealm にログイン

    kcadm.sh config credentials --server http://localhost:8080 --realm master --user mikoto --password <PASSWORD>
  5. クライアント saml-sp の id を確認

    • JSON から頑張って探す
    • 今回は dafe91db-651f-4728-90f0-0faf2228fa38 だった
    kcadm.sh get clients --server http://localhost:8080 --realm myrealm
  6. saml-sp にクライアント証明書を設定

    • MIID...(略) の部分をクライアント証明書の文字列に置き換える
    kcadm.sh update clients/dafe91db-651f-4728-90f0-0faf2228fa38 --server http://localhost:8080 --realm myrealm -s 'attributes={"saml.signing.certificate": "MIID...(略)"}'
  7. 適用されたかの確認

    • saml.signing.certificate が更新されていれば OK.
    kcadm.sh get clients/dafe91db-651f-4728-90f0-0faf2228fa38 --server http://localhost:8080 --realm myrealm

動作確認

Keycloak の myrealm -> Clients -> saml-sp -> Keys -> Signing keys config -> Client signeture requiredOn にしても、 Spring Boot の SAML 認証が通るようになった。 OK.

2025年4月7日月曜日

devcontainer.vim で Claude Code を使う

AI コーディングというやつをやってみたかったのです。

前提

  • OS: Windows 11 Pro 23H2 ビルド 22631.5039
  • Docker Desktop: Version 4.37.1 (178610)
  • WSL2 の Ubuntu 24.04 から WSL Integration で Docker Desktop を利用
  • Java プロジェクトを作って、 Hello,World! するのが目標
  • Claude Code のアカウントを作って $5 入金済み

devcontainer 用のファイル作成

.devcontainer/devcontainer.json

  • Maven の Java プロジェクトを作りたいので、 devcontainer の features を利用してインストール
  • Claude Code に Node が必要なので、 devcontainer の features を利用してインストール
{
  "name": "app",
  "image": "mcr.microsoft.com/devcontainers/base",
  "features": {
    "ghcr.io/devcontainers/features/node:1": {},
    "ghcr.io/devcontainers/features/java:1": {
      "version": "21",
      "installGradle": "false",
      "installMaven": "true"
    }
  }
}

.devcontainer/devcontainer.vim.json

  • devcontainer.vim config -g > .devcontainer/devcontainer.vim.json で生成したものそのまま使う
{
  "remoteEnv": {
    "EDITOR": "~/squashfs-root/AppRun",
    "PAGER": "sed -r 's/\\x1B\\[[0-9;]*[mGKH]//g' | ~/squashfs-root/AppRun -R -",
    "LESSCHARSET": "utf-8",
    "SHELL": "bash",
    "TERM": "xterm-256color",
    "HISTCONTROL": "erasedups",
    // If use WSLG
    // "DISPLAY": "${localEnv:DISPLAY}",
    // "WAYLAND_DISPLAY": "${localEnv:WAYLAND_DISPLAY}",
    // "XDG_RUNTIME_DIR": "${localEnv:XDG_RUNTIME_DIR}",
    // "PULSE_SERVER": "${localEnv:PULSE_SERVER}",
  },
  // devcontainer/cli はまだ forwardPorts に対応していないため、
  // 必要に応じて forwardPorts の定義を appPort に転記する。
  // ※ コンテナ側で Listen する際は、 `127.0.0.1` **ではなく** `0.0.0.0` で Listen すること。
  // "appPort": [
  // ],
  // Linux で実行する場合には、 runArgs をコメントアウトし、コンテナからホストへの接続ができるようにしてください
  //"runArgs": [
  //  "--add-host=host.docker.internal:host-gateway"
  //],
  "mounts": [
    {
      "type": "bind",
      "source": "${localEnv:HOME}/.vim",
      "target": "/home/vscode/.vim"
    },
    {
      "type": "bind",
      "source": "${localEnv:HOME}/.gitconfig",
      "target": "/home/vscode/.gitconfig"
    },
    {
      "type": "bind",
      "source": "${localEnv:HOME}/.ssh",
      "target": "/home/vscode/.ssh"
    },
    // If use host's bashrc
    //{
    //  "type": "bind",
    //  "source": "${localEnv:HOME}/.bashrc",
    //  "target": "/home/vscode/.bashrc"
    //},
    // If use WSLG
    //{
    //  "type": "bind",
    //  "source": "/tmp/.X11-unix",
    //  "target": "/tmp/.X11-unix"
    //},
    //{
    //  "type": "bind",
    //  "source": "/mnt/wslg",
    //  "target": "/mnt/wslg"
    //},
  ],
  // denops など、別の実行環境が必要な場合や、
  // 後乗せで追加したいツールがある場合には以下の対象行をコメントアウトするか
  // https://containers.dev/features から必要な feature を探して追加してください。
  //"features": {
  //  "ghcr.io/devcontainers-community/features/deno:1": {}
  //  "ghcr.io/devcontainers/features/node:1": {}
  //  "ghcr.io/devcontainers/features/python:1": {}
  //  "ghcr.io/devcontainers/features/ruby:1": {}
  //  "ghcr.io/devcontainers-extra/features/fzf:1": {}
  //  "ghcr.io/jungaretti/features/ripgrep:1": {}
  //  "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {},
  //  "ghcr.io/devcontainers/features/docker-in-docker:2": {}
  //}
}

開発環境の起動

devcontainer.vim start .

Claude Code のインストール・初期設定

インストール

Vim でターミナルを開き、以下コマンドを実行。

npm install -g @anthropic-ai/claude-code

初期設定

Vim でターミナルを開き、以下コマンドを実行。

cloude
  1. 表示のカラースタイルを聞かれるので答える(僕は Light text にした)
  2. ログイン処理 2.1 Enter 押下で表示される URL にアクセスし、ログインを行う 2.2 ログイン完了後に表示されるコードをターミナルにペーストし、 Enter
  3. Yes, proceed まで Enter を押し続ける
  4. プロンプト入力画面が表示される

あとはここにやって欲しいことを入力していくだけ。

実装

プロジェクトの作成

claude へ次の命令を入力する。

Maven の Java プロジェクトを作り、 Hello, World! を表示したい。 Java のバージョンは 21 とすること。

今回は、以下のような処理を行ってくれた。

  1. mkdir -p src/main/java/com/example

  2. pom.xml の作成

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.example</groupId>
        <artifactId>hello-world</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <properties>
            <maven.compiler.source>21</maven.compiler.source>
            <maven.compiler.target>21</maven.compiler.target>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-jar-plugin</artifactId>
                    <version>3.3.0</version>
                    <configuration>
                        <archive>
                            <manifest>
                                <mainClass>com.example.App</mainClass>
                            </manifest>
                        </archive>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
  3. App.java の作成

    package com.example;
    
    public class App {
        public static void main(String[] args) {
            System.out.println("Hello, World!");
        }
    }
  4. コンパイル(mvn compile)

  5. 実行(mvn exec:java -Dexec.mainClass="com.example.App")

  6. 完了報告(Maven プロジェクトが正常に作成され、「Hello, World!」が表示されました。Java 21 を使用する Maven プロジェクトの基本構造を作成しました。)

良い感じ。

devcontainer.vim 特有のことが無かった気がするけれど、 Claude Code が使えたのでヨシ!

以上。

参考資料

2025年4月6日日曜日

Go 言語で MCP サーバーを作り、 Claude Desktop で利用する

前提

  • OS: Windows 11 Pro 24H2
  • Claude Desktop インストール済み
  • Go 言語の開発環境構築済み

MCP サーバーの実装

mark3labs/mcp-go を使用して、 Hello, World! を返却するツールを実装した。

プロジェクト初期化とライブラリのインストール

以下コマンドで環境構築完了。

go mod init mikoto2000.dev/study/mcp
go get github.com/mark3labs/mcp-go
go get github.com/mark3labs/mcp-go/server
go get github.com/mark3labs/mcp-go/mcp

ソースコード

main.go:

package main

import (
    "context"
    "fmt"

    "github.com/mark3labs/mcp-go/mcp"
    "github.com/mark3labs/mcp-go/server"
)

func main() {
    // サーバーの生成
    s := server.NewMCPServer("hello-mcp", "1.0.0")

    // ツールの仕様生成
    tool := mcp.NewTool("hello_world",
        mcp.WithDescription("Say `Hello, World!`"),
    )

    // サーバーにツールを追加
    s.AddTool(tool, helloHandler)

    // サーバーの開始
    if err := server.ServeStdio(s); err != nil {
        fmt.Printf("Server error: %v\n", err)
    }
}

// `Hello, World!` を返却するツールのリクエスト受信ハンドラ
func helloHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
    return mcp.NewToolResultText("Hello, World!"), nil
}

ビルド

go build

Claude Desktop の設定

サーバーの配置

先ほどビルドした mcp.exe を、任意の場所に配置する。

今回は C:\Users\mikoto\tmp\mcp.exe に配置した。

MCP サーバーの設定

  1. Claude Desktop を開く

  2. 左上のハンバーガーアイコンをクリック -> ファイル -> 設定 -> 開発者 -> 構成を編集

  3. エクスプローラーが開くので、その中にある claude_desktop_config.json を編集

    {
      "mcpServers": {
        "hello-mcp": {
          "command": "C:\\Users\\mikoto\\tmp\\mcp.exe",
          "args": []
        }
      }
    }
  4. Claude Desktop を再起動する

呼び出してみる

新しいチャットを開始し、

「hello-mcp を呼び出して結果を教えて」と入力する。

hello-world(ローカル)からのツールを許可しますか?と聞かれるので このチャットで許可する を選択。

そうすると、しばらくぐるぐるした後以下のような返答が返ってきた。 OK.

関数の実行結果は「Hello, World!」でした。ご要望通りに関数を実行し、その結果をお知らせしました。何か他にお手伝いできることがありましたら、お気軽にお尋ねください。

参考資料

2025年3月26日水曜日

Windows で Gitea を試す

オンプレに Git サーバーを立てる機運だったので、 なるだけ簡単、かつ、Windows サーバーで動くやつとして Gitea を試す。

前提

  • OS: Windows 11 Pro 23H2 ビルド 22631.5039
  • Gitea: 1.23.5

ダウンロード

Gitea Official Website から Windows 版のバイナリをダウンロード。

名前を gitea.exe に変更し、 C:\gitea\gitea.exe に配置する。

初期設定

サーバーを起動し、アクセスする

以下コマンドで gitea サーバーを立ち上げ http://localhost:3000 へアクセスする。 初回アクセス時に初期設定を行うための画面が表示される。

.\gitea.exe web

初期設定入力

今回は以下の通り入力した。下記項目以外はデフォルト。

入力したら Giteaをインストール ボタンを押下。

必須設定

項目
データベースのタイプ SQLite3
パス C:\gitea\data\gitea.db
サイトタイトル Mikoto’s Gitea
リポジトリのルートパス C:\gitea\data\gitea-repositories
Git LFS ルートパス C:\g itea\data\lfs
実行ユーザー名 mikoto
サーバードメイン mnmain
SSHサーバーのポート 8022
Gitea HTTPポート 3000
GiteaのベースURL http://mnmain:3000/
ログの保存先パス C:\gitea\log

オプション設定 - サーバーと外部サービスの設定

項目
OpenIDを使ったサインインを有効にする チェックを外す
ページ閲覧にサインインが必要 チェックを入れる

オプション設定 - 管理者アカウントの設定

項目
管理者ユーザー名 mikoto
メールアドレス mikoto2000@gmail.com
パスワード ひみつ
パスワード確認 ひみつ

しばらくすると、管理者でログインした画面が表示されるが、ベース URL が違うため、改めて http://mnmain:3000 にアクセスしなおす。

HTTPS 設定

証明書の生成

なんと、自己証明書も作れちゃうらしい。

cd c:\gitea
.\gitea.exe cert --host mnmain --ca

設定ファイルの編集

C:\gitea\custom\conf\app.ini に設定ファイルがあるので、これを編集する。

今回編集するのは server セクション。

  • ROOT_URLhttphttps に変更
  • 以下 3 行を追加
    • PROTOCOL = https
    • CERT_FILE = C:\gitea\cert.pem
    • KEY_FILE = C:\gitea\key.pem

設定が完了したらもう一度 .\gitea.exe web を起動して https://mnmain:3000 にアクセス。

警告が出るが無視して進むと https で接続できる。

SSH サーバーの設定

Windows で実行する場合は、 Gitea 組み込みの SSH サーバーを使用するのが無難そうなので、その設定を行う。

組み込み SSH サーバーの有効化

C:\gitea\custom\conf\app.ini を編集する。

編集するのは SERVER セクション。 以下項目を追加する。

  • START_SSH_SERVER = true
    • 組み込み SSH サーバーを起動する

新規登録の禁止化

C:\gitea\custom\conf\app.ini を編集する。

DISABLE_REGISTRATIONtrue にする。

Windows サービス化

サービス化用に設定変更

C:\gitea\custom\conf\app.ini を編集する。

今回は RUN_USERmnmain$ (<コンピューター名>$) に修正。

サービスへの登録

コマンドプロンプト を管理者で開き、以下コマンドを実行。

sc.exe create gitea start= auto binPath= "\"c:\gitea\gitea.exe\" web --config \"c:\gitea\custom\conf\app.ini\""

後は Windows のサービスから 開始 を選択すれば OK.

公開鍵の登録

GitHub と同じで、 SSH 経由の操作は、公開鍵を用いて行われる。

公開鍵の登録方法は以下の通り。

  1. 右上の自分のアイコンをクリック -> 設定 -> SSH / GPG キー を選択
  2. SSHキーの管理キーを追加 ボタンを押下
  3. SSH の公開鍵を入力する

以上。

参考資料

変更履歴

日付 内容
2025/3/26 新規作成
2025/4/20 管理者ユーザーを明示的に追加する手順を追加
組み込み SSH サーバーによる接続について追加

Vim のマクロを使って気持ち良くなった話 - 表のデータを TS の enum にする

こんなデータから、こんなデータを作りたい

PDF に以下のような表がありました。

項目1 項目2
1
2
× 3

実際は行が 200 行くらいあります。

これを、以下のような TypeScript の enum にしたいのです。

enum Type {
    松 = 1;
    竹 = 2;
    梅 = 3;
}

どうやったか?

まず、 PDF の表から文字列をコピペすると、以下のようなテキストデータになりました。

松
〇
1
竹
〇
2
梅
×
3

これを、前述の enum の形式にして、 200@q で気持ちよくなりたいわけです。

処理する順番としては以下のようにしました。

  1. 「項目2」 を消す
  2. 「項目1」の行末尾に = を追加
  3. 「項目1」と「値」 の行を結合
  4. 「3.」で結合した末尾に ; を追加
  5. 次の行の先頭にカーソルを配置

こうしてできた文字列を enum 内にコピペしてフォーマッタをかければ enum 定義の完成です。

ではやってみましょう。

<ここにカーソルがある>松
〇
1
竹
〇
2
梅
×
3

という状態から始め、 jddk で「項目2」の行を削除します。(1.)

<ここにカーソルがある>松
1
竹
〇
2
梅
×
3

つづいて A = <Esc> で「項目1」の末尾に = を追加します。(2.)

すると以下の状態になりますになります。

松 = <ここにカーソルがある>
1
竹
〇
2
梅
×
3

J で行を結合します。(3.)

松 = <ここにカーソルがある>1
竹
〇
2
梅
×
3

A;<Esc> で末尾に ; を追加します。(4.)

松 = 1;<ここにカーソルがある>
竹
〇
2
梅
×
3

マクロの連続実行ができるように、j^ で次の行の先頭にカーソルを持っていきます。(5.)

松 = 1;
<ここにカーソルがある>竹
〇
2
梅
×
3

この操作を記録して、 200@q とかすれば、他の項目も全て enum の形式になります。やったね。

2025年3月19日水曜日

Vim のマクロを使って気持ち良くなった話 - モックの JSON データ作成・編集編

モックデータの新規作成

こんなデータを作りたい

ホストにぶら下がっているエッジが大量にあり、そのエッジのステータスを取得する API がある。 その API のモックデータを作りたい。

{
    "type", "電球",
    "number", 1,
    "name", "電球1",
},
{
    "type", "電球",
    "number", 2,
    "name", "電球2",
},
{
    "type", "電球",
    "number", 3,
    "name", "電球3",
},
... これが 120 個くらい続く

どうしたか?

何個かデータを追加していくと、キー操作のパターンが見えてきました。

{
    "type", "電球",
    "number", 1,
    "name", "電球1",
},
<ここにカーソルがある>{
    "type", "電球",
    "number", 2,
    "name", "電球2",
},

上記状態で、qqVjjjjyjjjjpjj<C-a>j<C-a>kkkq としたあと、 117@q と打って気持ちよくなりました。 (実際は何個かやって感覚をつかんでからなので、 114@q くらいだった気がする…)

モックデータの修正

こんなデータを作りたい、既にデータはある

先ほど作ったデータが、実は間違いでした…。 本当は「電球60個に人感センサ60個」なのでした。

{
    "type", "電球",
    "number", 1,
    "name", "電球1",
},
{
    "type", "電球",
    "number", 2,
    "name", "電球2",
},
{
    "type", "電球",
    "number", 3,
    "name", "電球3",
},
... これが 60 個続く
{
    "type", "人感センサ",
    "number", 61,
    "name", "人感センサ1",
},
{
    "type", "人感センサ",
    "number", 62,
    "name", "人感センサ2",
},
{
    "type", "人感センサ",
    "number", 63,
    "name", "人感センサ3",
},
... これが 60 個続く

どうしたか?

このデータだけ見ると、「人感センサについてさっきと同じことすればいいじゃん」と思うでしょう。その通りです。

ただ、考えるのが面倒でこのデータには入れていませんが、既に使われているモックデータのため、「type, name 以外のパラメーターを変更したくない」という制約があったのです。

なんでそんな状況になったか覚えていませんが、そんな状況だったのです。

という事で、制約を守った編集ができるような手順を、マクロ含みで考えました。

まずは、61 個目以降のtype, name の数値部分以外を人感センサに一括置換します。

そうすると、以下のようなデータになります。

{
    "type", "電球",
    "number", 1,
    "name", "電球1",

},
{
    "type", "電球",
    "number", 2,
    "name", "電球2",
},
{
    "type", "電球",
    "number", 3,
    "name", "電球3",
},
... これが 60 個続く
{
    "type", "人感センサ",
    "number", 61,
    "name", "人感センサ61",
},
{
    "type", "人感センサ",
    "number", 62,
    "name", "人感センサ62",
},
{
    "type", "人感センサ",
    "number", 63,
    "name", "人感センサ63",
},
... これが 60 個続く

後は簡単ですね。

以下の位置にカーソルを移動し、 qq60<C-x>jjjjj^q59@q で完了です。

{
    "type", "電球",
    "number", 1,
    "name", "電球1",
},
{
    "type", "電球",
    "number", 2,
    "name", "電球2",
},
{
    "type", "電球",
    "number", 3,
    "name", "電球3",
},
... これが 60 個続く
{
    "type", "人感センサ",
    "number", 61,
<ここにカーソルがある>    "name", "人感センサ61",
},
{
    "type", "人感センサ",
    "number", 62,
    "name", "人感センサ62",
},
{
    "type", "人感センサ",
    "number", 63,
    "name", "人感センサ63",
},
... これが 60 個続く

これで気持ちよくなれました。

以上です。

2025年3月15日土曜日

Maven で Servlet 開発する

新人研修で JSP&Servlet をやるっぽいので素振りをした。

前提

  • Docker: Docker version 27.4.0, build bde2b89
  • 使用コンテナ: maven:3, tomcat:11

compose.yaml の作成

services:
  app:
    image: maven:3
    volumes:
      - .:/workspaces
      # tomcat11 との共有ディレクトリ
      # ビルドした war ファイルをここに格納する
      - firststep-webapps:/tomcat-webapps
    command: "sleep infinity"
  tomcat:
    image: tomcat:11
    volumes:
      - firststep-webapps:/usr/local/tomcat/webapps
    ports:
      - 8080:8080
volumes:
  firststep-webapps:
    driver: local

開発環境の起動

docker compose up -d

開発環境へ接続

docker compose exec app bash

Maven プロジェクトの作成

ひな形の作成

mvn archetype:generate -DgroupId=dev.mikoto2000.study.servlet -DartifactId=firststep -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

ただし、このプロジェクトは依存が足りなかったり Servlet が jakarta になる前のものだったりするので、 pom.xmlweb.xml を修正する。

pom.xml の修正

${PROJECT_ROOT}/pom.xml に以下依存を追加する。

    <!-- https://mvnrepository.com/artifact/jakarta.servlet/jakarta.servlet-api -->
    <dependency>
      <groupId>jakarta.servlet</groupId>
      <artifactId>jakarta.servlet-api</artifactId>
      <version>6.1.0</version>
      <scope>provided</scope>
    </dependency>

    <!-- https://mvnrepository.com/artifact/jakarta.servlet.jsp.jstl/jakarta.servlet.jsp.jstl-api -->
    <dependency>
      <groupId>jakarta.servlet.jsp.jstl</groupId>
      <artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
      <version>3.0.2</version>
    </dependency>

web.xml の修正

${PROJECT_ROOT}/src/main/webapp/WEB-INF/web.xml を修正。

before:

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
</web-app>

after:

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
         http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">
    <display-name>MyApp</display-name>
</web-app>

開発

実装

${PROJECT_ROOT}/src/main/java/dev/mikoto2000/study/servlet/firststep/HelloServlet.java に Servlet を実装する。

package dev.mikoto2000.study.servlet.firststep;

import java.io.IOException;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      response.setContentType("text/html;charset=UTF-8");
      response.getWriter().println("<h1>Hello, Servlet!</h1>");
  }
}

ビルド

mvn package

${PROJECT_ROOT}/target/firststep.war が作成される。

デプロイ

/tomcat-webapps が tomcat のアプリデプロイディレクトリと共有されているので、 war ファイルをそこにコピーする。

cp target/firststep.war /tomcat-webapps/

動作確認

http://localhost:8080/firststep/hello へアクセスすると、 Hello, Servlet! が表示される。

以上。

2025年2月28日金曜日

Vim でカーソル下の文字コード取得と文字コードでの入力を行う

使いどころはパッと思いつかないけど、過去に何度かお世話になったので備忘録として記す。

カーソル下の文字コード取得

ga コマンドで取得できる。

例えば、 にカーソルを合わせて ga を押すと、以下のように情報が表示される。

<→ > 8594, Hex 2192, Oct 20622, Digr ->

文字コードでの入力

インサートモード中に <C-v> を押すことで、文字コードによる入力ができる。

例えば、前述の を入力したい場合には i<C-v>u2192 と入力する。

以上。

参考資料

2025年2月11日火曜日

Spring Boot で SAML 認証をする(署名無しバージョン)

前提

  • Java: 21
  • Spring Boot: 3.4.2
  • Keycloak: 26

開発環境の起動

詳細は docker-compose.yaml を参照。

Spring Initilizr

this

Keycloak の設定

  1. http://localhost:8080/ へ接続し、以下情報でログイン
    • ユーザー名: admin
    • パスワード: password
  2. レルムの作成
    • レルム名: myrealm
  3. クライアントの作成
    1. Clients > Create client
      • Client type: SAML
      • Client ID: saml-sp
      • Name: SAML Practice
    2. Settings タブで必要事項を設定
      • Root URL: http://localhost:8081/
      • Valid redirect URIs: http://localhost:8081/*
    3. Keys タブで必要事項を設定
      • Client signature required: Off
  4. ユーザーの追加
    1. Users > Add user で、必要事項を記入して Create 押下
      • Username: test
    2. Credentials タブで Set password を押下し、必要事項を記入し Save
      • Password: test
      • Password confirmation: test
      • Temporary: Off

依存を追加

pom.xml に Shiboleth のリポジトリを追加し、 spring-security-saml2-service-provider の dependency を追加する。

<repositories>
  <repository>
    <id>shibboleth</id>
    <name>Shibboleth Repository</name>
    <url>https://build.shibboleth.net/nexus/content/repositories/releases/</url>
    <releases>
      <enabled>true</enabled>
    </releases>
    <snapshots>
      <enabled>false</enabled>
    </snapshots>
  </repository>
</repositories>
...(snip
  <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-saml2-service-provider</artifactId>
  </dependency>
...(snip

Security のコンフィギュレーションを作成

package dev.mikoto2000.study.saml.firststep.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

  @Bean
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
      .authorizeHttpRequests(auth -> auth
          .requestMatchers("/", "/public").permitAll()
          .anyRequest().authenticated()
          )
      .saml2Login(saml2 -> saml2
          .loginProcessingUrl("/login/saml2/sso/myrealm")
          )
      .logout(logout -> logout
          .logoutSuccessUrl("/")
          );
    return http.build();
  }
}

署名に使うファイル群の作成

# 1. 秘密鍵を作成
openssl genpkey -algorithm RSA -out private.key

# 2. 証明書リクエストを作成
openssl req -new -key private.key -out cert.csr \
    -subj "/C=JP/ST=Tokyo/L=Shibuya/O=ExampleCorp/OU=IT/CN=localhost"

# 3. 証明書を発行
openssl x509 -req -days 3650 -in cert.csr -signkey private.key -out certificate.crt

# 4. 秘密鍵と証明書をクラスパス直下に移動
mv certificate.crt private.key ./src/main/resources/

アプリケーション設定

spring:
  security:
    saml2:
      relyingparty:
        registration:
          myrealm:
            # Keycloak の Client ID と合わせる
            entity-id: "saml-sp"
            signing:
              credentials:
                # さっき作った証明書などの情報を入力
                - private-key-location: "classpath:private.key"
                  certificate-location: "classpath:certificate.crt"
                  private-key-password: "changeit"
                  private-key-alias: "saml-key"
                  key-store-password: "changeit"
            assertingparty:
              metadata-uri: "http://keycloak:8080/realms/myrealm/protocol/saml/descriptor"

# デバッグ出力を有効化
logging:
  level:
    org:
      springframework.web: debug

# Keycloak とポートがかぶるのでこっちを変更
server:
  port: 8081

表示するページの作成

以下静的ページを src/main/resources/static 下に格納する。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>test</title>
</head>
<body>
  Hello, World!
</body>
</html>

hosts ファイルの更新

hosts ファイルに以下エントリーを追加。

127.0.0.1 keycloak

動作確認

  1. http://localhost:8081/index.html へ接続すると、 keycloak へリダイレクトされる
  2. 先ほど作った test ユーザーでログインすると、 index.html が表示される

OK.