Spring AOP With Annotation

# Spring Boot AOP を用いた認可チェックの実装

## 1. 依存関係の追加（依存関係追加）
Spring Boot プロジェクトで AOP を使用するには、以下の依存関係を `pom.xml` に追加します。

```xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
```

## 2. カスタムアノテーションの作成（カスタムアノテーション作成）
まず、認可チェック用の `@AuthCheck` アノテーションを作成します。

```java
@Target(ElementType.METHOD) // メソッドに適用可能
@Retention(RetentionPolicy.RUNTIME) // 実行時に有効
public @interface AuthCheck {

    /**
     * 必須のユーザーロール（例: "ADMIN"）
     */
    String mustRole() default "";
}
```

このアノテーションを使うことで、特定のメソッドに「特定のロールが必要」という制約を簡単に追加できます。

## 3. AOP を使った認可インターセプターの実装（AOP インターセプター実装）
次に、`@AuthCheck` アノテーションが付与されたメソッドを AOP を使ってインターセプトし、アクセス制御を行う `AuthInterceptor` クラスを実装します。

```java
@Aspect  // AOP のアスペクトとして定義
@Component  // Spring の Bean として管理
public class AuthInterceptor {

    @Resource
    private UserService userService;

    /**
     * 認可チェックのインターセプト処理
     *
     * @param joinPoint メソッド実行のコンテキスト
     * @param authCheck 認可チェック用のカスタムアノテーション
     * @return メソッドの実行結果
     * @throws Throwable 例外処理
     */
    @Around("@annotation(authCheck)")
    public Object doInterceptor(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable {
        String mustRole = authCheck.mustRole(); // 必須のロールを取得

        // HTTP リクエスト情報を取得
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();

        // 現在のログインユーザー情報を取得
        User loginUser = userService.getLoginUser(request);
        UserRoleEnum mustRoleEnum = UserRoleEnum.getEnumByValue(mustRole);

        // 必須ロールが指定されていない場合、チェック不要なので通過
        if (mustRoleEnum == null) {
            return joinPoint.proceed();
        }

        // 現在のユーザーが持つロールを取得
        UserRoleEnum userRoleEnum = UserRoleEnum.getEnumByValue(loginUser.getUserRole());

        // ロール情報がない場合、認可エラー
        if (userRoleEnum == null) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }

        // 必須ロールが ADMIN で、ユーザーが ADMIN でない場合はアクセス拒否
        if (UserRoleEnum.ADMIN.equals(mustRoleEnum) && !UserRoleEnum.ADMIN.equals(userRoleEnum)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }

        // 認可チェックを通過した場合、元のメソッドを実行
        return joinPoint.proceed();
    }
}
```

## 4. 認可チェックの適用（アノテーション適用）
作成した `@AuthCheck` アノテーションを `Controller` のメソッドに適用し、特定のロールが必要な API を実装します。

```java
@RestController
@RequestMapping("/admin")
public class AdminController {

    @Resource
    private AdminService adminService;

    /**
     * 管理者専用 API
     */
    @AuthCheck(mustRole = "ADMIN") // 管理者のみアクセス可能
    @GetMapping("/dashboard")
    public String getAdminDashboard() {
        return adminService.getDashboardData();
    }
}
```

このようにすることで、`@AuthCheck(mustRole = "ADMIN")` を付けたメソッドは、「管理者ロール（ADMIN）を持つユーザーのみアクセス可能」というルールが適用されます。

## 5. まとめ（まとめ）
Spring AOP を活用して、カスタムアノテーションによる認可チェックを実装しました。これにより、認可ロジックを各メソッドに直接書く必要がなくなり、コードの再利用性が向上します。

### 本記事で学んだポイント
✅ `@Aspect` を使って AOP クラスを定義  
✅ `@Around("@annotation(authCheck)")` でカスタムアノテーションをインターセプト  
✅ HTTP リクエストから現在のユーザー情報を取得  
✅ 必須のロールを満たしていない場合、例外をスローしてアクセス拒否  

AOP を活用することで、認可チェックやロギング、パフォーマンス計測などの共通処理を簡単に適用できます。