CodeZine(コードジン)

特集ページ一覧

Spring BootでWebセキュリティを設定しよう

Spring Bootで作るマイクロサービス 第7回

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2019/09/09 11:00
目次

Spring Security Web(2)

特定ヘッダでのチェック

 続いて、特定のヘッダをチェックする方法を紹介します。

 APIの場合、図3に示すトークンを使ったアクセス制御はよく行う手法です。管理者からのアクセスに限らず、トークンに応じて柔軟なアクセスコントロールが行いやすいため、こちらの方法がより多く使われるのかもしれません。

 リスト4は、処理をより単純化し、X-Tokenという1つのヘッダ値でのチェックによって、アクセス制御を行うためのサンプルコードです。

特定ヘッダでのチェック
特定ヘッダでのチェック
[リスト4]特定ヘッダでのチェック例(src/main/java/com/coltware/springboot/zipcode/config/WebHeaderSecurityConfiguration.javaの抜粋)
public class WebHeaderSecurityConfiguration extends WebSecurityConfigurerAdapter {

    // : 省略

    // (1) フィルタを実装
    public class HeaderCheckFilter extends AbstractPreAuthenticatedProcessingFilter {

        private String headerName;

        public HeaderCheckFilter(String headerName) {
            this.headerName = headerName;
        }

        // (2) ヘッダ(X-Token)の値を返す
        @Override
        protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
            return request.getHeader(headerName);
        }

        @Override
        protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
            return "";
        }
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        HeaderCheckFilter filter = new HeaderCheckFilter("X-Token");

        // (3) ヘッダのチェック
        filter.setAuthenticationManager(new AuthenticationManager() {
            @Override
            public Authentication authenticate(Authentication authentication) throws AuthenticationException {

                String principal = (String) authentication.getPrincipal();

                // X-Tokenのヘッダの値をチェックする
                if(principal.equals("PASS")) {
                    // (4) 認証済みとして設定する
                    authentication.setAuthenticated(true);
                }
                else{
                    throw new BadCredentialsException("Token key error");
                }
                return authentication;
            }
        });

        // 認証の対象となるパス
        http.antMatcher("/admin/**");

        // (5) フィルタの設定
        http.addFilter(filter);

        // 対象のすべてのパスに対して認証を有効にする
        http.authorizeRequests().anyRequest().authenticated();

        // : 省略
    }

    //  : 省略
}

 (1)でヘッダから必要な値を取り出すためのフィルタのクラスを定義します。

 このクラスはAbstractPreAuthenticatedProcessingFilterインターフェースの実装クラスであり、getPreAuthenticatedPrincipalとgetPreAuthenticatedCredentialsのメソッドをオーバライドします。

 今回のサンプルでは(2)のように指定されたヘッダの値のみを返します。

 実際にヘッダのチェックは、(3)のようにsetAuthenticationManagerメソッド内で行い、ヘッダの値が問題なければ(4)のように認証済みとして設定します。

 後は先ほどのBasic認証と同様に設定していきますが、Basic認証を使う代わりに作成したフィルタを使うように(5)の通りに設定します。

 より自由に認証方法を実装したい場合には、今回の実装を参考にSpring Securityのリファレンスを参照するとよいと思います。

CSRF対策の設定

 CSRFとは、クロスサイトリクエストフォージェリの略であり、図4(左)のようにサイトアクセス者が不正サイトを通じて意図しない登録処理などを行う攻撃の1つです。

 この攻撃の一般的な防止法には、図4(右)のように、POSTやPUTなどのメソッドでそのページがリクエストをするときに、事前に作成したワンタイムパスワードと一緒にリクエストするといった方法があります。

 Spring Securityを有効にするとデフォルトでこのCSRF対策が有効になります。しかしながら、APIサーバではこの対策は必要ないためリスト5のように機能を無効にします。

図4:Spring SecurityでのCSRF対策
図4:Spring SecurityでのCSRF対策
[リスト5]CSRFの無効設定(src/main/java/com/coltware/springboot/zipcode/config/WebSecurityConfigurerAdapter.javaの抜粋)
@Override
protected void configure(HttpSecurity http) throws Exception {
    // : 省略
    http.csrf().disable();
    // : 省略
}

認証エラーの設定

 認証エラーが発生したときには、APIサーバであればリスト6のようにJSON形式でエラーを返す必要があります。

[リスト6]エラー時のJSON例
{
  "status": {
    "code": "error",
    "message": "Full authentication is required to access this resource"
  }
}

 その場合には、エラー時の処理をリスト6のように設定します。

[リスト7]エラーレスポンスの設定(src/main/java/com/coltware/springboot/zipcode/config/WebSecurityConfigurerAdapter.javaの抜粋)
@Override
protected void configure(HttpSecurity http) throws Exception {
    // : 省略

    // (1) エラーが生じたときのレスポンス設定
    http.exceptionHandling().authenticationEntryPoint(new ErrorAuthEntryPoint());
}
// (2) エラー結果を記述するための実装
public class ErrorAuthEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
            throws IOException, ServletException {

        // (3) 実際のエラーを出力する
        ErrorResponse errorResponse = Response.createErrorResponse(authException);
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(JsonParser.Feature.ALLOW_COMMENTS,true);

        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");

        mapper.writeValue(response.getWriter(),errorResponse);
    }
}

 (1)で、認証エラーが生じた場合に、レスポンスを生成するクラスのインスタンスを設定します。

 レスポンスを生成するクラスは、(2)のようにAuthenticationEntryPointインターフェースを実装したクラスを作成します。

 実際のレスポンスは自由にであり、JSON形式でメッセージを出力するように(3)のようにjacksonライブラリのObjectMapperクラスを使って出力しています。

最後に

 Spring Securityは今回紹介した機能以外にも非常に多くの機能があり、通常のWebアプリケーションを想定したFormを使ったログインやログアウトなどの機能も備えてます。

 また、APIサーバでセキュリティをより強固にしたい場合にはOAuthなどのセキュリティ機能を使います。Spring SecurityはOAuthのプロバイダ機能もあるので、より強固なセキュリティにも対応可能です。

 しかし、機能が高度である一方、記述方法が柔軟であり、なかなか正しい制限方法を明確に記述することが難しくもあります。このあたりは、慣れが必要だと思うので、さまざまなサンプルを見て他の人の記述方法なども参考にしてみてください。

 今回まではサンプルアプリケーションを通じてSpring Bootでの各種スターター(ライブラリ等)の使い方を中心に紹介してきました。次回からはSpring Bootの仕組み側を見ていきます。

参考資料

 

本連載の書籍が発売されました!

Javaによる高速Webアプリケーション開発のためのSpring Boot入門

Amazon(POD) Amazon(電子書籍) その他

Javaによる高速Webアプリケーション開発のためのSpring Boot入門

著者:WINGSプロジェクト 小林昌弘
発売日:2020年5月31日(水)
価格(POD):2,200円(税込)
価格(電書):1,760円(税込)



  • LINEで送る
  • このエントリーをはてなブックマークに追加

バックナンバー

連載:Spring Bootで作るマイクロサービス

もっと読む

著者プロフィール

  • WINGSプロジェクト 小林 昌弘(コバヤシ マサヒロ)

    <WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。個人紹介主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしど...

  • 山田 祥寛(ヤマダ ヨシヒロ)

    静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for ASP/ASP.NET。執筆コミュニティ「WINGSプロジェクト」代表。 主な著書に「入門シリーズ(サーバサイドAjax/XM...

あなたにオススメ

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5