【Java】Spring Bootで@Securedが動作しない時の対処法

spring

こんにちは、マネックス・ラボの田代です。
過去3回に渡りAWSやPython関連の記事を書いて参りましたが、先日社内で「当社はJavaのエンジニアを募集しているので、Javaに関する記事ももっと増やしたいね」 と言うお話になったので、
私がSpringビギナーの頃に陥った問題について書いてみようと思います。

問題のコード

package com.example.demo.controller;

import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class ExampleController {
  @Secured(value="ROLE_ADMIN")
  @RequestMapping(value="/example", method=RequestMethod.GET)
  private String example() {
    return "example";
  }
}

上記が問題のSpring BootのControllerのコードです。
/exampleにアクセスした際に、対応するViewを返すだけのシンプルなコードで、
ROLE_ADMINの権限を持たないユーザーに対してはエラーページを返す想定です。

しかし、上記のコードには一箇所誤りがあり、

example
ROLE_ADMINを持たないユーザーでアクセス

@Securedが機能せず誰でもアクセス出来てしまいます。
問題箇所がどこであるか、すぐに分かるでしょうか。

アノテーションを付与するメソッドはprivateではなくpublicで定義する

正解は12行目private String example()でメソッドをprivateで定義してしまっている部分です。
該当箇所をpublicに修正してあげると、

diff

error
もう一度ROLE_ADMINを持たないユーザーでアクセス

今度は想定通りエラーページが返りました。

まとめ

この事象はSpring AOP Proxyの制約であり、privateメソッドがサブクラスでオーバーライド出来ないことに起因します。
従って@Secured以外のアノテーションや、メソッドをfinalで定義した場合でも同様のことが起こり得ます。
興味がある方は下記を読んでみて下さい。 docs.spring.io

初歩的なミスではありますが、例外が出力される訳でもなく只々アクセス制限をすり抜けてしまうだけのように見えるので、
Security Configurationの方を疑ったりしてしまい当時は即座に気付くことが出来ませんでした。

今回の記事が同様の問題に陥った方の助けになれば嬉しいです。

田代 侑大システム開発部 マネックス・ラボ