###2021/07/25に追記しました
最後に簡単な方法を追記しました。
###環境
Spring Boot 2.4.0
java 1.8
thymeleaf 3.0.11
Spring Security 2.4.0
###実現したいこと
ログイン中のユーザーのUSERDETAILSを継承したクラスの情報を取得したい。
###いろいろ試してなんとか実現
ほぼ丸一日かかりました。というかちゃんとSpringBootを理解していないのだとは思いますが、いろんなサイトを見て試してみてなんとかうまく行ったので、記録として残しておきます。
###USERDETAILSを継承しているクラス
Tenantsクラス
Staffクラス
###ログイン認証
MyUserDetailsServiceクラスのloadUserByUsernameメソッドで、返すクラスを分けています。
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 引数がnullなら例外を投げる
if (Objects.isNull(username)) {
throw new UsernameNotFoundException("username is empty");
}
Tenants tenants = tenantRepository.findByAdminId(username);
Staff staff = staffRepository.findByEmail(username);
// nullが返ってきたら例外を投げる
if(tenants != null) {
return tenants;
} else if(staff != null) {
return staff;
} else {
throw new UsernameNotFoundException("Not found username: " + username);
}
}
###コントローラー
ログイン後のトップ画面を表示するコントローラーで、今どのクラスでログイン認証されているか表示したい
いろいろ試した結果、下の実装で、どのクラスで認証されているか取得できるようになりました。
@GetMapping("/top")
public ModelAndView userTop(eModelAndView mv, Principal principal) {
// ログイン中のユーザーのUSERDETAILSを継承したクラスを取得する
Authentication auth = (Authentication) principal;
String authClass = auth.getPrincipal().getClass().getSimpleName();
if(authClass.equals("Tenants")) {
System.out.println("テナントクラスだぜ!!");
String tenantId = ((Tenants)auth.getPrincipal()).getId().toString();
httpServletResponse.addCookie(new Cookie("tenantId", tenantId));
}
if(authClass.equals("Staff")) {
System.out.println("スタッフクラスやっちゅうねん!!");
String tenantId = ((Staff)auth.getPrincipal()).getTenantId().toString();
httpServletResponse.addCookie(new Cookie("tenantId", tenantId));
}
}
###ハマったところ
PrincipalやAuthenticationなどの使いかたがよくわからなくて、
・引数の渡し方
・@AuthenticationPrincipalアノテーションの使い方
・キャストが必要?
・PrincipalにTenantsクラス入ってるけど、フィールドが取り出せないよ?
・getClass()がコード・アシスタントで出てくるのにエラーが出て実行できないよ?
などなど。。。
どうやらPrincipalにはUserDetailsを継承したクラスの、認証がうまく行ったときのレコードが全部入っているようでした。
しかし、username以外は取り出せないということに。。。
Principalを使えば良いとばかり思っていたけど、どうやら違った。
Authenticationクラスの中にPrincipalクラスが入っていて、Principalの中にUserDetailsクラスが入っているイメージでした。(合っているかわかりません)
ただそのイメージで
// コントローラーの引数に渡しているPrincipalからまずAuthenticationを取得する
Authentication auth = (Authentication) principal;
String authClass = auth.getPrincipal() // Principalを取得
.getClass() // クラスを取得
.getSimpleName(); // パッケージ名を除外
この流れでうまくいきました。
最初の
Principal → Authentication
へのキャストが必要なの??って感じでしたが、これをやらないとうまく行かなかった。
###thymeleafでビューで表示する方法
<div sec:authentication="principal"></div>
<div th:text="${#authentication.principal}"></div>
いずれも同じ内容が表示されます。コントローラーで取得するようりも遥かに簡単に取得できます。
そしてちゃんとprincipalにはUserDetailsが入っているのが確認できます。
###まとめ
- コントローラー引数に Principal principalを渡す
- Authenticationから.getPrincipal()でPrincipalを取得
- thymeleafでは
<div sec:authentication="principal"></div>
- または
<div th:text="${#authentication.principal}"></div>
// コントローラー
// 引数に Principal principalを渡す
public ModelAndView userTop(ModelAndView mv, Principal principal) {
// コントローラー
// 引数に Principal principalを渡す
Authentication auth = (Authentication) principal;
// AuthenticationからPrincipalを取得
String authClass = auth.getPrincipal()
.getClass()
.getSimpleName();
2021/07/25追記
###もっと簡単な方法がありました
####前提
User.javaクラスはUserDetailsを継承している。
###コントローラーで認証したUserを取得する
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
public String userTop(
HttpServletResponse httpServletResponse,
@AuthenticationPrincipal User user) {
// ユーザIDをクッキーに保存
httpServletResponse.addCookie(new Cookie("userId", user.getId().toString()));
return "top";
}
つまり、コントローラーのメソッドの引数の
@AuthenticationPrincipal User user
の時点ですでにUserクラスにキャストされているので、自由に取り出せる!!