# Spring Security - 기본 API 및 Filter [1]

uuuu.jini·2022년 2월 14일
0

Spring Security

목록 보기
1/5
post-thumbnail

스프링 시큐리티는 보안시스템을 구축할때 많은 API를 제공한다. - 인증&인가

각각의 유형별 여러가지 다양한 API의 사용방법, 개념 , 실제로 사용자가 어떤 자원에 접근&인증시 처리되는 과정 동작방식등을 배운다.

API설정시 스프링 시큐리티가 실행이 되면서 생성및 초기화 되는 클래스인 filter가 사용자 요청을 실제적으로 처리하는 역할을 한다. filter에 대해 배울것이다.

두번쨰는, 스프링 시큐리티 내부 아키텍처 그 아키텍처를 구성하는 각 객체이 역할 및 처리과정을 학습한다. (초기화과정,인증과정,인가과정 등)

마지막은 실제 프로젝트를 구현하며 인증기능(form,Ajax)과 인가기능(DB연동)을 구현해 볼 것이다.

🐯프로젝트 구성 및 의존성 추가

project를 생성하고 web의존성만 추가하였다. maven으로는 처음 생성해보는 듯

먼저 root경로로 접속하는 경우 home이라는 문자열을 보이게 하는 컨트롤러인 SecurityController를 생성하였다.

위의 방법을 통해 우리의 서버가 정상적으로 작동하는 것을 확인하였다. 지금 우리 서버의 문제점은 아무런 보안이 적용이되지 않았다는 것이다. 누구하도 우리시스템에는 접근이 가능하다. 권한,인증 없이도 원하는 자원에 접근이 가능한 보안에 취약한 구조이다. 안정된 보안을 가지는 시스템이 되려면 작업이 필요하다.

가장 먼저 해야할 작업은 pom.xml에 의존성을 추가하는 작업이다.

위와 같이 의존성을 주입한 뒤 서버를 재실행할경우 보안(인증&권한)이 적용되게 된다. (maven 재로딩해줘야 하는데 안해줘서 빨간줄 떳었음 꼭 로딩한후 서버재실행)

재실행후 해당 경로로 다시 들어가게 되면 기존에 실행되어 hello를 반환해주었던것과는 다르게 로그인창이 뜬다.(인증을 받아야만 자원의 접근이 가능해진것 ) 인텔리제이의 서버 실행창에서도 Using generated security password : ... 라는 로그가 뜨는 것을 확인할 수 있다. (해당 비밀번호를 user라는 계정의 비밀번호로 작성하여 로그인할 경우 접속이 가능해진다. -- 기본계정)

우리의 시스템이 의존성을 추가한 이후부터 인증을 받은 사용자만 자원의 접근이 가능하고 인증을 하지않으면 로그인페이지로 이동하여 인증을 받도록 하는 보안페이지로 바뀌게 되었다.

의존성 추가시 어떤 절차를 걸쳐 보안기능이 작동하게 된건지 알아본다. 서버가 기동되면 스프링 시큐리티의 초기화 작업 및 보안 설정이 이루어진다. 스프링 시큐리티가 자동으로 실행한다. 별도의 설정이나 구현을 하지 않아도 기본적인 웹보안 기능이 현재 시스템에 연동되어 작동한다. 기본적인 보안기능에는 밑과 같다.

  • 모든 요청은 인증이 되어야 자원에 접근이 가능하다.
  • 인증방식은 폼 로그인 방식과 httpBasic 로그인 방식을 제공한다.
  • 기본 로그인 페이지 제공
  • 기본 계정 한개 제공 ( username : user , password: 랜덤 문자열 - 로그에 뜬것 )

이런 기본 웹보안 기능은 제공하지만 계정 추가,권한 추가, DB 연동 등이 필요하며 기본적인 보안 기능 외에 시스템에서 필요로 하는 더 세부적이고 추가적인 보안기능이 필요하다.


🐯사용자 정의 보안 기능 구현

스프링 시큐리티의 의존성을 추가한뒤 서버 구동시 스프링 시큐리티가 초기화되면서 웹보안기능을 활성화하고 보안기능이 우리의 시스템에 반영이 되어 작동이 된다.

