서비스 인증과 인가
보안과 관련된 용어와 개념 및 스프링에 보안을 적용할 때 사용하는 스프링 시큐리티(spring security)를 알아보겠습니다.
보안 용어
인증(authentication)
- 사용자가 누구인지 확인하는 단계를 의미한다.
- 인증의 대표적인 예시(ex. 로그인)
- 로그인은 데이터베이스에 등록된 아이디와 패스워드를 사용자가 입력한 아이디 및 비밀번호를 비교해서 일치 여부를 확인하는 과정이다.
- 로그인에 성공하면 애플리케이션 서버는 응답으로 사용자에게 토큰(token)을 전달한다.
- 로그인에 실패하면 사용자는 토큰을 전달받지 못해서 원하는 리소스에 접근할 수 없게 된다.
인가(authorization)
- 인증을 통해 검증된 사용자가 애플리케이션 내부의 리소스에 접근할 때 사용자가 해당 리소스에 접근할 권리가 있는지를 확인하는 과정이다.
- 인가의 대표적인 예시(ex. 게시판 접근)
- 로그인한 사용자가 특정 게시판에 접근해서 글을 보려고 하는 경우, 게시판 접근 등급을 확인하여 접근을 허가하거나 거부하는 것이 대표적인 인가의 사례이다.
- 일반적으로 사용자가 인증 단계에서 발급받은 토큰은 인가 내용을 포함하고 있다.
- 사용자가 리소스에 접근하면서 토큰을 함께 전달하면 서버는 토큰을 통해 권한 유무 등을 확인하여 인가를 수행한다.
접근 주체(principal)
- 애플리케이션의 기능을 사용하는 주체를 의미한다.
- 사용자가 될 수도 있고, 디바이스, 시스템 등이 될 수도 있다.
- 인증 과정을 통해 접근 주체가 신뢰할 수 있는지 확인하고, 인가 과정을 통해 접근 주체에게 부여된 권한을 확인하는 과정 등을 거친다.
스프링 시큐리티
스프링 시큐리티의 동작 구조
- 스프링 시큐리티는 서블릿 필터(Servlet Filter)를 기반으로 동작하며, DispatcherServlet 앞에 필터가 배치되어있다.

필터체인(FilterChain)
- 서블릿 컨테이너에서 관리하는 ApplicationFilterChain을 의미한다.
- 클라이언트에서 애플리케이션으로 요청을 보내면 서블릿 컨테이너는 URI를 확인해서 필터와 서블릿을 매핑한다.
- 필터체인을 서블릿 컨테이너의 필터 사이에서 동작시키기 위해서 DelegatingFilterProxy를 사용한다.

DelegatingFilterProxy
- 서블릿 컨테이너의 생명주기와 스프링 애플리케이션 컨텍스트 사이에서 다리 역할을 수행하는 필터 구현체이다.
- 표준 서블릿 필터를 구현하며, 역할을 위임할 필터체인 프록시(FilterChainProxy)를 내부에 가지고 있다.
- 필터체인 프록시는 스프링부트의 자동 설정에 의해 자동 생성된다.
필터체인 프록시(FilterChainProxy)
- 스프링 시큐리티에서 제공하는 필터로서, 보안 필터체인(SecurityFilterChain)을 통해 많은 보안 필터(Security Filter)를 사용할 수 있다.
- 필터체인 프록시에서 사용할 수 있는 보안 필터체인은 List 형식으로 담을 수 있게 설정되어 있어 URI 패턴에 따라 특정 보안 필터체인을 선택해서 사용하게 된다.
- 보안 필터체인에서 사용하는 필터는 여러 종류가 있으며, 각 필터마다 실행되는 순서가 다르다.
WebSecurityConfigurerAdpater
- 보안 필터체인은 WebSecurityConfigurerAdpater 클래스를 상속받아 설정할 수 있다.
- 필터체인 프록시는 여러 보안 필터체인을 가질 수 있는데, 여러 보안 필터체인을 만들기 위해서는 WebSecurityConfigurerAdpater 클래스를 상속받는 클래스를 여러 개 생성하면 된다.
- WebSecurityConfigurerAdpater 클래스에는 @Order 어노테이션을 통해 우선순위가 지정되어 있는데, 2개 이상의 클래스를 생성했을 때 똑같은 설정으로 우선순위가 100이 설정되어 있으면 예외가 발생한다.
- 이 때문에, 상속받은 클래스에서 @Order 어노테이션을 지정하여 순서를 정의하는 것이 중요하다.
- 별도의 설정이 없다면 SecurityFilterChain에서 사용하는 필터 중 UsernamePasswordAuthenticationFilter를 통해 인증을 처리한다.
UsernamePasswordAuthenticationFilter

