|
2024年現在の Spring Boot についての覚え書き。 目次 †
JDKのインストール †(Mac) VsCode環境設定 †https://code.visualstudio.com/docs/java/java-spring-boot 以下の拡張機能をインストール
プロジェクトの作成 †build.gradle †まだ認証機能を構築していないので spring security を無効化しておく :
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
//implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-aop'
implementation 'org.springframework.session:spring-session-jdbc'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
//testImplementation 'org.springframework.security:spring-security-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
:
application.properties †とりあえず最初の起動に必要な分だけ記載。(DBは事前準備) spring.application.name=demo spring.datasource.url=jdbc:postgresql://localhost:5432/sample spring.datasource.username=sample spring.datasource.password=sample spring.datasource.driver-class-name=org.postgresql.Driver spring.session.store-type=jdbc spring.session.jdbc.initialize-schema=always spring.session.jdbc.platform=postgresql TOPページ作成 †コントローラ †src/main/java/com/example/demo/controller/IndexController.java package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class IndexController {
@GetMapping("/")
public String index() {
return "index";
}
}
Thymeleafテンプレート †src/main/resources/templates/index.html <!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
</head>
<body>
Index Page!
</body>
</html>
テスト起動 †![]() 起動したら http://localhost:8080/ にアクセスしてTOPページを表示してみる。 以降は、諸々の解説を記載する コントローラについて †コントローラの基本 †
例) RestAPI用のコントローラ package com.example.demo.controller;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SampleRestController {
@GetMapping("api1")
public Map<String, String> samplejson() {
Map<String, String> res = new HashMap<String, String>();
res.put("var1", "test1");
return res;
}
@GetMapping("api2")
public ResponseEntity<Map<String, String>> samplejson3() {
Map<String, String> res = new HashMap<String, String>();
res.put("var1", "test1");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType(MediaType.APPLICATION_JSON, StandardCharsets.UTF_8));
return new ResponseEntity<Map<String, String>>(res, headers, HttpStatus.BAD_REQUEST);
}
}
例) SSR(サーバサイドレンダリング)を行う場合 package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HelloController {
@GetMapping("/")
public String index(Model model) {
model.addAttribute("var1", "Test1");
return "index";
}
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("name", "山田");
return "hello";
}
}
リクエストデータの受け取り †アノテーションを使用してリクエストデータやセッションデータ、Cookieデータ、HTTPヘッダ等をコントローラーのメソッド引数にバインド出来る。
例) @RequestParam によるパラメータ取得 package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestParam;
:
public class SampleController {
public String index(@RequestParam(name="param1", required=false, defaultValue="0") String param1) {
System.out.println("param1: " + param1);
:
}
Pathパラメータは PathVariable アノテーションで取得 package com.example.demo.controller;
:
import org.springframework.web.bind.annotation.PathVariable;
:
public class BookController {
@GetMapping("/books/{isbn}")
public String edit(@PathVariable(name="isbn") String isbn,
Model model) {
System.out.println("isbn: " + isbn);
:
}
}
レスポンスデータの設定 †Rest コントローラの場合は返却するモデルをそのまま返す事が可能(自動的にJSONに変換される) package com.example.demo.controller;
:
import org.springframework.web.bind.annotation.PathVariable;
:
public class BookController {
@GetMapping("/books/{isbn}")
public Book edit(@PathVariable(name="isbn") String isbn) {
Optional<Book> book = repository.findById(isbn);
return book.isPresent() ? book.get() : null;
}
}
テンプレート(Thymeleaf)に展開する値は引数にバインドした Model に設定し、戻り値でテンプレート名を返す。 package com.example.demo.controller;
:
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
:
@Controller
public class BookController {
// 書籍リポジトリ
private final BookRepository repository;
// コンストラクタ
public BookController(BookRepository repo) {
this.repository = repo;
}
@GetMapping("/books/{isbn}")
public String edit(@PathVariable(name="isbn") String isbn,
Model model) {
if ("new".equals(isbn)) {
model.addAttribute("book", new Book());
} else {
Optional<Book> book = this.repository.findById(isbn);
model.addAttribute("book", book.isPresent() ? book.get() : null);
}
return "book_edit";
}
}
セッションの利用 †初期設定 (以下、セッションストアをPostgreSQLにする場合の例) †build.gradle dependencies {
:
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
implementation 'org.springframework.session:spring-session-jdbc'
runtimeOnly 'org.postgresql:postgresql'
:
}
application.properties spring.datasource.url=jdbc:postgresql://localhost:5432/sample spring.datasource.username=sample spring.datasource.password=sample spring.datasource.driver-class-name=org.postgresql.Driver spring.session.store-type=jdbc spring.session.jdbc.initialize-schema=always spring.session.jdbc.platform=postgresql spring.session.jdbc.initialize-schema = always の場合、自動的にセッション管理用のテーブルが作成される。 セッション情報の取得/設定方法 †コンストラクタインジェクションで HttpSession セッションを取得する場合 package com.example.demo.controller;
:
import jakarta.servlet.http.HttpSession;
@Controller
public class BookController {
private final HttpSession session;
public BookController(HttpSession session) {
this.session = session;
}
@GetMapping("/sample")
public String sample(Model model) {
int count = 0;
// セッション情報の取得
Object countObj = session.getAttribute("sample_count");
if (countObj != null) {
count = (int)countObj;
}
// セッション情報の設定
session.setAttribute("sample_count", ++count);
System.out.println("sample_count: " + count);
:
}
}
RequestContextHolder から HttpSession セッションを取得する場合 package com.example.demo.controller;
:
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
:
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
@Controller
public class BookController {
@GetMapping("/sample2")
public String sample2(Model model) {
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
if (attributes != null) {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) attributes;
HttpServletRequest request = servletRequestAttributes.getRequest();
HttpSession session = request.getSession();
int count = 0;
// セッション情報の取得
Object countObj = session.getAttribute("sample_count");
if (countObj != null) {
count = (int)countObj;
}
// セッション情報の設定
session.setAttribute("sample_count", ++count);
System.out.println("sample_count: " + count);
model.addAttribute("sample_count", count);
}
return "sample";
}
}
DIの使用 †コンストラクタ、メソッド、フィールドに @Autowired を使用する事で依存性注入が可能となっている。
コンストラクタインジェクション †@Controller
public class SampleController {
private final BookRepository repository;
private final HttpSession session;
// Spring Framework 4.3 以降ではコンストラクタが1つの場合は Autowired は不要
// @Autowired
public SampleController(HttpSession session, BookRepository repository) {
this.session = session;
this.repository = repository;
}
:
}
フィールド 及び メソッドインジェクション †:
import org.springframework.beans.factory.annotation.Autowired;
:
@Controller
public class SampleController {
@Controller
public class SampleController {
// フィールドインジェクトション
@Autowired
private BookRepository repository;
private HttpSession session;
// メソッドインジェクトション
@Autowired
public void setSession(HttpSession session) {
this.session = session;
}
:
}
AOPの使用 †Spring によるアスペクト指向プログラミング AOP の概念 依存ライブラリの追加 †build.gradle dependencies {
:
implementation 'org.springframework.boot:spring-boot-starter-aop'
:
}
@AspectJ のサポートを有効にする †https://spring.pleiades.io/spring-framework/reference/core/aop/ataspectj/aspectj-support.html @EnableAspectJAutoProxy アノテーションを追加 package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
public class MyConfig {
:
}
アスペクト、ポイントカット、アドバイスの宣言 †Before、After 、Around などのアノテーションを使用して処理の前後に任意の処理を差し込む事ができる。
例) コントローラの前後に任意の処理を差し込む package com.example.demo.advise;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyControllerAdvise {
@Around("@within(org.springframework.stereotype.Controller)")
public Object aroundController(ProceedingJoinPoint jp) throws Throwable {
System.out.println("START " + jp.getSignature());
Object result = jp.proceed();
System.out.println("END " + jp.getSignature());
return result;
}
}
例) アドバイスからセッション情報にアクセスする package com.example.demo.advise;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
@Aspect
@Component
public class MyControllerAdvise {
@Around("@within(org.springframework.stereotype.Controller)")
public Object aroundController(ProceedingJoinPoint jp) throws Throwable {
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
if (attributes != null) {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) attributes;
HttpServletRequest request = servletRequestAttributes.getRequest();
HttpSession session = request.getSession();
:
}
System.out.println("START " + jp.getSignature());
Object result = jp.proceed();
System.out.println("END " + jp.getSignature());
return result;
}
}
Spring Data JDBCの使用 †Spring Data JDBC 設定 †build.gradle dependencies {
:
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.projectlombok:lombok'
:
}
application.properties spring.datasource.url=jdbc:postgresql://localhost:5432/sample spring.datasource.username=sample spring.datasource.password=sample spring.datasource.driver-class-name=org.postgresql.Driver logging.level.org.springframework.jdbc.core=DEBUG モデル定義 †package com.example.demo.entity;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
import lombok.Data;
@Data
@Table("books")
public class Book {
@Id
private String isbn;
private String title;
private int price;
// Lombok を使用している為、個別のsetter 、getter は定義なし
}
DAO定義 †インターフェースを定義する事により様々なSQLを発行する事ができる。 上記より一部抜粋
DAOの定義例 package com.example.demo.entity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
public interface BookRepository extends CrudRepository<Book, String> {
// ... where isbn like 'xxxx%' としてSQL生成される
public Iterable<Book> findByIsbnStartingWith(String isbn);
// ページネーションを利用する場合
public Page<Book> findByIsbnStartingWithAndTitleStartingWith(String isbn, String title, Pageable pageable);
// 任意のSQLを発行する場合
@Query("select * from books where 任意の条件")
public Iterable<Book> findByCustomConditions(@Param("field1") String field1, @Param("field2") String field2);
}
利用例 †コントローラからの利用例 †@Controller
public class BookController {
private final BookRepository repository;
public BookController(HttpSession session, BookRepository repo) {
this.repository = repo;
}
@GetMapping("/books")
public String index(@RequestParam(name="isbn", required=false, defaultValue="") String isbn, Model model) {
Iterable<Book> books = this.repository.findByIsbnStartingWith(isbn);
model.addAttribute("books", books);
return "books";
}
}
ページネーションを利用する場合 †:
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
:
@Controller
public class BookController {
private final BookRepository repository;
public BookController(HttpSession session, BookRepository repo) {
this.repository = repo;
}
@GetMapping("/books")
public String index(@RequestParam(name="isbn", required=false, defaultValue="") String isbn,
@RequestParam(name="title", required=false, defaultValue="") String title,
@RequestParam(name="page", required=false, defaultValue="0") int page,
@RequestParam(name="limit", required=false, defaultValue="5") int limit,
Model model) {
Pageable pageable = PageRequest.of(page, limit, Sort.by("isbn"));
Page<Book> bookPage = this.repository.findByIsbnStartingWithAndTitleStartingWith(isbn, title, pageable);
List<Book> books = bookPage.getContent();
model.addAttribute("pager", bookPage);
model.addAttribute("books", books);
return "books";
}
}
テンプレート側 :
<div th:if="${pager}">
Page: <span th:text="${pager.number}" />
Count: <span th:text="${pager.numberOfElements}" />
Total Count: <span th:text="${pager.totalElements}" />
Total Pages: <span th:text="${pager.totalPages}" />
</div>
<table>
<tr>
<th>ISBN</th>
<th>タイトル</th>
<th>価格</th>
</tr>
<tr th:each="book : ${books}">
<td><a th:text="${book.isbn}" th:href="|/books/${book.isbn}|">isbn</a></td>
<td th:text="${book.title}">title</td>
<td th:text="${book.price}">price</td>
</tr>
</table>
:
ログレベルの設定 †application.properties logging.level.org.springframework.jdbc.core=TRACE Spring Security(JDBC)の使用 †設定 †build.gradle dependencies {
:
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
:
}
application.properties spring.session.store-type=jdbc spring.session.jdbc.initialize-schema=always spring.session.jdbc.platform=postgresql スキーマ †デフォルトでは以下の構成を前提として認証、認可が行われる。 create table users( username varchar_ignorecase(50) not null primary key, password varchar_ignorecase(500) not null, enabled boolean not null ); create table authorities ( username varchar_ignorecase(50) not null, authority varchar_ignorecase(50) not null, constraint fk_authorities_users foreign key(username) references users(username) ); 但し、以降では以下の通りカスタマイズして認証機能を利用する 手順を記載する。
create table users( username varchar(50) not null primary key, username_disp varchar(50) not null, password varchar(500) not null, enabled boolean not null ); create table user_roles ( username varchar(50) not null, authority varchar(50) not null ); create unique index user_roles_ix1 on user_roles (username,authority); 認証用のエンティティ等 †MyUser.java package com.example.demo.entity;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
import lombok.Data;
@Data
@Table("users")
public class MyUser {
@Id
private String username;
private String usernameDisp;
private String password;
private boolean enabled;
}
MyUserRole.java package com.example.demo.entity;
import org.springframework.data.relational.core.mapping.Table;
import lombok.Data;
@Data
@Table("user_roles")
public class MyUserRole {
private String username;
private String authority;
}
MyUserDetails.java package com.example.demo.entity;
import java.util.Collection;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import lombok.Data;
@Data
public class MyUserDetails implements UserDetails {
private String username;
private String password;
private String usernameDisp;
private List<GrantedAuthority> authorities;
public MyUserDetails(String username, String password, String usernameDisp, List<GrantedAuthority> authorities) {
this.username = username;
this.password = password;
this.usernameDisp = usernameDisp;
this.authorities = authorities;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
}
MyUserRepository.java (DAO) package com.example.demo.entity;
import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
public interface MyUserRepository extends CrudRepository<MyUser, String> {
/**
* ユーザの保持しているロールを取得する.
* @param username ユーザ名
* @return ロール一覧
*/
@Query("select * from user_roles where username = :username")
public Iterable<MyUserRole> findUserRoles(@Param("username") String username);
}
認証用のサービス作成 †package com.example.demo.service;
import org.springframework.stereotype.Service;
import com.example.demo.entity.MyUser;
import com.example.demo.entity.MyUserDetails;
import com.example.demo.entity.MyUserRepository;
import com.example.demo.entity.MyUserRole;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@Service
public class MyAuthService implements UserDetailsService {
private final MyUserRepository repository;
public MyAuthService(MyUserRepository repo) {
this.repository = repo;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// ユーザ情報取得
Optional<MyUser> foundUser = this.repository.findById(username);
if (!foundUser.isPresent()) {
throw new UsernameNotFoundException("User not found");
} else if (!foundUser.get().isEnabled()) {
throw new UsernameNotFoundException("User not found");
}
MyUser user = foundUser.get();
// ユーザの保持ロールを取得
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
Iterator<MyUserRole> roles = this.repository.findUserRoles(username).iterator();
while (roles.hasNext()) {
MyUserRole role = roles.next();
authorities.add(new SimpleGrantedAuthority(role.getAuthority()));
}
// DBのパスワードが暗号化済みの場合
String encodedPassword = user.getPassword();
if (!encodedPassword.matches("\\{[a-zA-Z+0-9]+\\}.+")) {
// DBのパスワードが暗号化されていない場合
encodedPassword = "{bcrypt}" + new BCryptPasswordEncoder().encode(user.getPassword());
}
// UserDetails生成
return (UserDetails) new MyUserDetails(user.getUsername(), encodedPassword, user.getUsernameDisp(), authorities);
}
}
ログイン画面作成 †login.html <!DOCTYPE html>
<html lang="ja">
<head>
<title>Spring Security Example </title>
</head>
<body>
<div th:if="${param.error}">
Invalid username and password.
</div>
<div th:if="${param.logout}">
You have been logged out.
</div>
<form th:action="@{/login}" method="post">
<div><label> User Name : <input type="text" name="username"/> </label></div>
<div><label> Password: <input type="password" name="password"/> </label></div>
<div><input type="submit" value="Sign In"/></div>
</form>
</body>
</html>
許可がないページにアクセス時のエラー画面 †login.html <!doctype html>
<html lang="ja"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity6"
>
<head>
<meta charset="utf-8" />
</head>
<body>
Access Denined!
</body>
</html>
未ログイン時はログイン画面に飛ばす設定 †WebSecurityConfig.java package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableMethodSecurity
@EnableAspectJAutoProxy
public class WebSecurityConfig {
// エラーハンドラ(許可されていないページへのアクセス時用)
private final Customizer<ExceptionHandlingConfigurer<HttpSecurity>> authErrorHandler
= new Customizer<ExceptionHandlingConfigurer<HttpSecurity>>() {
@Override
public void customize(ExceptionHandlingConfigurer<HttpSecurity> t) {
t.accessDeniedPage("/access_denied");
}
};
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/dummy").permitAll() // for life check
.anyRequest().authenticated()
)
.formLogin((form) -> form
.loginPage("/login")
.successForwardUrl("/")
.permitAll()
)
.logout((logout) -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.deleteCookies("SESSION")
.invalidateHttpSession(true)
.permitAll()
)
.exceptionHandling(authErrorHandler);
return http.build();
}
}
コントローラの作成 †IndexController.java package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import lombok.RequiredArgsConstructor;
@Controller
@RequiredArgsConstructor
public class IndexController {
// ログイン後の表示
@PostMapping("/")
public String logined() {
return "redirect:/";
}
// TOPページ
@GetMapping("/")
public String index() {
return "index";
}
// アクセス不可
@GetMapping("/access_denied")
public String accessDenied() {
return "access_denied";
}
}
認可の設定 †コントローラにアノテーションを設定する事により、権限の有無によってアクセス制限をかける事ができる。 :
@GetMapping("/books")
@PreAuthorize("hasAuthority('ADMIN') || hasAuthority('BOOK_SEARCH')")
public String index(@RequestParam(name="isbn", required=false, defaultValue="") String isbn,
@RequestParam(name="title", required=false, defaultValue="") String title,
@RequestParam(name="page", required=false, defaultValue="0") int page,
@RequestParam(name="limit", required=false, defaultValue="5") int limit,
Model model) {
:
}
テンプレート(Thymeleaf)側で権限の有無によってボタン表示などを切り替えたい場合は、以下の通り制御できる。 <html lang="ja"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity6"
>
<body>
:
<div sec:authorize="hasAuthority('BOOK_UPDATE')">
<!-- 登録ボタン -->
<div th:if="!${book.createdAt}">
<button name="_method" value="post">登録</button>
</div>
<!-- 更新/削除ボタン -->
<div th:if="${book.createdAt}">
<button name="_method" value="put">更新</button>
<button name="_method" value="delete">削除</button>
</div>
</div>
:
</body>
</html>
ログインユーザ情報を画面に表示する †ここではヘッダにログインユーザ表示名を表示してみる。 Model にログインユーザを設定する為のアドバイスを作成する。 package com.example.demo.advise;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
import com.example.demo.entity.MyUserDetails;
@ControllerAdvice
public class MyLoginUserAdvise {
@ModelAttribute("currentUserName")
String currentUserName(Authentication authentication) {
if (authentication != null) {
MyUserDetails myUserDetails = (MyUserDetails) authentication.getPrincipal();
return myUserDetails.getUsernameDisp();
}
return null;
}
}
ヘッダ用のhtml :
<div th:fragment="header" id="header">
<div id="header_bar">
<div id="header_left">
Spring Boot サンプル
</div>
<div id="header_right">
<!-- ユーザ表示名 -->
<span th:text="${currentUserName}" />
<!-- ログアウトボタン -->
<form th:action="@{/logout}" method="post">
<input type="submit" value="ログアウト"/>
</form>
</div>
</div>
</div>
:
Thymeleaf の使用 †TODO:
|