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

uuuu.jini·2022년 2월 21일
0

Spring Security

목록 보기
3/5
post-thumbnail

🐯 세션 제어 필터: SessionManagementFilter,ConcurrentSessionFilter

SessionManagementFilter

밑의 네가지 기능을 수행한다.
1. 세션 관리 : 인증 시 사용자의 세션정보를 등록,조회,삭제등의 세션 이력을 관리
2. 동시적 세션 제어 : 동일 계정으로 접속이 허용되는 최대 세션수를 제한
3. 세션 고정 보호 : 인증 할 때 마다 세션쿠키를 새로 발급하여 공격자의 쿠키 조작을 방지
4. 세션 생성 정책 : Always,If_Required,Never,Stateless

ConcurrentSessionFilter

SessionManagementFilter와 연계를 하여 동시적 세션 제어를 수행한다. 매 요청마다 현재 사용자의 세션 만료 여부를 체크한다. 세션이 만료되었을 경우 즉시 만료를 처리한다.

  • session.isExpired() == true 인 경우 로그아웃처리를 하고 즉시 오류 페이지를 응답한다.

동시적 세션 제어

최대 세션 허용개수가 초과되었을 경우 session.expireNow()를 통해 세션을 만료하고 이전 사용자는 세션이 만료되어있다. ConcurrentSessionFilter는 매 요청마다 세션의 만료여부를 체크하므로 sessionManagementFilter의 이전 사용자의 세션을 만료하였던 설정을 참조하여 session.isExpired()를 검사한다. 세션이 만료되었을 경우 즉시 로그아웃을 한 뒤 오류페이지를 응답하게 된다.

🐯 권한 설정 및 표현식

권한 설정

선언적 방식

  • URL : http.andMatchers("경로").hasRole("역할")
  • Method : @PreAuthorize("hasRole("")")

동적 방식 - DB 연동 프로그래밍

  • URL
  • Method

