前回 は Thymeleaf を使ってログイン画面をレスポンスしました。次は認証機能を組み込んでみましょう。
コードはこちら
Session Authentication
自前で作るのも良いですけど、用意してくれたやつに乗っかりましょう。今回は、Session Authentication を組み込みます。
認証成功時にサーバサイドの Session に認証情報を入れて、かつ、それを紐付ける ID をブラウザの Cookie に入れておき、サーバにアクセスする度に Cookie も送って「このリクエストは認証済みのやつか?」を判断する奴です。昔ながらの安心する奴。
参考にしたのは、こちら
build.gradle
これを追加します。
annotationProcessor "io.micronaut:micronaut-security" compile "io.micronaut:micronaut-security" compile "io.micronaut:micronaut-security-session"
application.yml
ちょっと長いです
micronaut: security: enabled: true intercept-url-map: - pattern: /login -> 1 access: - isAnonymous() - pattern: /public/** -> 2 access: - isAnonymous() - pattern: /tasks/** -> 3 access: - SYSTEM_ADMIN - pattern: /** -> 4 access: - isAuthenticated() endpoints: login: enabled: true -> 5 session: enabled: true -> 6 login-success-target-url: /tasks -> 7 unauthorized-target-url: /login -> 8
この設定は以下を示します。
/login
は未ログイン状態でもアクセスできるようにisAnonymous()
/public/**
も未ログイン状態でもアクセスできるようにisAnonymous()
/tasks/**
は、ログイン状態で、かつ role にSYSTEM_ADMIN
を持つ- その他の path はログイン状態の必要がある
- true にすると Micronaut の LoginController が有効になります
- true にすると Session Authentication が有効になります
- ログイン成功時に遷移する URL を指定します
- 未ログイン状態、ログイン状態でもrole を持っていないユーザがアクセスした時に遷移する URL を指定します
ログイン画面
Micronaut の LoginController を使う時、
- POST method
- Content-Type が
application/x-www-form-urlencoded
orapplication/json
- パラメータ名は username / password
でなければなりません。なので、ログイン画面では form タグの method
に POST
を設定し、Login ボタン click 時に submit します。input タグの name 属性も合わせてください。
認証処理
AuthenticationProviderUserPassword
今回のケースでは認証処理は AuthenticationProvider を implements します。
@Singleton // 1 @RequiredArgsConstructor // lombok public class AuthenticationProviderUserPassword implements AuthenticationProvider { private final SystemConfigurationProperties systemConfigurationProperties; // 2 @Override public Publisher<AuthenticationResponse> authenticate( @Nullable HttpRequest<?> httpRequest, AuthenticationRequest<?, ?> authenticationRequest) { return Flowable.create( emitter -> { if (Objects.equals( authenticationRequest.getIdentity(), systemConfigurationProperties.getIdentity()) && Objects.equals( authenticationRequest.getSecret(), systemConfigurationProperties.getSecret())) { // 認証成功時、UserDetails を設定する 3 var userDetails = new UserDetails( (String) authenticationRequest.getIdentity(), Collections.singletonList(Role.SYSTEM_ADMIN.name())); // 4 emitter.onNext(userDetails); } else { emitter.onError(new AuthenticationException(new AuthenticationFailed())); } emitter.onComplete(); }, BackpressureStrategy.ERROR); } }
何となく Spring っぽい感じがしませんか。
- Spring で言うところの
@Bean
とか@Service
とかに相当します。@Singleton
だとインスタンスは1つだけ作られます。Thread Safe でお願いします - プロパティファイルの情報を保持する為の class として SystemConfigurationProperties を別途定義しました
- RDBMS に保存していた情報と付き合わせるのが一般的でしょうけど、今回はプロパティに定義した値と比較するようにしました
- この認証に成功した時は role に
SYSTEM_ADMIN
を設定しました。application.yml の設定が生きてきます。
これで認証成功時は UserDetails インスタンスが Session 上に格納されるようになります。
SystemConfigurationProperties
プロパティファイルの情報を保持する class です。
@Data // lombok @ConfigurationProperties("system.admin") // 1 public class SystemConfigurationProperties { /** システム管理者ID. */ private String identity; /** システム管理者パスワード. */ private String secret; }
Spring っぽいですね。
- プロパティの prefix を指定します
yml をみてもらうとわかると思いますが、プロパティの名前を合わせることで Micronaut がこのインスタンス生成時に良い感じに値を設定してくれます。この class も Bean 登録されるので、AuthenticationProviderUserPassword のコンストラクタに含めておくと Injection されるのです。
認証後に遷移する画面
認証後に遷移するのは /tasks
と設定したので、レスポンスを返すように Controller と html を作っておきます。
Micronaut を起動して http://localhost:8080/tasks に直接アクセスするとログイン画面に戻ってしまうのが確認できると思います。
Application Configuration & 認証処理
起動時のパラメータでプロパティファイルを変更する仕組みが Micronaut にあります。 SpringBoot にもあります。
例えば、ローカル環境 / CI 環境 / 本番環境で振る舞いを変えたい時に使用します。*1
今回はapplication-local.yml
という名前のローカル環境のプロパティファイルを作ります。
system: admin: identity: scott secret: tiger
認証情報を新しく設定しました。 これを SystemConfigurationProperties にマッピングさせるには、MICRONAUT_ENVIRONMENTS を使用して起動します。
$ MICRONAUT_ENVIRONMENTS=local ./gradlew run
http://localhost:8080 にアクセスすると、ログイン画面に遷移しましたね。 そこで application-local.yml に指定した認証情報を入力して、Login ボタンをクリックすると...
/tasks
が表示できました。また、ブラウザの開発ツールで SESSION
Cookie の存在を確認できるはずです。
まとめ
今回は、認証を組み込んでみました。起動パラメータによってプロパティの認証情報も application.yml の値が上書きされていることが確認できたと思います。
Spring 使ったことがある方は使えそうな気がしませんか?