JWT 토큰 인증 서버 구현을 위한 Spring Security

dropKick·2020년 9월 25일
2

스프링 프레임워크

목록 보기
4/13

Spring Security와 REST API

스프링 시큐리티 필터 체인


스프링 시큐리티는 스프링 프레임워크 밖에서 최대한 인증과 권한을 처리할려고 하기에
굉장히 복잡한 과정을 거친다.
스프링 프레임워크에서 최대한 벗어나려고 하는 이유는 뭘까.. 이건 별도로 알아봐야겠음.


Spring Security의 동작은 필터 체인을 Dispatcher Servlet 앞에 두어
요청을 가로채는 것부터 시작한다.

  • 정리하자면 HTTP 리퀘스트는 일단 필터로 들어옴
  • Authentication Filter Class
    이 필터는 커스텀 가능하고 별도의 토큰 오브젝트를 받을 수 있음
  • Authentication Manager Class
    이 인증 매니저는 커스텀한 필터에 맞는 Provider를 호출
    토큰이라면 커스텀 된 Token Provider를 통해 들어온 객체 정보(ID/PW)를 만든다.
  • Authentication Provider Class
    호출 된 Provider는 데이터를 담을 수 있는데 데이터는 UserDetailsService를 통해 받는다.

그 후 과정은 역순으로 가서 최종적으로 Security Context에 인증 정보가 남고
이후에는 권한 처리를 통해 서블릿으로 넘어갈 수 있다

접근 권한이 없는 경우

접근 권한 자체가 없는 경우 별도의 오류처리를 해야 한다.
기본적으로 UserPasswordAuthenticationFilter(Token)이 loginForm을 내려주지만 Rest API의 경우 별도의 폼이 존재하지 않아 이를 JSON으로 내려줄 수 있어야 한다.
따라서 UserPasswordAuthenticationFilter에 JSON 오류 처리 메소드가 존재해야 한다.

Stateless

CORS

Security Config

Http

 // Http
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        		.httpBasic.disable()
                .csrf().disable()
                .headers().frameOptions().disable()
                .and()
                .authorizeRequests() 
                // 권한 설정
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/info").hasRole("USER")
                .antMatchers("/**").authenticated()
                .and()
                .formLogin().disable()
                .and()
                // 403 예외처리 핸들링
                .exceptionHandling().accessDeniedPage("/user/denied");
    }
  • httpBasic
    기본적으로 로그인 폼으로 리다이렉션 시킴
    REST API의 경우 로그인 폼이 필요없으므로 disable
  • CSRF
    세션 보안
    토큰을 사용한 인증방식이기 때문에 세션 보호의 필요가 없음 disable
  • headers.frameOptions()
    CSS ClickJacking
    역시 disable
  • authorizationRequest()
    인증을 위한 요청 처리로 HttpServletRequest를 이용
  • andMatchers().hasRole()
    들어온 리퀘스트를 문자열 경로와 매칭하고 지정된 권한을 판단한다
    authenticated()의 경우 권한만 있다면 접근 가능
    이를 이용해 모든 경로가 접근 가능하지만 유저 정보 수정, 어드민 등은 권한 사용자만 접근하게 할 수 있다.
  • formLogin()
    스프링 시큐리티에서 제공하는 form을 이용하여 로그인 할 수 있다
    REST API기에 disable

UserService

 // DetailService를 통해 User 접근, 인코딩해서 저장
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(passwordEncoder());

스프링 시큐리티의 모든 인증 로직은 AuthenticationManager를 통해 이루어진다

따라서 AuthenticationMangerBuilder를 이용하여 생성
AuthenticationManger는 UserDetailService 인터페이스를 구현
이후 loadByUserName() 메소드를 UserService에서 구현하여 사용

Security + JWT

필터 체인에서 Authentication Provider를 무엇으로 하냐에 따라 인증 구현이 다르다
Session Provider라면 세션 인증, Token Provider라면 토큰 인증이 된다


Token Provider를 사용했을 때 인증 로직의 동작 구조다

  • HTTP 리퀘스트 요청은 그대로 인증 필터로 들어온다
  • 여기서 토큰을 체크하는 토큰 유효성 검사가 들어가고 토큰이 없다면 컨트롤러에게 토큰 발급을 요청한다
  • 토큰 발급 요청을 받으면 Token Provider가 위치한 필터 체인의 Authentication Manager로 요청이 넘어간다
  • Authentication Manager는 UserDetailsService를 통해 객체 정보(ID/PW)를 저장/검증하고 다시 컨트롤러에게 정보를 넘긴다
  • 컨트롤러는 받은 정보를 통해 토큰을 발급받는다
  • 발급된 토큰을 컨트롤러가 다시 유저에게 넘긴다

기존 필터 체인의 동작에 토큰을 발급받는 과정만이 추가되는데 조금 더 자세히 보면 다음과 같다.

토큰 유효성 검사

  1. 토큰 유효성 검사용 Token Authentication Filter
    HTTP 리퀘스트가 인증 필터로 들어오는데 가장 먼저 토큰 인증 필터를 거친다
  2. HTTP 리퀘스트 Authorization 헤더 추출
    이를 통해 토큰 유효성을 검증하고 UPAT 토큰을 Context Holder에 등록한다
  3. 이후 접근은 컨트롤러가 Context Holder에 있는 토큰을 통해 처리한다

토큰 발급

  1. Custom Authentication Filter
    토큰 유효성 검사를 통과하지 못한다면 Authentication Manager에게 UPAT 토큰을 넘긴다
  2. Authentication Manager
    여기서 UserDetailsService를 호출하고
    loadByUser() 메소드를 통해 DB 정보와 기존 정보를 검증한다
  3. Custom Authentication Filter
    검증 성공 시 토큰을 발급한다

Reference

0개의 댓글