REST APIへのアクセス制限

ログイン状態によるREST APIへのアクセス制御を実装します。

ユーザー認証を実装した後は、ログインしていなければToDoページを表示できないため、REST APIについても同様にログインしていなければアクセスできないようにします。

ここでは、ハンドラを実装することで実現します。ハンドラの実装についてはexample-chatのバックエンド実装から流用します。

ログインチェックハンドラの作成

まず、ハンドラのクラスを格納するcom.example.system.nablarch.handlerパッケージを作成します。

続いて、example-chatの同パッケージにあるLoginCheckHandlerクラスを持ってきます。

このクラスでは、ログインしていなくてもアクセスが可能なREST APIを設定することで、ログインしていない状態でそれ以外のREST APIへアクセスするとエラーレスポンスを返します。

対象のREST APIを設定

LoginCheckHandlerクラスはそのままではToDoアプリに合わないため、一部を修正していきます。

まず、対象のREST APIを設定するために、コンストラクタで設定している「ログインしていなくてもアクセスが可能なREST API」として、次のように修正します。

public LoginCheckHandler() {
    whitePatterns
            .add("/api/signup", HttpMethod.POST)
            .add("/api/login", HttpMethod.POST);
}

ログインチェック処理の修正

次に、ユーザーIDの取得方法と、エラーレスポンスの生成方法を変更するため、handleメソッドを次のように修正します。

@Override
public Object handle(HttpRequest request, ExecutionContext context) {
    if (!whitePatterns.matches(request)) {
        if (ThreadContext.getUserId().equals("guest")) {
            String path = request.getRequestPath();
            String method = request.getMethod();
            logger.logWarn(String.format("Unauthorized access to path. path=[%s] method=[%s]", path, method));
            throw new HttpErrorResponse(HttpResponse.Status.FORBIDDEN.getStatusCode());
        }
    }
    return context.handleNext(request);
}

example-chatでは、セッションストアから独自の値を取得していますが、ここでは、スレッドコンテキストからユーザーIDを取得します。スレッドコンテキストからユーザーIDを取得するには、ThreadContext#getUserIdを使用します。(参考:スレッドコンテキストの属性値を設定/取得する | Nablarch

スレッドコンテキストにユーザーIDがされていない場合は、common.configに設定されている次の環境依存値がユーザーIDとして使用されます。

# 未ログイン時、ログに出力するユーザID
nablarch.userIdAttribute.anonymousId=guest

そのため、スレッドコンテキストのユーザーIDがここで設定している値であればログインしていないと判断するように変更します。

example-chatではエラーレスポンスを独自の例外で表現していますが、ここでは、今までの実装と同様に、HttpErrorResponseを使用するように変更します。

ハンドラを設定

LoginCheckHandlerクラスをハンドラとして設定するため、rest-component-configurationにあるwebFrontController名のコンポーネント定義を修正します。

ハンドラを設定するhandlerQueueプロパティの最後にあるpackageMapping名のハンドラの手前に、LoginCheckHandlerクラスをハンドラとして追加します。

<component name="webFrontController" class="nablarch.fw.web.servlet.WebFrontController">
  <property name="handlerQueue">
    <list>
...
      <component class="com.example.system.nablarch.handler.LoginCheckHandler"/>

      <component-ref name="packageMapping"/>
    </list>
  </property>
</component>

REST APIのテスト

ハンドラが有効になっているかを確認するため、REST APIのテストを実行します。

ハンドラが有効になっていれば、レスポンスのステータスコードが403 Forbiddenになり、いくつかのテストが失敗します。テストは失敗しますが、ハンドラが有効になっていることが確認できます。

次に、テストが成功するように修正していきます。REST APIテスティングフレームワークではCookieが使えないため、テスト開始時にセッションストアにユーザーIDを設定し、それに使用したExecutionContextをリクエスト送信時に使用するようにします。特定のExecutionContextをリクエスト送信時に使用するには、sendRequestメソッドではなくsendRequestWithContextメソッドを使用します。

ユーザー認証のテストではユーザーIDに1010を使用していますので、例えばログアウトのテストであれば、次のように修正します。

@Test
public void RESTAPIでログアウトできる() {
    ExecutionContext executionContext = new ExecutionContext();
    SessionUtil.put(executionContext, "user.id", "1010");

    RestMockHttpRequest request = post("/api/logout");
    HttpResponse response = sendRequestWithContext(request, executionContext);
...

Todo操作のテストではユーザーIDに1001を使用していますので、例えばTodo一覧取得のテストであれば、次のように修正します。

@Test
public void RESTAPIでToDo一覧が取得できる() throws Exception {
    ExecutionContext executionContext = new ExecutionContext();
    SessionUtil.put(executionContext, "user.id", "1001");

    RestMockHttpRequest request = get("/api/todos");
    HttpResponse response = sendRequestWithContext(request, executionContext);
...

他のテストケースもこれらと同様に修正します。全て修正したら、もう一度テストを実行し、全てのテストが成功することを確認します。

これで、REST APIへのアクセス制御の実装は完了です。

results matching ""

    No results matching ""