JWT(Json Web Token)는 사용자 인증과 권한 부여를 위해 사용되는 토큰 기반 인증 방식
일반적으로 사용자가 로그인하면 서버는 JWT를 발급하고, 이후 요청마다 클라이언트는 이 토큰을 포함하여 서버에 보냄
서버는 이 토큰을 검증하여 사용자를 인증하고 권한을 확인할 수 있음.
헤더(Header).페이로드(Payload).서명(Signature)
ex)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxMjMiLCJyb2xlIjoiVVNFUiIsImV4cCI6MTcwOTAwMDAwMH0.abc123xyz456
일반적으로 Spring Security에서 UsernamePasswordAuthenticationFilter를 사용하여 로그인 처리를 함.
하지만 JWT를 사용할 경우, 서버가 사용자 세션을 관리하지 않고, 클라이언트가 인증 정보를 자체적으로 보유하게 됨.
따라서 요청이 들어올 때마다 JWT를 검증하고, 해당 정보를 기반으로 사용자를 인증하는 커스텀 필터를 구현해야함.
Spring Security의 GenericFilterBean을 상속하여 필터를 구현
@Component
@RequiredArgsConstructor
public class LoginFilter extends GenericFilterBean {
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper;
private final Utils utils;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
String token = getToken(request);
if (StringUtils.hasText(token)) {
loginProcess(token);
}
chain.doFilter(request, response);
}
}
클라이언트가 보낸 요청에서 JWT 토큰을 추출하는 메서드를 작성
private String getToken(ServletRequest request) {
HttpServletRequest req = (HttpServletRequest) request;
String bearerToken = req.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7).trim();
}
return req.getParameter("token");
}
JWT를 검증하고, 사용자 정보를 SecurityContextHolder에 저장하는 로직을 작성
private void loginProcess(String token) {
try {
String apiUrl = utils.url("/account", "memberservice");
HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(token);
HttpEntity<Void> entity = new HttpEntity<>(headers);
ResponseEntity<JSONData> response = restTemplate.exchange(apiUrl, HttpMethod.GET, entity, JSONData.class);
if (response.getStatusCode().is2xxSuccessful()) {
JSONData data = response.getBody();
if (data != null && data.isSuccess()) {
String json = objectMapper.writeValueAsString(data.getData());
Member member = objectMapper.readValue(json, Member.class);
List<SimpleGrantedAuthority> authorities = List.of(new SimpleGrantedAuthority(member.getAuthority().name()));
MemberInfo memberInfo = MemberInfo.builder()
.email(member.getEmail())
.password(member.getPassword())
.member(member)
.authorities(authorities)
.build();
Authentication authentication = new UsernamePasswordAuthenticationToken(memberInfo, token, memberInfo.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
-SimpleGrantedAuthority는 Spring Security에서 제공하는 클래스,
Spring Security에서 권한을 설정하고 체크할 때 필수적으로 사용
Spring Security에서 LoginFilter가 실행되도록 필터 체인에 등록해야 한다.
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, LoginFilter loginFilter) throws Exception {
return http
.addFilterBefore(loginFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.build();
}
}
addFilterBefore(loginFilter, UsernamePasswordAuthenticationFilter.class)을 사용하여 로그인 필터가 Spring Security 필터 체인에서 먼저 실행되도록 설정한다.
모든 요청을 인증된 사용자만 접근할 수 있도록 설정한다.