실제 JWT 발급 및 검증 구현
이전 단계에서 만든 임시 토큰을 실제 암호화된 JWT(JSON Web Token)로 대체하고, Spring Security 필터를 통해 API 요청을 보호하는 방법을 구현합니다.
Part 1: 백엔드 (Spring Boot) JWT 로직 구현
1단계: JWT Secret Key 설정
[수정]
JWT 라이브러리의 보안 요구사항(256비트 이상)을 만족하는 더 길고 안전한 Secret Key 예시로 변경합니다.
backend/src/main/resources/application.properties
파일에 아래 내용을 추가합니다.
# JWT Secret Key 설정
# 256비트 이상의 길이를 가진 Base64 인코딩된 문자열이어야 합니다.
# 아래는 예시이며, 실제 운영 환경에서는 openssl rand -base64 32 명령 등으로 생성한 키를 사용하세요.
jwt.secret=256비트보타작은키는 오류
2단계: JWT 유틸리티 클래스 생성
JwtUtil.java
package dev.nerobong2.openehr.open_ehr.backend.jwt;
// ... (이전과 동일한 코드)
3단계: AuthService 수정
AuthService.java
package dev.nerobong2.openehr.open_ehr.backend.auth;
import dev.nerobong2.openehr.open_ehr.backend.jwt.JwtUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class AuthService {
private final AuthenticationManager authenticationManager;
private final JwtUtil jwtUtil;
public String login(String username, String password) {
// Spring Security를 통해 인증 시도
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(username, password)
);
// 인증 성공 시 UserDetails 객체를 가져와 JWT 생성
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
return jwtUtil.generateToken(userDetails);
}
}
4단계: JWT 인증 필터 생성
JwtAuthFilter.java
package dev.nerobong2.openehr.open_ehr.backend.jwt;
// ... (이전과 동일한 코드)
5단계: SecurityConfig와 ApplicationConfig 분리
ApplicationConfig.java
package dev.nerobong2.openehr.open_ehr.backend.config;
import dev.nerobong2.openehr.open_ehr.backend.user.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.util.ArrayList;
@Configuration
@RequiredArgsConstructor
public class ApplicationConfig {
private final UserRepository userRepository;
@Bean
public UserDetailsService userDetailsService() {
return username -> userRepository.findByUsername(username)
.map(user -> new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
new ArrayList<>()))
.orElseThrow(() -> new UsernameNotFoundException("User not found: " + username));
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
SecurityConfig.java
package dev.nerobong2.openehr.open_ehr.backend.config;
import dev.nerobong2.openehr.open_ehr.backend.jwt.JwtAuthFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthFilter jwtAuthFilter;
private final AuthenticationProvider authenticationProvider;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
)
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
6단계: 인증 테스트용 API 생성
TestController.java
package dev.nerobong2.openehr.open_ehr.backend.test;
// ... (이전과 동일한 코드)
Part 2: 프론트엔드 (Vue) JWT 연동 및 테스트
(이전과 동일)
1단계: 상태 관리 (Pinia) 설정
frontend/src/stores/auth.ts
// ... (이전과 동일한 코드)
2단계: 인증된 API 호출 테스트
frontend/src/views/HomeView.vue
<script setup lang="ts">
// ... (이전과 동일한 코드)
</script>
<template>
<main>
<h1>메인 페이지</h1>
<p>로그인에 성공했습니다.</p>
<hr />
<p><strong>{{ message }}</strong></p>
</main>
</template>
다음 단계: 이제 백엔드 서버를 재시작하시면 오류 없이 정상적으로 실행될 것입니다. 그 후 다시 로그인을 테스트해 보세요.
댓글 없음:
댓글 쓰기