Qiita第2投稿!なぜ Spring Security と JWT を混同してはいけないのか?【Spring編】
こんにちは!前回の投稿では PURGE と TRUNCATE の違い(Oracle編) について整理しましたが、
今回は Java/Spring 界隈でよく話題になる 「Spring Security」と「JWT」 の違いについて書いてみようと思います。
Spring を使って開発していると、認証まわりの話で
「Spring Security と JWT、どっちを使えばいいの?」
「Spring Security = JWT のことじゃないの?」
といった質問をよく耳にします。
実際にはこの2つは まったく別のレイヤーの技術 です。
混同してしまうと、セキュリティ設計を誤る可能性もあるので注意が必要です。
Spring Securityとは?
Spring Security は、Spring Framework 向けの セキュリティフレームワーク です。
主に次のような機能を提供します:
- 認証(Authentication)
- 認可(Authorization)
- CSRF 対策
- セッション管理
- パスワードの暗号化
- Security Filter Chain の構築
要するに、アプリケーション全体のセキュリティを制御する仕組みです。
Spring Boot でアプリを立ち上げたとき、自動的に /login ページが出てくるのはこの仕組みのおかげです。
JWTとは?
JWT (JSON Web Token) は、認証情報をトークンとしてやり取りするための フォーマット です。
一方、Spring Security は「認証と認可をどう制御するか」という仕組みそのものです。
Spring Security と JWT の関係
実は、Spring Security は JWT を使って動くこともできる のです。
Spring Security は “認証方法” を自由に差し替えられるように設計されています。
たとえば:
- セッションベースの認証(従来型 Web アプリ)
- JWT ベースのトークン認証(SPA + REST API)
- OAuth2 / OpenID Connect 認証(Google, GitHub ログインなど)
このように、
Spring Security = フレームワーク, JWT = データ形式
という役割分担になります。
🧩 簡単な設定例(Spring Boot 3.x × Spring Security × JWT コード整理)
この記事では、Spring Boot 3.x と JWT 認証を Spring Security で組み合わせる場合の コード部分だけを整理 して説明します。
全体像が理解しやすいように 説明 + コード 形式にしています。
1️⃣ build.gradle (依存関係)
下記の依存関係を追加してください。
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
}
🔹 Spring Boot 3.x + JJWT 最新 0.11.x 版
🔹 jjwt-impl と jjwt-jackson は実行時に必要
2️⃣ JWT 認証フィルター (JwtAuthenticationFilter.java)
Spring Boot 3.x + JJWT 0.11.x の方式です。
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private static final String SECRET_KEY = "my-secret-key-my-secret-key"; // 最低 256bit 推奨
@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 jwt = authHeader.substring(7);
try {
// parserBuilder() を使用
Jws<Claims> claimsJws = Jwts.parserBuilder()
.setSigningKey(Keys.hmacShaKeyFor(SECRET_KEY.getBytes(StandardCharsets.UTF_8)))
.build()
.parseClaimsJws(jwt);
String username = claimsJws.getBody().getSubject();
// 認証情報をセット(権限なし)
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(username, null, List.of());
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (Exception e) {
System.out.println("Invalid JWT: " + e.getMessage());
}
}
filterChain.doFilter(request, response);
}
}
🔹 javax.servlet.(Springboot2.x) → jakarta.servlet. に変更
🔹 最低 256bit 以上のキーが必要
3️⃣ Security 設定 (SecurityConfig.java)
Spring Security 6.x での推奨設定です。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable()) // CSRF 無効化
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/**").permitAll() // 認証不要
.anyRequest().authenticated() // その他は認証必要
)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
}
🔹 WebSecurityConfigurerAdapter は不要
🔹 JWT フィルターは UsernamePasswordAuthenticationFilter の前に配置
重要ポイント
-
Spring Security
フレームワークとして JWT 認証を接続する役割。 -
JWT
認証情報を運ぶデータフォーマット。 -
Spring Boot 3.x + JJWT 0.11.x での注意点
-
parserBuilder()を使用すること -
jakarta.servletパッケージを使用 -
SecurityFilterChainBean を必ず定義すること - HMAC SHA256 用に最低 256bit のキーを使用すること
-
5. よくある誤解
| 誤解 | 正しい理解 |
|---|---|
| Spring Security = JWT | ❌ Spring Security は JWT を使うこともできるセキュリティフレームワーク |
| JWT があれば Spring Security は不要 | ❌ JWT は認証情報を運ぶだけ。検証や認可は別途実装が必要 |
| JWT を使えば自動的に安全になる | ❌ 有効期限管理や署名検証などの正しい実装が必須 |
まとめ
| 項目 | Spring Security | JWT |
|---|---|---|
| 種類 | フレームワーク | トークンフォーマット |
| 主な役割 | 認証・認可・セキュリティ全般の制御 | 認証情報の伝達 |
| 状態管理 | セッション or Stateless | Stateless |
| 関係性 | JWTを利用可能 | Spring Securityと組み合わせて使うことが多い |
おわりに
Spring Security と JWT は、よく一緒に語られるテーマですが、
同じものではなく、補完関係にある という点を理解しておくことが大切です。
次回は「Spring Security で JWT 認証を実装してみる」記事を書こうと思っています。
興味がある方はぜひフォローしていただけると嬉しいです! 😊