보안 기능은 작동을 하지만 현재 시스템에는 계정이 하나밖에 없으므로 계정추가와 권한 추가 등 추가 작업이 필요하다. ( 해커의 침입에 대비할수 있는 보안 옵션 등이 부재 ) --> 단순한 최소한의 기능만 가짐

위의 문제를 보안하여 안전,강화된 보안기능을 구현해볼 것이다.

보안 기능을 구현하기 위해서 알아야 할 항목들에 대해 알아보며 사용자 정의 보안 기능을 하는 클래스를 만들어서 확인해 볼 것이다.

WebSecurityConfigureAdapter / HttpSecurity

핵심적인 클래스로서 스프링 시큐리티의 웹 보안 기능을 초기화하고 설정을 하는 클래이다. 의존성 추가시 가장 기본 보안 기능을 활성화하고 보안기능 적용을 하게 해준 클래스이기도 하다.

WebSecurityConfigureAdapterHttpSecurity라는 클래스를 생성하며 해당 클래스는 세부적인 보안 기능을 설정할 수 있는 API를 제공한다. 이 두개의 클래스는 우리가 의존성을 추가 했을 때 우리의 시스템에 가장 기본인 웹 보안기능을 활성화하고 적용을 해주는 역할을 한다.

SecurityConfig라는 사용자 정의 클래스를 만들어서 이 클래스는 WebSecurityConfigureAdapter를 상속받을 것이다. 그래서 이 설정 클래스 안에서 HttpSecurity 클래스를 사용하여 세부적이고 추가적으로 설정할 수 있는 API를 사용하여 사용자 정의 보안기능을 구현할 것이다.

HttpSecurity는 인증 API와 인가 API를 각각 가지고 있으며 해당 API들이 어떤 역할을 하고 처리를 하는지를 알아본다. 즉 , 두 API를 활용하여 보안에 안전한, 강력한 보안시스템을 구축해보는 것이다.

SecurityConfig 설정

SecurityConfig 클래스는 우리가 사용자 보안 기능을 설정을 할 클래스이다. WebSecurityConfigureAdapter를 상속받고 void configure(HttpSecurity http) 메서드를 오버라이드 하여 메서드 안에서 인가나 인증과 관련된 API를 설정하여 우리만의 사용자 정의 보안 기능을 구현할 수 있도록 할 것이다.

WebSecurityConfigureAdapter클래스는 스프링 시큐리티의 초기화 작업들을 수행한다. 서버가 기동이 되면 스프링 시큐리티가 WebSecurityConfigureAdapter 클래스를 호출하고 해당 클래스 내부에서는 HttpSecurity클래스를 생성한다. 이 클래스를 활용하여 대략 11가지 정도의 API를 호출하며 설정 초기화 작업을 수행한다. 각각의 API는 설정과정에서 해당하는 Filter들을 생성한다. configure 메서드에서는 추가적인 설정을 지원한다. 초기에는 기본적인 설정이 되어있으며 이러한 기본 설정으로 초기의 기본 보안기능을 제공하고 있는 것이다.

ExceptionHandlingConfigure 예외 처리를 하는 설정 클래스이다.
ExceptionTranslationFilter 인증 인가 예외를 처리하는 필터이다.

WebSecurityConfigureAdapter 클래스를 상속받아서 configure 메서드를 오버라이드하여 사용자 정의 보안 기능을 구성해 볼 것이다.

처음 SecurityConfigure 설정 클래스를 생성시에는 WebSecurityConfigureAdapter 클래스를 상속받고 @Configuration어노테이션과 @EnableWebSecurity 어노테이션을 기본으로 선언이 필요하며 이는 WebSecurityConfiguration클래스를 임포트하여 실행시키는 어노테이션이다. 그래야 웹보안이 활성화된다.

편리한 사용을 위해 사용자를 생성하는 법

