스프링 시큐리티란 어떤 방식으로 동작할까?
스프링 시큐리리?
스프링 시큐리티는 인증, 인가를 지원하고 주요 공격으로 부터 어플리케이션을 보호해 주는 프레임워크이다. 시큐리티를 통해서 사용자 인증, 권한 부여, 보안 설정등을 손쉽게 관리할수있으며, 이를 통해 사용자의 민감한 데이터를 안전하게 관리및, 보호할수 있는스프링 기반의 보안 프레임워크이다.
인증(Authentication)이란?
인증은 특정 리소스에 접근하려고 하는 사용자가 누구인지를 확인할 때 사용한다. 흔히 우리가 로그인할때 아이디와 비밀번호를 통해 접속하는것을 인증이라고한다.(스프링 시큐리티는 기본적은 인증기능을 제공한다.)
인가(Authorization)란?
인가란 쉡게 말하자면 특정 페이지에 접속할때 권한을 설정해 특정기능을 사용여부를 판단하는것을말한다.
인가에는 크게 3가지 방법이있다.
- 역활 기반 접근제어(Role-Based Access Control, RBAC) : 사용자에게 역활을 할당하고 해당 역활에 따라 권한을 부여하는 방식
- 자원 기반 접근제어(Resource-Based Access Control, ReBAC) : 개별 자원에 대한 권하을 설정하고 관리하는 방식
- Attribute-Based Access Control(ABAC): 이방식은 역할이나 개별 자원에만 국한 되지않고 다양한 속성에 기반하여 접근을 제어하는 방식이다.
이런방식중에 스프링 시큐리티는 역활기반 접근제어(RBAC) 방식의 인가를 사용하고있으며, 3개의 등급을 부여하여 관리한다.
- ROLE_USER(사용자) : 가장 기본적인 사용자권한이다.
- ROLE_ADMIN(관리자) : 높은 권한을 가진 관리자 권한으로, 일반 사용자에 비해 더많은 기능과 리소스를 사용 가능
- 기타 사용자 정의 역활 : 필요에 따라 사용자가 집적 정의하는 추가적인 역활을 정의할수 있다.
비밀번호 인코딩
새로운 홈페이지에 회원가입을 하고나면 DB에 비밀번호가 저장된다. 하지만 인코딩되지않은 비밀번호를 그대로 저장하게되면 해커가 암호를 쉽게 회득할수있다. 그래서 인코딩을하여 DB에 저장해야한다. 시큐리티는 PasswordEncoder 인터페이스를 통해서 비밀번호를 안전하게 저장할수 있도록 단반향변환을 해준다.
passwordEncoder
String idForEncode = "bcrypt";
Map encoders = new HashMap<>();
encoders.put(idForEncode, new BCryptPasswordEncoder());
encoders.put("noop", NoOpPasswordEncoder.getInstance());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
encoders.put("sha256", new StandardPasswordEncoder());
PasswordEncoder passwordEncoder =
new DelegatingPasswordEncoder(idForEncode, encoders);
hashMap에 PasswordEncoder를 사용해 5가지의 종류를 사용하여 인코딩하여 저장하고 출력하면 아래와 같이 출력되는것을 볼수있따.
{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG// BCryptPasswordEncoder
{noop}password // NoOpPasswordEncoder
{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc // (3)
Pbkdf2PasswordEncoder());
{scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc= // SCryptPasswordEncoder());
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0 //
StandardPasswordEncoder());
- BcryptPasswordEncoder : 널리 사용되는 bcrypt알고리즘을 통해 비밀번호를 해싱한다. bcrypt 는 의도적으로 느리게 동작하여 비밀번호를 해독하기 힘들게한다.( $2a$....으로 출력되는 것은 Bcrypt 알고르즘을 통해 암호화한것이다.)
동작방식
username(Id) : testId
password : 1234
값을 통해 로그인 한다고 가정
- Http Request(Clinet) : 용자가 'testId' 와 '1234'를 입력하고 로그인시도
- AuthenticationFilter : 'UsernamePasswordAuthenticationToken' 토큰을 셍성한다.
- UsernamePasswordAuthentication Token : testId와 1234가 담긴 ' UsernamePasswordAuthenticationToken' 객체를 생성한다.
String username = "testId";
String password = "1234";
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
- AuthenticationManager : 만들어진 토큰이 'AuthenticationManager' 로 전달된다. 'AuthenticationManager' 는 등록된 'AuthenticationProvider' 를 사용하여 사용자 인증을 한다.
- AuthenticationProvider: ' AuthenticationProvider'는 받은 토큰을 기반으로 실제 인증을 수행한다. 인증을 위해서 'UserDetailService'를 호풀하여 사용자 정보를 요청한다.
- UserDetailsService : 'testId' 기반으로 DB나 다른저장소에서 검색을한다. 해당 사용자 정보를 'UserDatils'객체로 반환한다.
- UserDatails : ' UserDatails ' 객체는 사용자의 세부정부(아이디.비밀번호 등)을 포함하고있어 포함된 사용자의 정보와 UsernamePasswordAuthenticationToken토큰을 비교하여 인증을 수행한다.
- SecurityContextHolder : 인증이 성공하면 'AuthentocationManager'는 'Authentication' 객체를 생성하며 반환한다. ' Authentication' 객체는 사용자의 인증정보와 권환을 담고 있으며, 성공적으로 인증된 사용자 정보는 'SecurityContextHolder'에 저장되어 보안 컨텍스트를 유지한다.
이러한 동작 방식으로 인증을 성공하게되면 'SecurityContextHolder' 에 정보가 저장되어 해당 세션에서 인증된 사용자여서 접근 및 권한관리하 가능하다.
Filter란?
시큐리티는 클라이언트-> 보안필터 -> 서플릿 순으로 이뤄지며 아래와 같은 방식으로 사용된다.
일반적으로는 보안이 적용되지않은 상태는 Clinet -> Servlet 로 진행되지만, 시큐리티가 적용된 상태는 중간에 필터를 둬서 보안을 적용한다. 이러한 필터가 여러개를 함께 사용될수 있다. 이를 묶어 FilterChain 이라고 부른다.
앞서 설명한 동작방식은 아래 그림과 같은 방식으로 인증을 하게된다.
- DelegatingFilterProxy :서블릿 필터를 스프링 애플리케이션 컨텍스트의 빈으로 등록하기위한 클래스, 보안관련 필터들을 스프링의 빈으로 등록할수 있다. 즉 스프링 시큐리티에서 보안관련 필터들을 스프링빈으로 등록한다.
- FilterChainProxy : 여러 보안 필터들을 구성하고 관리하는 역활을 하는 클래스, 각 'SecurityFilterChain'은 'FilterChainProxy'에서 관리되어 클라이언트이 요청을 처리하는데 사용된다. 즉 'SecurityFilterChain' 을 관리하며, 클라이언트 요청이 도착하면 적절한 보안 필터체인을 선택하여 요청을 처리한다.
- SecurityFilterChain : 실제보안 필터 체인을 구성하는 인터페이스이다. 하나의 애플리케이션안에 여러개의 SecurityFilterChain이 존재할수 있으며, 서로 다른 보안 요구 사항에 다라서 필터들을 구성하고 설정할수있다. 즉 각각의 보안 필터체인을 설정하고, 실제로 클라이언트의 요청에 대한 보안을 담당한다.
마치며
기본적인 시큐리티가 어떻게 동작하는지 간단하게 알아봤지만. 동작하는 방법은 알지만 어떤식으로 사용하는지 모를수있다. 그래서 다음 포스팅에서는 간단한 회원가입을 만들고 시큐리티를 이용한 로그인하는 방법을 알아보겠다.
참고자료