선언적 방식의 url 방식에 대해 알아본다. 인가 설정도 인증 설정과 동일한 메서드 내에서 설정이 가능하다. ( configure 메서드 )

	http
    	.antMatcher("/shop/**") // 특정 경로 지정 -- > 현재 사용자 요청이 설정한 경로로 접근시만 보안기능이 작동한다.
        // 위를 생략하게 되면 모든 요청에 대해 보안검사를 하게 된다. 
        .authorizeRequests() 
        	.antMatchers("/shop/login","/shop/users/**").permitAll()   
            .antmatchers("/shop/mypage").hasRole("USER") 
            .antMatchers("/shop/admin/pay").acess("hasRole('ADMIN')")
            .antMatchers("/shop/admin/**").access("hasRole('ADMIN') or hasRole('SYS'):)
            .anyRequest().authenticated()
            ;
            

순차적으로 검사하며 위의 요청의 인가 심사가 통과되어야 밑의 다른 인가 심사가 계속적으로 이루어진다. antMatcher(특정 경로에 대한 요청 )로 지정한 경로로의 요청이 일치하는 경우 , 해당 경로로의 요청에 대해 인가 심사를 하며 이때 인가심사 종류는 뒤에 작성한다. 각각의 인가심사를 모두 통과해야 자원에 대해 접근할 수 있게 된다.

마지막에는 특정한 자원,권한 정보를 제외한 모든 요청 anyRequest() 에는 authenticated() 인증을 받은 사용자만 접근이 가능하다고 설정해줄 수 있다. ( 설정 시 구체적인 경로가 먼저 오고 그것 보다 큰 범위의 경로가 뒤에 오도록 해야 한다. -- 인가 처리시 각각의 설정을 위에서부터 아래로 해석을 하며 처리를 한다. 그러므로 작은 범위가 먼저 작성되고 큰 범위를 뒤에 작성해야 한다. )

antMatcher()

특정 경로로의 요청에 대한 권한을 설정하기 위하여 작성하며 괄호()안에 특정 경로를 , 로 구분하여 작성한다. 해당 메서드뒤에 여러 표현식이 올수 있으며 , 표현식에 따라 지정한 경로의 요청에 대한 권한을 설정할 수 있다.

표현식

메소드동작
authenticated()인증된 사용자의 접근을 허용
fullyAuthenticated()인증된 사용자의 접근을 허용, rememberMe 인증 제외
permitAll()무조건 접근 허용
denyAll()무조건 접근 허용하지 않음
anonymous()익명사용자의 접근을 허용 , ROLE_USER 는 익명사용자권한에 접근할 수 없다. 익명사용자용 이다.
rememberMe()리멤버미 기능을 통해 인증된 사용자의 접근을 허용
access(String)주어진 SpEL 표현식의 평가 결과가 true이면 접근을 허용 , 여러 표현식 사용 가능
hasRole(String)사용자가 주어진 역할이 있다면 접근을 허용
hasAuthority(String)사용자가 주어진 권한이 있다면 접근을 허용
hasAnyRole(String...)사용자가 주어진 역할 중 어떤것이라도 있다면 접근을 허용
hasAnyAuthority(String...)사용자가 주어진 권한 중 어떤 것이라도 있다면 접근을 허용
hasIpAddress(String)주어진 IP로부터 요청이 왔다면 접근을 허용

사용자 생성

여러 사용자를 생성하고 여러 권한을 부여하여 각 자원에 접근하였을 떄 권한에 따라 스프링시큐리티가 어떻게 인가처리를 하는지 살펴본다.

위의 클래스가 사용자를 생성하고 권한을 설정할 수 있도록 한다.

밑의 코드는 예시이다.

        http
                .authorizeRequests()
                .antMatchers("/user").hasRole("USER")
                .antMatchers("/admin/pay").hasRole("ADMIN")
                .antMatchers("/admin/**").access("hasRole('ADMIN') or hasRole('SYS')")
                .anyRequest().authenticated();

사용자 권한의 계층 설정

각 사용자에는 계층이 존재한다. 예로, admin권한을 가진 사용자는 user권한이 필요한 자원들에는 접근을 할 수 있어야 한다. 그러려면 admin 권한은 user권한보다 상위에 존재해야 하는 것이다. 지금은 이런 효과를 주기위해 사용자 생성시 상위 권한을 가진 사용자에는 모든 역할권한을 가지도록 설정해주었다.
auth.inMemoryAuthentication().withUser("admin").password("{noop}1111").roles("ADMIN","USER","SYS");


🐯 예외 처리 및 요청 캐시 필터 : ExceptionTranslationFilter,RequestCacheAwareFilter

ExceptionTranslationFilter

크게 두가지 종류의 예외를 처리한다.

  • AuthenticationException : 인증 예외 처리

    1.AuthenticationEntryPoint 구현체 호출 : 로그인 페이지 이동, 401오류 코드 전달 등 / 우리가 직접 인터페이스를 구현하여 클래스를 만들어서 설정을 하는 경우 우리가 만든 구현체를 호출해준다. 즉, 우리가 원하는 처리를 할 수 있게 된다.
    2.인증 예외가 발생하기 전의 요청 정보를 저장 : RequestCache 구현체(사용자의 이전 요청 정보를 세션에 저장하고 이를 꺼내오는 캐시 메카니즘 ) , SavedRequest 구현체(사용자가 요청했던 requests 파라미터 값들, 그당시의 헤덕밧들 등이 저장 )

  • AccessDeniedException : 인가 예외 처리

    AccessDeniedHandler에서 예외 처리하도록 제공 , 권한이 없어서 접근하고자 하는 자원에 접근이 되지 않는다는 메세지등을 전달하는 등의 구현체를 호출해준다.

두개의 예외를 발생시키는 필터가 FilterSecurityInterceptor라는 필터이다. 맨마지막에 위치하며 이 필터 앞에 위치하는 필터가 ExceptionTranslationFilter이다. 해당 필터는 실제 사용자 요청을 받고 다음 필터로 요청을 전달시 Try..Catch..로 감싸서 FilterSecurityInterceptor를 호출하고 있다. 호출받은 필터는 인증예외/인가 예외를 다시 자신을 호출한 필터로 throw 해준다. 그럼 ExcpetionTranslationFilter는 예외를 받아서 각각 처리를 하게 된다.

http.exceptionHandling()

예외처리 기능이 작동한다. - 인증 예외 ( AuthenticationEntryPoint ) , 인가 예외 ( AccessDeniedHanlder )

  • .authenticationEntryPoint(authentiationEntryPoint()) : 인증 실패 예외 처리
  • .accessDeniedHandler(accessDeniedHandler()) : 인가(권한) 실패 예외 처리

우리가 만든 구현체를 설정하면 우리가 만든 구현체를 호출하여 예외 이후의 후속작업을 처리할 수 있게 된다.

위의 코드와 같이 각각의 구현체를 우리가 구현하여 후속작업을 처리한다.

인증에 성공한 경우 , 사용자가 원래 가고자했던 정보를 가지고 있는 객체(인증 실패시 세션에 저장해두고 있음 , RequestCache객체를 이용하여 SavedRequest객체를 가져옴 ) 를 통해 해당 경로로 보내주는 기능을 제공한다.

RequestCacheAwareFilter

Request 객체가 있는 경우 넘겨주는 역할을 하는 필터이다. 미리 저장된(캐싱된) 데이터를 담고있는 request객체를 계속적으로 필터로 전달해서 정보를 활용할 수 있도록 한다.


🐯 사이트 간 요청 위조 - CSRF,CsrfFilter

사용자가 쇼핑몰로부터 인증을 받고 쿠키를 발급받은 이후 쇼핑몰이 발급한 쿠키는 사용자의 브라우저에 저장이 되어 있다. 이때, 공격자가 전달한 링크를 클릭하면 사용자의 인증정보로 요청을 함으로 인증이 완료되어 정상적으로 요청에 대한 응답을 하게 된다. 이렇게 하면서 사용자의 의도와는 무관하게 공격자가 심어놓은 방식을 통해서 공격자가 의도한 대로 요청에대한 응답을 받을 수 있게 되는 취약점을 사이트 간 요청 위조 라고 한다.

Spring security 는 이 문제를 방지하기 위한 api를 제공한다.

CsrfFilter

해당 필터를 제공함으로써 csrf 취약점을 방지하는 기능을 제공한다. 이 필터는 모든 요청에 랜덤하게 토큰을 생성하고 프론트에게 발급한다. 그 이후에는 클라이언트가 서버에 접속을 위해서는 발급한 토큰을 가지고와야만 스프링 시큐리티는 발급한 토큰에 대해 검사를 하게 된다. 요청 시 전달되는 토큰 값과 서버에 저장된 실제 값과 비교한 후 만약 일치하지 않으면 요청은 실패한다.

접속시 csrf 필터를 통해 접속한 클라이언트에게 csrf 토큰을 발급한다.

  • <input type = "hidden" name = "$..." value = "$..." /> 와 같은 형식으로 토큰이름과 토큰값을 설정을 할 수 있다. 설정 한 이후 서버에 Http메소드 중 Patch,post,put,delete 와 같은 요청시 서버에서 발급한 토큰명과 토큰값을 가지고 요청을 해야 한다. 그렇지 않으면 요청에 대해 예외처리를 하여 사용자가 접근하지 못하게 막는다.

Spring Security

  • 기본적으로 http.csrf()가 기본 활성화 되어있다.(api작성하지 않아도 활성화되어있음) 비활성화를 위해서는 http.csrf.disabled()를 작성한다.

위와 같이 사용자가 csrf토큰을 가지고 요청을 해야만 자원에 접근이 가능하다. 즉, 공격자가 전달한 링크를 이용해 서버에 접근을 하는 경우 csrf토큰이 없으므로 스프링 시큐리티가 접근을 허가하지 않는다.

profile
멋쟁이 토마토

0개의 댓글