HttpSecurity 를 매개변수로 받는 configure메서드를 오버라이드하여 사용자 정의 보안 기능을 구현해보았다. 먼저 간단하게 기존의 설정과 비슷한 API를 사용하였다. ( 인가 : 유저에 대한 권한을 허가 , 인증: 사용자 접근 허용 , 유저가 누구인지 확인하는 절차 )

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .anyRequest().authenticated(); //요청에 대한 보안 검사가 실행되고 어떠한 요청에도 인증을 받도록 설정하였다.
                //위는 인가 정책이다.
        http
                .formLogin(); //인증 정책은 기본적으로 formLogin방식으로 인증을 할 수 있도록 설정
    }

위의 코드를 작성한뒤 url에 접속시 인증이 되지 않은 경우 formLogin창이 뜨며 설정한 계정으로 로그인시 접근이 되는 것을 확인 할 수 있다.


🐯Form 인증

Client와 는 GET 방식으로 home url로 접근을 시도한다. Server 자원의 접근을 위해서는 인증된 사용자만이 접근이 가능하도록 보안 설정을 해놓았다. 사용자가 인증이 되어있지 않은 경우에는 바로 Login페이지로 이동을 시켜 사용자가 인증을 할 수 있도록 한다.

Login페이지는 별도로 만들거나 스프링 시큐리티가 기본적으로 제공도 해준다. username과 password를 입력하여 post요청을 하여 인증을 시도하면 서버는 세션을 생성하고 최종성공한 인증결과를 담은 인증토큰,인증객체(Authentication 타입의 객체--SecurityContext 객체 ) 를 생성하고 이 클래스 객체는 나중에 배우게 된다.

인증처리가 이루어지고 관련 객체가 생성되고 저장되는 처리가 이루어진다. 인증을 받은 이후에 다시 home url로 접근을 시도하게 되면 스프링 시큐리티는 현재 사용자가 가진 세션으로부터 인증토큰의 존재 여부를 확인한다. 사용자는 인증토큰으로 계속 해당 자원에 접근하며 스프링 시큐리티는 세션에 저장된 인증토큰이 있게되면 사용자의 접근을 허용한다.

Form Login 인증 API

HttpSecurity 객체는 인증 Api와 인가 Api를 모두 지원하며 SecurityConfig 클래스의 configure메서드의 파라미터인 HttpSecurity 객체(http)를 이용하여 formLogin 인증 설정을 한다.

http.formLogin()

Form로그인 인증 기능이 작동한다.

  • loginPage("url") : 사용자 정의 로그인 페이지- 로그인 페이지 경로 작성 , 기본적으로는 로그인 페이지를 제공한다. 직접 만든것 사용을 위해 작성한다.
  • defaultSuccessUrl("url") : 로그인 성공 이후에 이동할 페이지 경로를 작성한다.
  • failureUrl("url") : 로그인이 실패한 이후에 이동할 페이지
  • usernameParameter("username") : 아이디 파라미터명 설정, ui화면에서도 form태그명을 동일하게 작성해야 한다. ( 로그인시 파라미터로 username,password를 받는다. )
  • passwordParameter("password") : 패스워드 파라미터명 설정
  • loginProcessingUrl("url") : 로그인 form action url , 폼태그에서 액션 url 정보를 설정한데로 변경할 수 있다. 사용자 이름과 암호를 제출할 url 이다. 위의 파라미터와 url모두 폼 태그에서 이름을 동일하게 작성하여야 한다. (post로 로그인 정보 보낼 경로 , username과 password를 파라미터로 가지고 감,위에서 작성한 이름대로 넘어가므로 login페이지를 직접 작성하는 경우 해당 이름들을 일치시켜주어야 한다. )
  • successHandler(loginSuccessHandler()) : 로그인 성공 후 핸들러 --> 핸들러 클래스를 만들어서 작성해주면 성공시 해당 핸들러가 작동
  • failureHandler(loginFailureHandler()): 로그인 실패 후 핸들러
         http
                .formLogin() //인증 정책은 기본적으로 formLogin방식으로 인증을 할 수 있도록 설정
                .loginPage("/loginPage") //원하는 페이지를 로그인 화면으로 사용
                .defaultSuccessUrl("/") //인증 성공시 이동하는 페이지
                .failureUrl("/login") // 인증 실패시 이동하는 페이지
                .usernameParameter("userID")  // username 파라미터 설정
                .passwordParameter("passwd")
                .loginProcessingUrl("/login_proc") //폼 태그의 액션 url
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        System.out.println("authentication" + authentication.getName()); // 인증에 성공한 사용자 이름 출력
                        response.sendRedirect("/"); //루트 페이지로 이동                       
                    }
                }) //성공시 호출할 핸들러 , AuthenticationSuccessHandler() --> 인증 성공시 인증한 결과를 닮은 객체 까지 파라미터로 넘어온다. 이러한 정보를 활용하여 구체적인 로직들을 구현한다.
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                        System.out.println("exception: " + exception.getMessage()); //예외에 메세지를 출력한다.
                        response.sendRedirect("/loginPage"); //로그인 페이지로 이동
                    }
                }) // 실패시 호출할 핸들러, 인증예외에 객체를 파라미터로 전달한다.
                .permitAll() // 위의 작성한 모든 url경로로의 접근을 허용한다. 
        ;

