Spring Securityのanonymous

[公式ドキュメントのAnonymous Authentication](https://docs.spring.io/spring-security/reference/servlet/authentication/anonymous.html)にあるとおり、セキュリティにおいては許可を与えたもの以外はすべて拒否がベターである。spring-securityをwebで使う場合はfilter chainで実装される。このとき、認証ユーザはfilter chainを通す一方、未認証ユーザはバイパスすると、未認証ユーザだけ特殊扱いになり不便な場合もある。そこでspring-securityはanonymous認証ユーザを用意している。実質的には未認証な認証ユーザという位置づけになる。

以降のサンプルコードはspring-security 6がベース。```build.gradle```は以下の通り。

```groovy
plugins {
	id 'java'
	id 'org.springframework.boot' version '3.0.3'
	id 'io.spring.dependency-management' version '1.1.0'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	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.thymeleaf.extras:thymeleaf-extras-springsecurity6'
	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.security:spring-security-test'
}

tasks.named('test') {
	useJUnitPlatform()
}
```

両者の分かりやすい挙動の違いはcontrollerにおいて認証オブジェクトである```Authentication```の取得とその型。参考:[公式ドキュメントのGetting Anonymous Authentications with Spring MVC](https://docs.spring.io/spring-security/reference/servlet/authentication/anonymous.html#anonymous-auth-mvc-controller)

```java
  @GetMapping("/sample")
  String sample(Authentication authentication, @CurrentSecurityContext SecurityContext context) {
    ...
```

注意点としては、anonymousだと```Authentication```引数は```null```になる。これを避けたければ```@CurrentSecurityContext```を使う。

|状態|認証済み|未認証(anonymous)|
|------|------|------|
|Authentication|UsernamePasswordAuthenticationToken|null|
|@CurrentSecurityContext|UsernamePasswordAuthenticationToken|AnonymousAuthenticationToken|

またanonymousは```ROLE_ANONYMOUS```という認可をデフォルトで持つのでこれでアクセス制御が出来る。たとえば、以下にすると```/anon```は未認証ユーザ**のみ**アクセス可能になる。参考：https://www.tenohira.xyz/tech/spring-anonymous-only-access/


```java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
  @Bean
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.formLogin(login -> login
        // 省略
    ).authorizeHttpRequests(authz -> authz
        .requestMatchers("/anon").anonymous()
        //.requestMatchers("/anon").hasRole("ANONYMOUS") // 上と同じ意味
        .anyRequest().authenticated()
    );
    return http.build();
  }
```

thymeleafでanonymousかどうかで何等かの表示の出し分けをするのは以下の通り。より細かい使い方については https://kagamihoge.hatenablog.com/entry/2019/11/12/195357 を参照。

```html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:th="https://www.thymeleaf.org"
  xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>anonymous sample</title>
</head>
<body>
		<div sec:authorize="isAnonymous()">
		  ANONYMOUSです。
		</div>
		<div sec:authorize="hasRole('ANONYMOUS')">
		  ANONYMOUSです。
		</div>
</body>
</html>
```

anonymousの認可をデフォルトの```ROLE_ANONYMOUS```から変更したり、principalを変更したり、anonymous認証の無効化は以下のようにする。

```java
  @Bean
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http. 
     // 省略
    ).anonymous((anon) -> anon
        .authorities("ROLE_HOGE", "ROLE_FOO")
        .principal("アノニマス")
        // .disable() // 無効化
    );
```