Spring BootでJWT認証を実装する方法を初心者向けに徹底解説!基本の流れとメリットを学ぼう
新人
「先輩、Spring BootでJWT認証を使う方法を勉強したいんですが、まずJWT認証って何ですか?」
先輩
「JWT認証は、ユーザーの情報をトークンという形にしてやり取りする認証方式だよ。ステートレスな仕組みで、セッションの代わりに使えるんだ。」
新人
「ステートレスってことは、サーバーに状態を保存しないんですよね?どんなメリットがあるんですか?」
先輩
「そう。複数サーバーで負荷分散しても認証情報を共有する必要がないから、大規模なシステムやAPI認証に向いているんだ。」
新人
「なるほど!じゃあ基本の流れから教えてください!」
1. JWT認証とは何か(基本的な説明)
JWT(JSON Web Token)認証は、ユーザーの情報をJSON形式でトークン化し、署名を付けてクライアントとサーバー間でやり取りする認証方式です。トークンは改ざんを防ぐために署名されており、サーバーはその署名を検証してユーザーを認証します。
この仕組みでは、認証情報をサーバー側に保持しないため、セッション管理が不要になります。Spring Bootと組み合わせることで、WebアプリケーションやREST APIでの認証をシンプルに構築できます。
例えば、ユーザーがログインするとサーバーはJWTを発行し、そのJWTをクライアントに返します。以後のリクエストでは、このJWTをHTTPヘッダーに付与して送信し、サーバーは署名を検証して認証を行います。
2. Spring BootでJWT認証を使うメリット
Spring BootでJWT認証を採用する大きなメリットは、ステートレス認証を簡単に実装できる点です。セッション認証ではサーバーにセッション情報を保存する必要がありますが、JWT認証ではその必要がありません。
これにより、以下のようなメリットが得られます。
- 複数サーバー構成でもセッション情報を共有する必要がない
- APIサーバーやマイクロサービスとの相性が良い
- 外部サービス間で安全にユーザー情報を共有できる
特にマイクロサービス環境では、各サービスが独立して認証処理を行えるため、システム全体のスケーラビリティが向上します。
3. JWT認証の基本的な流れ(発行・送信・検証)
Spring BootでのJWT認証は、以下の3つのステップで構成されます。
① JWTの発行
ユーザーがログインフォームから送信した認証情報(ユーザー名・パスワード)をサーバーが検証し、正しければJWTを生成して返します。
@Controller
public class AuthController {
@PostMapping("/login")
public ResponseEntity<String> login(@RequestParam String username, @RequestParam String password) {
if ("user".equals(username) && "pass".equals(password)) {
String token = JwtUtil.generateToken(username);
return ResponseEntity.ok(token);
}
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("認証失敗");
}
}
② JWTの送信
クライアントは発行されたJWTをブラウザのローカルストレージやセッションストレージに保存し、以降のリクエストでHTTPヘッダーAuthorizationに付与して送信します。
③ JWTの検証
サーバーは受け取ったJWTの署名を検証し、有効期限や発行者情報を確認します。正しい場合のみ、リクエストを処理します。
public static boolean validateToken(String token) {
try {
Jwts.parser()
.setSigningKey("mysecretkey")
.parseClaimsJws(token);
return true;
} catch (JwtException e) {
return false;
}
}
この流れを理解しておくことで、Spring BootでのJWT認証の実装がスムーズになります。次のステップでは、実際にGradleの依存関係やユーティリティクラスの作成方法を解説します。
4. Gradleで必要な依存関係を追加する方法
Spring BootでJWT認証を実装するには、JWTを生成・検証するためのライブラリが必要です。代表的なのは「jjwt」というライブラリで、Javaから簡単にJWTを扱うことができます。
pleiadesで作成したGradleプロジェクトでは、build.gradleに以下の依存関係を追加します。なお、依存関係はpleiadesのチェック機能からも追加可能ですが、ここでは直接記述する方法を示します。
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'io.jsonwebtoken:jjwt:0.9.1'
}
上記のio.jsonwebtoken:jjwtがJWT処理用のライブラリです。この依存関係を追加したら、Gradleをリフレッシュして反映させます。
また、Spring BootのWeb機能を使うためにspring-boot-starter-webも必要です。これはpleiadesのプロジェクト作成時にほとんどの場合自動で追加されていますが、確認しておきましょう。
5. JWTトークンを生成するユーティリティクラスの作成
次に、JWTを生成・検証する処理をまとめたユーティリティクラスを作成します。このクラスでは、秘密鍵の設定、発行時刻、有効期限、署名のアルゴリズムを定義します。
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
public class JwtUtil {
private static final String SECRET_KEY = "mysecretkey"; // 秘密鍵(本番環境では安全な方法で管理)
public static String generateToken(String username) {
return Jwts.builder()
.setSubject(username) // ユーザー名をペイロードに設定
.setIssuedAt(new Date()) // 発行時刻
.setExpiration(new Date(System.currentTimeMillis() + 60 * 60 * 1000)) // 有効期限(1時間)
.signWith(SignatureAlgorithm.HS256, SECRET_KEY) // 署名アルゴリズムと秘密鍵
.compact();
}
public static boolean validateToken(String token) {
try {
Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
}
このJwtUtilクラスは、ユーザー名を基に署名付きのJWTを生成し、検証も行います。秘密鍵は固定値ではなく、環境変数や設定ファイルで安全に管理することが推奨されます。
6. ログイン時にJWTを発行するControllerの実装
最後に、ユーザーがログインした際にJWTを発行して返す処理を@Controllerクラスで実装します。ここでは簡単な例として、ユーザー名とパスワードを固定値で検証していますが、実際にはデータベースと連携して認証を行うのが一般的です。
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class AuthController {
@PostMapping("/login")
public ResponseEntity<String> login(@RequestParam String username, @RequestParam String password) {
// 本来はDBなどで認証する
if ("user".equals(username) && "pass".equals(password)) {
String token = JwtUtil.generateToken(username);
return ResponseEntity.ok(token);
}
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("認証失敗");
}
}
このControllerでは、ログイン成功時にJwtUtilクラスを使ってJWTを生成し、そのトークンをクライアントに返しています。クライアントはこのトークンをHTTPヘッダーに含めて以降のリクエストを送信します。
これで、Spring BootにおけるJWT発行の基本実装が完成しました。次のパートでは、このJWTをリクエスト時に検証し、認証が必要なエンドポイントにアクセス制御をかける方法を解説します。
7. 受け取ったJWTを検証するフィルターの実装
ログイン時に発行したJWTを利用するためには、クライアントからのリクエストに含まれたトークンを検証する仕組みが必要です。Spring Bootでは、フィルターを作成してリクエストごとにトークンの有効性をチェックできます。
ここでは、HTTPヘッダーのAuthorizationからトークンを取得し、JwtUtilクラスを使って検証するシンプルなフィルターを作成します。
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.web.filter.OncePerRequestFilter;
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7); // "Bearer "を除去
if (!JwtUtil.validateToken(token)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
} else {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
filterChain.doFilter(request, response);
}
}
このフィルターでは、トークンが存在しない場合や検証に失敗した場合は401 Unauthorizedを返します。OncePerRequestFilterを継承することで、各リクエストごとに一度だけ処理を実行します。
8. 認証が必要なAPIにJWTを適用する方法
作成したフィルターをSpring Bootのアプリケーションに登録し、特定のAPIエンドポイントに適用することで、JWT認証を有効化できます。
Spring Securityを使わないシンプルな例として、WebMvcConfigurerを使ってフィルターを追加します。
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<JwtAuthenticationFilter> jwtFilter() {
FilterRegistrationBean<JwtAuthenticationFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new JwtAuthenticationFilter());
registrationBean.addUrlPatterns("/secure/*"); // /secure以下のパスに適用
return registrationBean;
}
}
この設定では、/secure/で始まるパスにアクセスする際にJwtAuthenticationFilterが動作します。これにより、トークンが正しい場合のみAPIの処理が実行されます。
例えば、次のようなControllerを作成すると、JWTが必要なAPIを簡単に作れます。
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class SecureController {
@GetMapping("/secure/data")
@ResponseBody
public String getSecureData() {
return "このデータはJWT認証済みのユーザーだけが取得できます。";
}
}
このようにして、Spring Bootの任意のエンドポイントにJWT認証を簡単に適用できます。
9. JWT認証を安全に運用するためのポイント(有効期限・リフレッシュトークンなど)
JWT認証は便利でスケーラブルな方式ですが、安全に運用するためにはいくつかのポイントに注意する必要があります。
① 有効期限の設定
トークンの有効期限は短めに設定するのが推奨されます。長すぎると、トークンが漏洩した際に悪用されるリスクが高まります。例えば、アクセス用のJWTは30分〜1時間程度が目安です。
② リフレッシュトークンの利用
有効期限が切れた後もスムーズに再ログインできるように、リフレッシュトークンを併用します。リフレッシュトークンは有効期限を長くし、セキュリティを強化するために安全なストレージに保存します。
③ 秘密鍵の安全管理
JWTの署名に使う秘密鍵は、アプリケーションのソースコードに直接書かず、環境変数や外部の設定ファイルで管理します。これにより、GitHubなどに誤って公開してしまうリスクを減らせます。
④ HTTPS通信の利用
JWTは暗号化されていないペイロード部分を含むため、通信経路で盗聴されると情報が漏れる可能性があります。必ずHTTPS通信を使って安全にやり取りしてください。
⑤ 必要最小限の情報だけを含める
ペイロード部分には、ユーザーIDや権限情報など必要最小限のデータだけを入れます。個人情報や機密情報は絶対に含めないようにしましょう。
これらの運用ポイントを守ることで、Spring BootのJWT認証をより安全に活用できます。
まとめとしての活用イメージ
今回解説した手順を順番に実装すれば、Spring BootでのJWT認証が動作します。ログイン時にトークンを発行し、フィルターで検証、特定APIに認証を適用し、安全な運用ルールを組み合わせることで、実践的な認証基盤を構築できます。