🐯 Form Login 인증 필터 : UsernamePasswordAuthenticationFilter

LoginForm 인증을 하는 필터이다. 해당 api들을 설정하면 로그인 인증 처리가 작동할 수 있도록 스프링 시큐리티가 구성을 한다. 실제적으로 사용자가 로그인시 인증 처리가 이루어지며 그 인증처리를 담당하고 인증처리에 관련된 요청을 처리하는 필터가 UsernamePasswordAuthenticationFilter 이다. 필터가 내부적으로 여러 클래스를 활용하여 인증처리를 하게 된다.

사용자가 인증을 시도하면 해당 필터가 요청을 받고 처음에 현재 사용하자가 요청한 정보를 먼저 확인을 한다. (AntPathRequestMatcher(/login) - 현재 요청정보의 url이 /login으로 시작하는지 검사 default--> login ) 매칭이 되면 실제 인증 처리를 하며 매칭이 되지 않으면 인증 처리를 하지 않고 다음 필터로 이동한다. ( url은 변경이 가능하다 )

매칭이 되고 인증처리를 하기 전에 해당 필터는 username과 password가 담긴 Authentication객체를 만든다. 인증 객체를 생성하여 인증 객체를 AuthenticationManager클래스에 맡기게 된다. 인증 관리자가 인증 객체를 전달 받고 인증 처리를 하게되는데 내부적으로 AuthenticationProvider 클래스 타입의 객체를 가지며 이 객체들중 하나를 선택하여 인증 처리를 위임하게 된다. AuthenticationProvider가 실질적으로 인증처리를 담당하는 클래스이고, 인증 성공/실패 결과를 리턴하고 실패시 AuthenticationException예외를 filter로 전달하여 예외에 대한 후속작업을 처리하게 된다. 인증 성공시 AuthenticationProvider는 Authentication객체를 만들어서 인증에 성공한 결과(user, 권한정보 등 )를 저장하여 AuthenticationManager에게 리턴한다.

AuthenticationManager는 최종적인 인증객체를 다시 filter에게 리턴한다. 이 filter는 인증처리이후 인증 성공시 authentication객체를 전달받고 이 객체에는 인증결과 객체, 부여된 권한 정보를 담은 객체를 받는 것이다. 그리고 인증 객체를 다시 SecurityContext 객체에 저장한다. ( 인증 객체를 저장하는 보관소이다. 나중에는 세션에도 저장됨 ) 이후에 성공작업에 대한 처리를 하게 된다.(SucessHandler )

FilterChainProxy 란 ?
는 필터를 관리하는 빈이다. 여러 필터들을 가지고 있으며 각각의 필터들은 스프링 시큐리티가 초기화되며 생성되는 필터들이 있으며, 설정 클래스에서 설정하게 되면 작성한 api에 해당하는 필터들도 존재한다. (formLogin api설정시 usernamePasswordAuthenticationFilter가 생성됨 ) 즉, 이 클래스는 각각의 필터들을 순서대로 사용자 요청을 처리할 수 있도록 처리를 해준다. 하나의 필터가 끝나면 다음 필터로 넘어가게 되고 맨 마지막까지 필터가 사용자 요청을 처리하게 된다.

profile
멋쟁이 토마토

0개의 댓글