- 클라이언트로부터 요청을 받으면 서블릿 필터에서 SecurityFilterChain으로 작업이 위임되고 그중UsernamePasswordAuthenticationFilter에서 인증 처리한다.
- AuthenticationFilter는 요청 객체에서 username과 password를 추출해서 토큰을 생성한다.
- 그 후, AuthenticationManager에게 토큰을 전달한다. AuthenticationManager는 인터페이스이며, 일반적으로 사용되는 구현체는 ProviderManager이다.
- ProviderManager는 인증을 위해 AuthenticationProvider 로 토큰을 전달한다.
- AuthenticationProvider 는 토큰의 정보를 UserDetailsService에 전달한다.
- UserDetailsService는 전달받은 정보를 통해 데이터베이스에서 일치하는 사용자를 찾아 UserDetails 객체를 생성한다.
- 생성된 UserDetails 객체는 AuthenticationProvider로 전달되며, 해당 Provider에서 인증을 수행하고 성공하게 되면서 ProviderManager로 권한을 담은 토큰을 전달한다.
- ProviderManager는 검증된 토큰을 AuthenticationFilter로 전달한다.
- AuthenticationFilter는 검증된 토큰을 SecurityContextHolder에 있는 SecurityContext에 저장한다.
- UsernamePasswordAuthenticationFilter는 접근 권한을 확인하고 인증이 실패할 경우, 로그인 폼이라는 화면을 보내는 역할을 수행한다.
JWT(JSON Web Token)
- 당사자 간에 정보를 JSON 형태로 안전하게 전송하기 위한 토큰이다.
- URL로 이용할 수 있는 문자열로만 구성되어 있으며, 디지털 서명이 적용되어 있어 신뢰할 수 있다.
- 주로 서버와의 통신에서 권한 인가를 위해 사용된다.
- URL에서 사용할 수 있는 문자열로만 구성되어 있기에 HTTP 구성요소 어디든 위치할 수 있다.
JWT의 구조

- 헤더(Header)
- JWT 헤더는 검증과 관련된 내용을 담고 있다.
- [예시] 헤더
{"alg": "HS256","typ": "JWT"}
- alg 속성에서는 해싱 알고리즘을 지정한다.
- 해싱 알고리즘
- 보통 SHA256 또는 RSA 를 사용하며, 토큰을 검증할 때 사용되는 서명 부분에서 사용된다.
- 위의 예시는 HS256은 'HMACSHA256' 알고리즘을 사용한다는 의미이다.
- typ 속성에는 토큰의 타입을 지정한다.
- 완성된 헤더는 Base64Url 형식으로 인코딩되어 사용된다.
- 내용(Payload)
- JWT 내용에는 토큰을 담는 정보를 포함한다.
- 포함되어 있는 속성들은 클레임(Claim)이라고 하며, 크게 3가지로 분류된다.
- 등록된 클레임(Registered Claims)
- 공개 클레임(Public Claims)
- 비공개 클레임(Private Claims)
- 등록된 클레임의 정의
- iss : JWT의 발급자(Issuer) 주체를 나타낸다. iss의 값은 문자열이나 URI를 포함하는 대소문자를 구분하는 문자열이다.
- sub : JWT의 제목(Subject)
- aud : JWT의 수신인(Audience)이다. JWT를 처리하려는 각 주체는 해당 값으로 자신을 식별해야하며, 요청을 처리하는 주체가 'aud' 값으로 자신을 식별하지 않으면 JWT는 거부가 된다.
- exp : JWT의 만료시간(Expiration)이다. 시간은 NumericDate 형식으로 지정해야한다.
- nbf : 'Not Before'를 의미한다.
- iat : JWT가 발급된 시간(Issued at)
- jti : JWT의 식별자(JWT ID)이다. 주로 중복 처리를 방지하기 위해 사용된다.
- 공개 클레임은 키 값을 마음대로 정의할 수 있다. (충돌되자 않을 이름으로 설정해야한다)
- 비공개 클레임은 통신 간에 상호 합의되고 등록된 클레임과 공개된 클레임이 아닌 클레임을 의미한다.
{"sub": "payload","exp": "160276402","userId": "test","username": "김철수"}
- Base64Url 형식으로 인코딩되어 사용된다.
- 서명(Signature)
- 헤더, 인코딩된 내용, 비밀키, 헤더의 알고리즘 속성값을 가져와 생성된다.
HMACSHA256(base64UrlEncode(Header) + "." +base64UrlEncode(payload),secret)
- 서명은 토큰의 값들을 포함해서 암호화하기 때문에 메시지가 도중에 변경되지 않았는지 확인할 때 사용한다.
- 헤더, 인코딩된 내용, 비밀키, 헤더의 알고리즘 속성값을 가져와 생성된다.
JWT의 디버거
- 웹 브라우저에서 해당 링크에 접속하면 JWT를 생성해볼 수 있다. [JWT 디버거 링크]
- Encoded와 Decoded로 나뉘어있으며, 양측의 내용이 일치하는지 사이트에서 확인할 수도 있다.
- Decoded 내용을 변경하면 Encoded의 컨텐츠가 자동으로 반영된다.

반응형
'Book > 스프링부트 핵심가이드' 카테고리의 다른 글
스프링 시큐리티(Spring Security) 구현(of. Jwt Token) (1) | 2023.12.06 |
---|---|
스프링부트를 이용한 서버 간의 통신(of. WebClient) (0) | 2023.12.02 |
스프링부트를 이용한 서버 간의 통신(of. RestTemplate) (0) | 2023.12.01 |
댓글