출처 : 개발자 유미(유튜)
이번 글에선 여태까지 작성해준 코드에 jwt를 주입하고 로그인 검증 성공 시 메서드만 작성하면 되는 것이기 때문에 제 깃허브 코드를 확인하시면 됩니다.
코드 : https://github.com/mmingoo/Spring-Security-JWT-
1. JWTUtil 주입
- LoginFilter : JWTUtil 주입
package com.example.jpamysql.jwt;
import com.example.jpamysql.dto.CustomUserDetails;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import java.util.Collection;
import java.util.Iterator;
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
private final JWTUtil jwtUtil;
public LoginFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil) {
this.authenticationManager = authenticationManager;
this.jwtUtil = jwtUtil;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
//클라이언트 요청에서 username, password 추출
String username = obtainUsername(request);
String password = obtainPassword(request);
//스프링 시큐리티에서 username과 password를 검증하기 위해서는 token에 담아야 함
//authentication Manager에게 username과 password를 던져주기 위해선 dto처럼 담아서 전달해야하는데
//UsernamePasswordAuthenticationToken이 그 역할을 함
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(username, password, null);
//token에 담은 검증을 위한 AuthenticationManager로 전달하여 검증함
return authenticationManager.authenticate(authToken);
}
//검증 성공 시
//로그인 성공시 실행하는 메소드 (여기서 JWT를 발급하면 됨)
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) {
CustomUserDetails customUserDetails = (CustomUserDetails) authentication.getPrincipal();
String username = customUserDetails.getUsername();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
Iterator<? extends GrantedAuthority> iterator = authorities.iterator();
GrantedAuthority auth = iterator.next();
String role = auth.getAuthority();
String token = jwtUtil.createJwt(username, role, 60*60*10L);
response.addHeader("Authorization", "Bearer " + token);
System.out.println(username);
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
response.setStatus(401);
}
}
- SecurityConfig에서 Filter에 JWTUtil 주입
import com.example.jpamysql.jwt.JWTUtil;
import com.example.jpamysql.jwt.LoginFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final AuthenticationConfiguration authenticationConfiguration;
private final JWTUtil jwtUtil;
public SecurityConfig(AuthenticationConfiguration authenticationConfiguration, JWTUtil jwtUtil) {
this.authenticationConfiguration = authenticationConfiguration;
this.jwtUtil = jwtUtil;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
// 시큐리티를 통해서 회원 정보를 저장하고, 회원가입하고, 검증할 땐 비밀번호를 해시로 암호화시켜서 검증하는 방식으로 진행
//BCryptPasswordEncoder 사용
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
//csrf disable
// 세션방식에선 세션이 고정되기 때문에 csrf 공격이 필수적으로 방어를 해줘야 한다.
//jwt는 세션을 stateless 상태로 관리하기 때문에 csrf에 대한 공격을 방어할 필요가 없기 때문에 disable
http
.csrf((auth) -> auth.disable());
//jwt 를 사용할 것이기 때문에
//From 로그인 방식 disable
http
.formLogin((auth) -> auth.disable());
//http basic 인증 방식 disable
http
.httpBasic((auth) -> auth.disable());
//경로별 인가 작업
http
.authorizeHttpRequests((auth) -> auth //
.requestMatchers("/login", "/", "/join").permitAll() // 로그인과 join은 모두 접근 가능
.requestMatchers("/admin").hasRole("ADMIN") // 관리자 페이지는 ADMIN만 접근 가능
.anyRequest().authenticated()); // 다른 요청은 로그인된 사람만 가능
//필터 추가 LoginFilter()는 인자를 받음 (AuthenticationManager() 메소드에 authenticationConfiguration 객체를 넣어야 함) 따라서 등록 필요
http
.addFilterAt(new LoginFilter(authenticationManager(authenticationConfiguration), jwtUtil), UsernamePasswordAuthenticationFilter.class);
//세션 설정
//stateless방식으로 해야함
http
.sessionManagement((session) -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
}
2. LoginFilter 로그인 성공 successfulAuthentication 메소드, 실패 unsuccessfulAuthentication 메소드 구현
import com.example.jpamysql.dto.CustomUserDetails;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import java.util.Collection;
import java.util.Iterator;
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
private final JWTUtil jwtUtil;
public LoginFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil) {
this.authenticationManager = authenticationManager;
this.jwtUtil = jwtUtil;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
//클라이언트 요청에서 username, password 추출
String username = obtainUsername(request);
String password = obtainPassword(request);
//스프링 시큐리티에서 username과 password를 검증하기 위해서는 token에 담아야 함
//authentication Manager에게 username과 password를 던져주기 위해선 dto처럼 담아서 전달해야하는데
//UsernamePasswordAuthenticationToken이 그 역할을 함
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(username, password, null);
//token에 담은 검증을 위한 AuthenticationManager로 전달하여 검증함
return authenticationManager.authenticate(authToken);
}
//검증 성공 시
//로그인 성공시 실행하는 메소드 (여기서 JWT를 발급하면 됨)
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) {
CustomUserDetails customUserDetails = (CustomUserDetails) authentication.getPrincipal();
String username = customUserDetails.getUsername();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
Iterator<? extends GrantedAuthority> iterator = authorities.iterator();
GrantedAuthority auth = iterator.next();
String role = auth.getAuthority();
String token = jwtUtil.createJwt(username, role, 60*60*10L);
response.addHeader("Authorization", "Bearer " + token);
System.out.println(username);
}
//실패 시 메서드
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
response.setStatus(401);
}
}
'JAVA > Spring Security JWT' 카테고리의 다른 글
JWT 검증 필터 (0) | 2024.07.04 |
---|---|
JWT 발급 및 검증 클래스 (0) | 2024.07.04 |
DB기반 로그인 검증 로직 (0) | 2024.07.03 |