|
https://docs.spring.io/spring-security/site/docs/5.2.1.RELEASE/reference/htmlsingle/
Spring Security 5 OAuth 총정리: 클라이언트부터 서버까지 https://www.youtube.com/watch?v=-YbqW-pqt3w
"데이터의 중요성이 커지면서 고객 데이터의 효율적인 관리 능력 또한 중요해지고 있다. 쇼핑몰 또는 웹 페이지 같은 온라인 플랫폼을 운영하는 IT 기업이라면 사용자 권한 관리의 정교함이 특히나 중요해지고 있다. 홈페이지에 인증 및 권한 기능을 빠르게 부여해 인증 및 권한 보호 기능을 손쉽게 추가할 수 있는 ‘Spring Security’의 요구가 많아지고 있다. 원하는 데이터에 안전하고 빠르게 접근하도록 지원하며, 동시에 고객 정보 및 거래 데이터를 승인되지 않는 사용자의 접근으로부터 보호하여, 데이터를 효율적으로 관리하는 데 도움을 주는 Spring Security의 사용 이유 및 설정 방법에 대한 이해는 필수라고 할 수 있다."
Spring Security란?
Spring Security는 인증, 권한 관리 그리고 데이터 보호 기능을 포함하여 웹 개발 과정에서 필수적인 사용자 관리 기능을 구현하는데 도움을 주는 Spring의 강력한 프레임워크이다. 일반적으로 개발 시 가장 먼저 작업하는 부분이 사용자 관리 부분으로 가볍게는 회원가입부터 로그인, 로그아웃, 세션 관리, 권한 관리까지 온라인 플랫폼에 맞춰 다양하게 작업되는 인가/보안 기능은 개발자에게 많은 시간을 요구한다. Spring 생태계에서 이러한 요구사항을 효과적으로 지원하기 위해 개발된 것이 Spring Security로 개발자들이 보안 관련 기능을 효율적이고 신속하게 구현할 수 있도록 도와준다.
Spring Security를 사용하는 이유
자바 개발자들이 보안 기능을 추가할 때 Spring Security를 사용하는 이유는 보안에 필요한 기능들을 제공하기 때문이다. Spring Security는 개발 구조가 Spring이라는 프레임워크 안에서 활용하기 적합한 구조로 설계되어 있어, 보안 기능을 추가할 때 활용하기 좋다. 프레임워크를 사용하지 않고 코드를 직접 작성할 경우 Spring에서 추구하는 IoC/DI 패턴과 같은 확장 패턴을 염두 해서 인증/인가 부분을 직접 개발하기는 쉽지 않은데, Spring Security에서는 이와 같은 기능들을 제공해 주기 때문에 개발 작업 효율을 높일 수 있다. 때문에 많은 개발자들이 Spring을 사용할 경우에는 Spring Security를 활용하여 보안 기능을 추가하고 있으며, Spring Security에서 제공하는 기능 외에 추가적인 기능이 필요할 경우 Spring Security를 기반으로 기능을 추가하여 업무 효율을 향상시키고 있다.
인증(Authentication)
: 인증은 사용자의 신원을 입증하는 과정이다. 간단히 말해 어떤 사이트에 아이디와 비번을 입력하여 로그인 하는 과정이다.
인가(Authorization)
: '권한부여'나 '허가'와 같은 의미로 사용된다. 즉, 어떤 대상이 특정 목적을 실현하도록 허용하는 것을 말한다. 예를 들면, 파일 공유 시스템에서 권한별로 접근할 수 있는 폴더가 상이하다. 관리자는 접속이 가능하지만 일반 사용자는 접속할 수 없는 경우에서 사용자의 권한을 확인하게 되는데, 이 과정을 인가라고 한다.
스프링 시큐리티는 필터 기반으로 동작한다.
Spring Security는 '인증'과 '권한'에 대한 부분을 Filter 흐름에 따라 처리하고 있다. Filter는 DispatcherServlet을 만나기 전에 적용되므로 가장 먼저 URL 요청을 받지만, Interceptor는 Dispatcher와 Controller 사이에 위치한다는 점에서 적용 시기의 차이가 있다.
그러므로 Spring Security는 보안과 관련하여 다양한 옵션을 제공해주기 때문에 개발자는 일일이 보안 관련 로직을 작성하지 않아도 된다는 장점이 있다. 기본 구조는 다음과 같다.
Spring Security 구조 확인 https://www.elancer.co.kr/blog/view?seq=235
DelegatingFilterProxy :
Spring Security가 서블릿 필터로 동작하지만 실제 구현은 스프링에게 위임한다. 왜냐하면 Spring Security가 스프링에게 구현한 자원을 보호하기 위해 Bean접근이 불가능하기 때문이다.
스프링(부트)가 위임받아 스프링시큐리티 필터를 구현하는 인터페이스를 DelegatingFilterProxy라고 한다.
Spring Security를 사용하면, DelegatingFilterProxy가 생성된다. 이는 Spring Boot 사용시 SecurityFilterAutoConfiguration을 통해서 DelegatingFilterProxyRegistrationBean 빈을 생성한 후 DelegatingFilterProxy 필터를 생산하는 빈으로 ServletContextInitializer을 통해 서블릿 컨테이너의 필터 체인에 DelegatingFilterProxy을 등록하도록 지원한다.
원리 : 스프링 시큐리티는 서블릿(Servlet) 필터로 동작하며, 서블릿 필터 중 한 개로 등록되어 보안설정을 수행한다. 이 때 SecurityFilterChain 기능은 여러 개의 체인으로 구성되어 있다. SecurityFileterChain으로 관리되는 필터 체인을 security filter라고 부르는데, 각 필터는 인증/인가의 고유한 기능을 담당한다.
예를 들어 로그인을 담당하는 필터, 세션을 관리하는 필터, 로그아웃을 담당하는 필터가 존재한다. https://docs.spring.io/spring-security/reference/servlet/architecture.html
FilterSecurityInterceptor
마지막에 위치한 필터이며 인증된 사용자에 대해 요청의 승인/거부 여부를 결정한다.
- 인증 객체 없이 자원에 접근 시도할 경우 AuthenticationException 발생.
- 권한 없이 자원에 접근할 경우 AccessDeniedException 발생.
- 권한 처리는 AccessDecisionManager에서 처리한다.
즉, SecurityContext 내에 Authentication 객체가 존재하면, SecurityMetadataSource에서 요청한 자원에 필요한 권한 정보를 찾아서 AccessDecisionManager에 전달한다.
* 이해를 위해 스프링 시큐리티 공식문서의 그림을 첨부한다.
스프링 공식 문서에 나온 순서는 다음과 같다.
1) FilterSecurityInterceptor는 SecurityContextHolder에서 Authentication 객체를 얻는다.
2) FilterSecurityInterceptor HttpServletRequest, HttpServletResponse, FilterChain으로부터 FilterInvocation을 만든다.
3) ConfigAttributes를 얻기 위해 SecurityMetadataSource에 FilterInvocation을 넘긴다.
4) Authentication, FilterInvocation, ConfigAttributes를 AccessDecisionManager로 넘긴다.
5) 인증이 실패하면, AccessDeniedException을 발생시킨다. 이 경우엔 위에서 언급했던 ExceptionTranslationFilter가 AccessDeniedException를 처리한다.
6) 인증 성공시 FilterSecurityInterceptor는 어플리케이션을 다음 동작으로 진행시킨다.
securityfilterchain 내부동작 (https://docs.spring.io/spring-security/reference/)
https://passionfruit200.tistory.com/426
스프링시큐리티 구현 원리 https://malwareanalysis.tistory.com/143
HttpSecurity는 Spring Security 프레임워크에서 사용되는 클래스이다. 주로 웹 애플리케이션의 보안 구성을 설정할 때 사용된다. Spring Security는 Java 기반 애플리케이션에서 인증과 권한 부여를 위한 포괄적인 보안 기능을 제공하는 프레임워크이다. HttpSecurity는 이러한 보안 설정을 세밀하게 제어할 수 있도록 하는 주요 구성 요소 중 하나이다.
HttpSecurity의 역할 : 애플리케이션의 특정 HTTP 요청에 대해 어떤 보안 정책을 적용할지 구성하는 데 사용된다. 이를 통해 개발자는 다양한 보안 규칙을 쉽게 설정할 수 있다.
Spring Security의 동작 구조 : 의존성을 추가한 경우 아래 그림과 같이 WebSecurityConfigurerAdapter 클래스가 실행되게 된다.
WebSecurityConfigurerAdapter 클래스는 스프링 시큐리티의 웹 보안 기능의 초기화 및 설정들을 담당하는 내용이 담겨있으며 내부적으로 getHttp() 메서드가 실행될 때 HTTPSecurity 클래스를 생성하게 된다. 이 때 HTTPSecurity는 인증/인가 API들의 설정을 제공한다.
🎈주요 메서드와 기능
1) authorizeRequests(): 요청에 대한 권한 부여 규칙을 설정한다.
- antMatchers("/admin/**").hasRole("ADMIN"): "/admin/**" 경로에 대한 요청은 "ADMIN" 역할을 가진 사용자만 접근할 수 있도록 설정.
- anyRequest().authenticated(): 나머지 모든 요청은 인증된 사용자만 접근할 수 있도록 설정.
2) formLogin(): 폼 기반 로그인을 설정.
- loginPage("/login"): 사용자가 접근해야 하는 로그인 페이지의 URL을 설정.
- permitAll(): 로그인 페이지는 누구나 접근할 수 있도록 설정.
3) httpBasic(): HTTP 기본 인증을 활성화. 이 방식은 HTTP 헤더를 통해 사용자 이름과 비밀번호를 전송하는 간단한 인증 방법을 제공.
4) csrf(): CSRF(Cross-Site Request Forgery) 공격에 대한 방어를 설정.
- disable(): CSRF 보호 기능을 비활성화. 일반적으로 REST API에서는 이 기능을 비활성화 할 수 있다.
5) logout(): 로그아웃 설정을 구성.
- logoutUrl("/logout"): 로그아웃 URL을 설정.
- logoutSuccessUrl("/login?logout"): 로그아웃 성공 후 리다이렉션할 URL을 설정.
6) rememberMe(): 'Remember Me' 기능을 설정. 사용자가 로그인한 상태를 유지하도록 설정할 수 있다.
7) sessionManagement(): 세션 관리 정책을 설정.
- maximumSessions(1): 한 사용자 당 허용되는 최대 세션 수를 설정할 수 있다.
샘플 보기 --------------------------------------------
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll() // 루트와 /home 경로는 누구나 접근 가능
.antMatchers("/admin/**").hasRole("ADMIN") // /admin/** 경로는 ADMIN 역할을 가진 사용자만 접근 가능
.anyRequest().authenticated() // 나머지 모든 요청은 인증 필요
.and()
.formLogin()
.loginPage("/login") // 커스텀 로그인 페이지 설정
.permitAll() // 로그인 페이지는 누구나 접근 가능
.and()
.logout()
.permitAll(); // 로그아웃 누구나 접근 가능
}
}
이 코드는 기본적으로 애플리케이션의 보안을 설정하고, 특정 경로에 대한 접근 권한을 관리하며, 폼 기반 로그인 및 로그아웃 기능을 설정한다. HttpSecurity는 Spring Security에서 HTTP 요청에 대한 보안 구성을 세밀하게 설정할 수 있는 중요한 클래스이다. 이를 통해 개발자는 애플리케이션의 다양한 보안 요구 사항을 충족시키기 위해 여러가지 설정을 정의할 수 있다.
참고 : 역할(Role)
User.builder()
.username(String.valueOf(jikwon.getJikwon_no()))
.password(passwordEncoder.encode(jikwon.getJikwon_name()))
.roles("USER") // 해당 사용자에게 "USER" 권한을 부여
.build();
.roles("USER")는 Spring Security에서 사용자의 권한(역할)을 "USER"로 지정하는 부분이다. 이 메서드는 사용자에게 특정 권한을 부여하여, 이후 애플리케이션에서 해당 사용자가 어떤 작업을 수행할 수 있는지를 결정하는 데 사용된다.
Spring Security에서는 권한을 지정할 때 보통 "ROLE_" 접두어를 붙여 처리한다. 따라서 .roles("USER")를 설정하면 내부적으로는 "ROLE_USER" 권한을 사용자에게 부여하게 된다. 이 권한은 이후 @Secured, @PreAuthorize, @PostAuthorize와 같은 애노테이션이나, Security 설정에서 접근 제어를 수행할 때 사용된다.
* 권한과 역할의 차이:
- 권한(Authority): 역할보다 세밀하게 제어할 수 있는 개념으로, 특정 리소스에 대한 접근 권한을 나타낼 수 있다. 예를 들어 "READ_PRIVILEGES" 또는 "WRITE_PRIVILEGES" 등이 있다.
- 역할(Role): 보통 특정 작업 그룹을 나타내며, "USER", "ADMIN" 등이 그 예이다. Spring Security에서는 역할을 "ROLE_" 접두어로 표시한다.
따라서 .roles("USER")는 해당 사용자가 "ROLE_USER"라는 역할을 가지게 하며, 이 역할에 따라 애플리케이션 내에서 허용된 작업들을 수행할 수 있게 된다.