이전 기사에서는 JWT를 생성하기 위해 JwtProvider 구현을 살펴보았지만 이번에는 비밀번호 암호화 + JWT 생성 구현을 사용하여 SpringSecurity를 사용하여 특정 리소스에 대한 인증 및 권한 부여 조건을 구현합니다.
사이드 프로젝트 JWT 애플리케이션 프로세스 기록
오늘은 주말동안 다양한 기술 블로그와 강의를 통해 jwt를 공부하고 jwt를 공부해서 사이드 프로젝트에 적용해봤는데 이만 줄이겠습니다.
JWT에 대한 잘 정리된 기사 및
kkkdh.tistory.com
WebSecurityConfigurer에서 상속받은 설정 파일을 이해하기 어려웠는데, 아무래도 스프링 시큐리티를 처음 공부해서 그런 것 같습니다.
먼저 WebSecurityConfigurer에서 상속한 후 configure 메서드를 재정의하는 메서드는 신중한 연구 끝에 더 이상 사용되지 않는 것으로 표시되었습니다.
따라서 다음과 같이 설정해야 합니다.
@RequiredArgsConstructor
@EnableWebSecurity
@Configuration
public class SecurityConfig {
// 생성자 DI
// 기존의 configure를 overriding하고, WebSecurityConfigurerAdapter를 상속받는 방식 대신
// 수동 빈 등록을 통한 자동 진행 과정을 변경됨
private final JwtProvider jwtProvider;
//PasswordEncoder interface의 구현체가 BCryptPasswordEncoder 임을 수동 빈 등록을 통해서 명시한다.
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
http
.httpBasic().disable()
.csrf().disable()
.cors().and()
.authorizeRequests()
// 글 작성시에는 ROLE_USER 권한이 있어야 한다.
.antMatchers("/login", "/sign-up", "/loginerror").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // jwt 사용하는 경우
.and()
.addFilterBefore(new JwtFilter(jwtProvider), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
코드에 쓰여진 대로 설정 방식을 오버라이드하는 방식이 직접 Bean을 등록하는 방식으로 변경되었습니다.
여러 블로그를 뒤져보고 공식 문서를 찾아보니 필터 체인 구성에서 LoginForm과 Logout 메소드를 연결하여 스프링 시큐리티에서 기본적으로 제공하는 로그인과 로그아웃 방식을 설명했다.
UserDetails, UserDetailsService 같은 객체를 오버라이드하는 메서드를 참조해 보았지만 해결되지 않아 직접 필터를 등록하고 인증을 처리하기로 했습니다.
addFilterBefore 사용 방법 UsernamePassword인증 필터 직접 구현한 필터를 미리 추가할 수 있었습니다.
그 부분입니다.
@RequiredArgsConstructor
public class JwtFilter extends OncePerRequestFilter {
@Autowired
private MemberService memberService;
private final JwtProvider jwtProvider;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
final String authorization = request.getHeader("Authorization");
logger.info("authorization : " + authorization);
if(authorization == null || !
authorization.startsWith("Bearer ")){
logger.error("authorization is null");
filterChain.doFilter(request, response);
}
// Authorization에서 token 추출
String token = authorization.split(" ")(1);
// token 유효성 검증
boolean expired = jwtProvider.isExpired(authorization);
if(expired){
logger.error("token is expired");
filterChain.doFilter(request, response);
}
String userName = (String)jwtProvider.parseJwtToken(authorization).get("userEmail");
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(userName, null, List.of(new SimpleGrantedAuthority("USER")));
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
// filter chain의 다음 filter를 호출
System.out.println("end of authorization with jwt filter");
filterChain.doFilter(request, response);
}
}
위와 같이 별도로 JwtFilter를 구현했습니다.
사실 위의 코드는 다른 사람의 글을 보고 얼핏 이해하기가 너무 어려워 보입니다.
하지만 간단히 말해서: OncePerRequest 필터에서 상속하고 필터를 구현하고 doFilterInternal 메서드를 재정의하여 필터의 내부 작업을 정의합니다.
이름에서 알 수 있듯이 OncePerRequestFilter는 사용자 요청당 한 번만 실행되는 필터를 만들기 위한 추상 클래스입니다.
(나중에 자세히 알아봐야 할듯..)
처음에는 이 필터 체인 개념이 뭔지 몰랐는데, 그 부분은 아래 링크를 참고하시면 좋을 것 같습니다.
아키텍처::스프링 시큐리티
Spring Security의 서블릿 지원은 서블릿 필터를 기반으로 하므로 먼저 일반적으로 필터의 역할을 살펴보는 것이 도움이 됩니다.
다음 이미지는 단일 HTTP 요청에 대한 처리기의 일반적인 계층을 보여줍니다.
클라이언트가 AP에 요청을 보냅니다.
docs.spring.io
따라서 필터 체인은 요청을 이행하는 서블릿을 연결하는 DispatcherServlet 이전에 실행되고 중간에는 액세스할 수 있는 SecurityContextHolder에 인증 정보(인증)를 저장하기 위해 jwt를 사용하여 인증 프로세스를 정의하는 JwtFilter가 실행됩니다.
사실 권한 생성 시 인증 정보를 보낼 때 멤버 엔터티에 원래 등록된 모든 인증 정보를 제공해야 하는데 USER라는 권한만 권한 부여로 설정되어 있었습니다.
이 부분도 개선이 필요하고 몇 일간 스프링 시큐리티를 붙어서 배워봤는데 바로 필요한 기능만 이해한 것 같아서 더 배워보고 정리하면서 작업해야 할 것 같습니다.
프로젝트.
기본적으로 토큰을 통해 사용자 정보를 인증하고 공유하려면, 입증 물체에 사용자 세부 정보 표준 방식은 개체에 대한 사용자 정보를 저장하고 컨트롤러 구성 요소에서 사용할 수 있는 올바른 권한을 이 방식으로 생성된 인증에 부여하는 것 같습니다.
하지만 이번 구현 결과, 로그인 과정에서 jwt를 생성하여 쿠키로 전달하고, jwt의 유효성을 검증하기 위해 사용자 요청 메시지의 헤더를 확인하는 과정을 구현할 수 있었다고 생각합니다.
에.
jwtProvider를 구현하기 전까지는 바로 하고 싶었지만 Spring Security에 이렇게 많은 콘텐츠가 있을 줄은 몰랐습니다.
이 영상을 참고하면 스프링 시큐리티 사용이 쉬웠던 것 같아서 여기에 남깁니다!