728x90
📌 이번 글에서는 프로젝트 생성부터 SecurityConfig의 초기 설정을 설명합니다.
🤗 저의 스프링 시큐리티 구현은 아래와 같은 시나리오를 기준으로 합니다.
- 프론트 엔드와 백엔드가 나뉘어 진행되는 프로젝트를 기반으로 하여 스프링 시큐리티 설정에서 로그인 페이지에 대한 설정을 따로 하지 않음
- JWT 토큰 인증 방식을 사용함
- 토큰 관리에 redis를 이용함
🙉 이전 글 보기
첫 번째 글부터 정독하시면 보다 쉽게 이해하실 수 있습니다!
https://suzuworld.tistory.com/438 - 당신의 첫 프로젝트를 위한 스프링 시큐리티 톺아보기
📖 목차
스프링 시큐리티 톺아보기
SecurityConfig 구성하기 (현재 글)
⚙️프로젝트 생성
- 스프링부트 3.x, 자바 17로 테스트 코드를 작성합니다.
- 의존성을 참고하시어 프로젝트를 생성해 주세요.
🚗 프로젝트 실행해 보기
- 생성된 프로젝트를 바로 실행해 봅시다.
- 스프링 시큐리티가 적용되어 기본적으로 사용될 비밀번호가 생성되었음을 확인할 수 있습니다.
- 이후 프로젝트에서 설정한 포트번호에 /login이라는 엔드포인트로 접속하면 시큐리티에서 제공하는 기본 로그인 폼 페이지가 나옴을 확인할 수 있습니다.
- 기본 값은 username(id)은 user, password는 실행할 때 콘솔에 나오는 비밀번호입니다.
- 저의 경우에는 위의 사진에 나온 것과 같이 Using generated security password: c151d88c-71ab-4628-8628-ebd64dd0ea36입니다.
- 만약 기본 username과 비밀번호를 변경하고 싶다면, application.properties 파일이나 application.yml 파일에 사용자 정의 설정을 추가할 수 있습니다.
- 로그인을 하면 기본적인 스프링부트 에러페이지가 나옵니다.
- 당연하죠? 아직 아무것도 만들지 않았으니까요.
🛠️ 시큐리티 Config
- 본격적으로 시큐리티 설정을 해봅시다.
@Configuration
- 스프링 설정 클래스임을 나타내어 스프링에게 알립니다.
@EnableWebSecurity
- 웹 시큐리티를 활성화합니다.
@RequiredArgsConstructor
- 클래스의 모든 final이 붙은 필드에 대해 생성자를 자동으로 생성해 주는 기능을 합니다.
- 이를 통해 불필요한 코드를 줄이고, 코드의 가독성을 높일 수 있습니다.
- 다음 글에서 사용합니다.
extends SecurityConfigurerAdapter
- ScurityConfigurerAdapter는 스프링 시큐리티의 설정을 정의할 때 사용하는 추상 클래스입니다.
- 이 클래스를 상속받아 필요한 설정을 오버라이드하여 구현합니다.
<DefaultSecurityFilterChain, HttpSecurity>
- 제네릭 타입 <DefaultSecurityFilterChain, HttpSecurity>는 설정할 시큐리티 필터 체인과 설정 객체를 지정합니다.
- DefaultSecurityFilterChain : 기본 시큐리티 필터 체인으로, 여러 시큐리티 필터들이 포함되어 있습니다.
- HttpSecurity : HTTP 보안 설정을 구성하는 객체로 특정 URL 패턴에 대한 보안 설정을 정의할 수 있습니다.
⚙️ SecurityFilterChain
- 구글링을 통해 나오는 WebSecurityConfigurerAdapter를 상속하고 configure 메서드를 오버라이딩하는 방식은 스프링 부트 3.x 이상부터 스프링 시큐리티 6 이상 버전이 적용되면서 deprecated 되었습니다. 따라서 현재는 @Bean으로 등록하여 구현하는 방식을 사용하시면 됩니다.
- 각종 설정은 HttpSecurity를 통해 이뤄집니다.
👨🏻🔬 내부 설정
- 위 사진과 같이 HttpSecurity를 이용하여 각종 설정을 추가할 수 있습니다.
- 이곳에서 csrf, oauth2, 각종 필터, 핸들러, 로그인 페이지 등 다양한 시큐리티 관련 설정을 할 수 있습니다.
- 이 예시에서는 구현에 필요한 최소한의 기능만을 다룹니다.
- oauth2나 로그인페이지에 대한 추가 설정이 필요하면 구글링을 통해 찾아보시면 생각보다 쉽게 하실 수 있습니다.
- 핵심은 전체적인 동작원리에 대한 이해라고 생각합니다.
- 일단 위 설정에 대해 알아봅시다.
- 위 내용이 전부는 아닙니다. 처음에 다 추가하는 것보다 순서대로 추가하는 것이 좋을 것 같아서 다른 설정은 해당 내용이 나올 때 추가하겠습니다.
✨ 람다(Lambda)
- 스프링 시큐리티 6.1 이후부터는 시큐리티 설정을 구성할 때 람다 DSL을 이용합니다.
- 람다식은 익명 함수를 간단하게 표현하는 방법입니다.
- ::나 ->와 같은 식의 표현을 처음 보신 분들은 별 거 없고 그냥 함수 표현 방식이 람다라는 것이니 넘어가셔도 됩니다.
- 궁금하신 분들은 아래 글을 참고해 주세요.
https://docs.spring.io/spring-security/reference/migration-7/configuration.html
csrf(AbstractHttpConfigurer::disable)
- csrf(사이트 간 요청 위조)는 세션이나 쿠키 인증 방식의 취약점을 노린 공격 방식입니다.
- 여기에서는 JWT 토큰 인증 방식을 사용할 것이므로 해당 공격에 대한 방어가 필요 없어 설정을 비활성화하는 것입니다.
- 이 부분에 대한 설명만으로 글 여러 개를 작성이 가능할 정도로 방대한 분량이므로 잘 요약 정리된 블로그 글을 소개해드리겠습니다.
.httpBasic(AbstractHttpConfigurer::disable)
- HTTP Basic 인증은 사용자 이름과 비밀번호를 Base64로 인코딩하여 HTTP 헤더에 포함시켜 서버에 전송하는 방식으로 이것 역시
- JWT 토큰 인증 방식을 사용하기 때문에 비활성화합니다.
.formLogin(AbstractHttpConfigurer::disable)
- 스프링 시큐리티가 기본으로 제공하는 로그인 폼 기능을 비활성화합니다.
- 위에서 제가 보여드린 기본 로그인 페이지를 말합니다.
- 저희는 백엔드 영역만 만들 거니까 필요 없습니다.
.authorizeHttpRequests((authorizeRequests) -> authorizeRequests
.requestMatchers("/login").permitAll()
.anyRequest().authenticated())
- HTTP 요청에 대한 인가 규칙을 설정합니다.
- 한 프로젝트에서 백엔드 파트는 여러 api를 만들 것이고, 클라이언트는 여러 페이지를 만들어 api를 붙이잖아요. 이때 백엔드에서 만든 여러 api 엔드포인트에 어딘가는 권한이 필요할 것이고, 어딘가는 권한이 필요 없을 텐데요.
- 예를 들면 로그인한 상태로 접근 요청을 해야 하는 api와 그렇지 않은 api를 구분하는 설정을 하는 부분입니다.
- 람다식을 사용하여 HTTP 요청에 대한 인가 규칙을 설정합니다.
- reuestMatcher는 요청 api 엔드포인트를 적습니다.
- 저의 경우에는 /login이라는 엔드포인트는 누구나 접근 가능하게 설정했습니다.
- 로그인할 페이지를 로그인 사용자만 접근가능하게 한다면 아무도 로그인 할 수 없겠죠?
- 로그인 외 요청은 로그인 사용자만 이용 가능한 굉장히 폐쇄적인 애플리케이션입니다ㅋ.ㅋ
🤔 특정 엔드포인트에 대한 자세한 설정
- 특정 엔드포인트에 대해 굉장히 자세한 접근 권한 등을 설정할 수 있습니다.
- 저도 전부 사용해보지는 않았지만 메서드 명으로 기능을 짐작할 수 있습니다.
- 예를 들면 permitAll은 해당 경로는 누구나 접근 가능하다. demyAll은 누구도 접근할 수 없다. 뭐 그런 거겠죠?
📑 대표적인 패턴 경로 매칭
.requestMatchers("/admin") // /admin 경로와 정확히 일치하는 요청
.requestMatchers("/admin/*") // /admin/ 하위의 한 수준 경로와 매칭, 예: /admin/user
.requestMatchers("/admin/**") // /admin/ 하위의 모든 경로와 매칭, 예: /admin/user/edit
.requestMatchers("/*.html") // 루트 디렉토리의 모든 .html 파일 요청과 매칭
.requestMatchers("/admin/{regex:[a-z]+}") // 정규 표현식을 사용한 매칭
.requestMatchers(HttpMethod.GET, "/admin/**") // GET 요청에 대해 /admin/ 하위의 모든 경로와 매칭
- 위와 같이 접근 경로에 대한 패턴 매칭을 통해 커스텀하게 설정이 가능합니다.
😄 예시
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/user/**").hasRole("USER")
.requestMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
- 이런 식으로 설정하면 됩니다.
.requestMatchers("/admin/**").hasRole("ADMIN")
- /admin이 포함된 모든 엔드포인트는 로그인 사용자 중 ADMIN이라는 Role을 가진 사람만이 접근 가능하다는 설정입니다.
.requestMatchers("/user/**").hasRole("USER")
- /user가 포함된 모든 엔드포인트는 로그인 사용자 중 USER라는 Role을 가진 사람만이 접근 가능하다는 설정입니다.
.requestMatchers("/", "/home").permitAll()
- "/"나 "/home"은 모두 접근 가능한 엔드포인트라는 설정입니다.
.anyRequest().authenticated()
- 이외의 모든 요청은 특별한 Role 없이 로그인한 사용자는 모두 접근 가능하다는 설정입니다.
.sessionManagement(sessionManagement -> sessionManagement
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
- 스프링 시큐리티가 세션을 생성하거나 유지하지 않도록 설정하는 부분입니다.
- 이 부분 역시 JWT 토큰 인증 방식을 사용하기 때문에 세션을 만들지 않도록 설정합니다.
return http.build();
- 위에서 설정한 구성을 빌드하고 리턴합니다.
🏃🏻 다음으로 넘어가기
- 현재까지의 설정으로는 아무것도 할 수가 없겠죠.
- 이제부터는 이 설정을 적용받은 필터를 추가하고 로그인이나 로그아웃 등의 작업을 구현해야 합니다.
- 위와 같이 addFilter와 같은 메서드 등으로 필터를 적용하고, 순서를 정할 수 있습니다.
- 다음 글에서는 로그인 인증을 처리하는 필터를 만들어보겠습니다.
참고
뤼튼
https://velog.io/@sehwan24/Lamda-DSL을-이용한-HttpSecurity-WebSecurity-구성
728x90
'[Spring] > Spring Security' 카테고리의 다른 글
당신의 첫 프로젝트를 위한 스프링 시큐리티(4) - jwt 발급과 redis (1) | 2024.09.08 |
---|---|
당신의 첫 프로젝트를 위한 스프링 시큐리티(3) - AuthenticationManager, AuthenticationProvider, UserDetailsService, UserDetails (0) | 2024.08.04 |
당신의 첫 프로젝트를 위한 스프링 시큐리티(2) - 인증 방식 개념과 AuthenticationFilter (11) | 2024.07.20 |
당신의 첫 프로젝트를 위한 스프링 시큐리티 톺아보기 (4) | 2024.07.06 |