Things of interesting

プログラミングに関する技術ネタの備忘録

Vue + Spring SecurityでJWT認証を使う

やること

フロントエンドをVue、バックエンドをSpringで構成したアプリケーションで、JWTによる認証を行う

やりかた

バックエンド側の設定

以下の記事によくまとまっていたので参考にしました。

qiita.com

VueでAPIを呼び出した際にCORSで弾かれてしまったので、WebSecurityConfigに以下設定を追加しています。
フロントエンドをdev serverで起動しているので動作確認しているのでそのせいかも?
フロントエンド側でヘッダーに格納されるAuthorization情報が取得できなかったので expose 設定に追加しています。
それ以外は一般的なCORS設定です。

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    // 省略
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .cors().configurationSource(this.corsConfigurationSource())
        // 省略
    }
    private CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedMethod(CorsConfiguration.ALL);
        corsConfiguration.addAllowedHeader(CorsConfiguration.ALL);
        corsConfiguration.addExposedHeader("Authorization");
        corsConfiguration.addAllowedOrigin("http://hogehoge.com"); 
        corsConfiguration.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource corsSource = new UrlBasedCorsConfigurationSource();
        corsSource.registerCorsConfiguration("/**", corsConfiguration);

        return corsSource;
    }
}

フロントエンドの設定

ログイン処理の中でヘッダー情報のトークンをvuexのストアに保存します。
axiosの実装は下のJavascriptファイルを参考にしてください。
vuexへの保存は一般的な実装なので省略します。

// ログイン処理を行うメソッド内
const res = await HttpClient.getAxios().post('/api/login', 
      {
        "userName" : this.loginId,
        "password" : this.password
      }
    );
const jwt: string = res.headers.authorization;
this.$store.commit('saveJwt', jwt);
// 省略

HTTP通信をするのにaxiosというモジュールを使っているので、これを介してHTTPクライアントを作成するときに ヘッダー情報がセットされるようにしています。(ログインで保存しておいたvuexのストアから取得)
※初回リクエストとなるloginではjwtは空になりますが、サーバー側でloginAPIのみトークン認証がかからないようにしています。

import axios from 'axios'
import store from 'store/index';

const URL = 'http://localhost:8081';
const instance = axios.create({
    baseURL: URL,
    timeout: 10000,
    headers: {
        'Content-Type': 'application/json'
    }
  });

export default class HttpClient {
    public static getAxios() {
        instance.defaults.headers.common['Authorization'] = 'Bearer ' + store.getters.jwt;
        return instance;
    }
}

残作業

セッションのような状態を管理しないので、多重ログイン制御がこれ単体ではできない。
セッションの場合はRedisを使っていたので同じようにRedisに有効なJWTを保存してバリデーションをかける。