DSlog

전체 글

스프링 시큐리티 - 인가

2021, Oct 12    



Spring Boot 기반으로 개발하는 Spring Security (3)

스프링 시큐리티 인가에 대해 공부하고 사용자를 만들어서 적용해보자

인프런 강의: 스프링 시큐리티 - Spring Boot 기반으로 개발하는 Spring Security 강의를 듣고 복습차원에서 기록하는 내용입니다.


인가

당신에게 무엇이 허가 되어있는지 자격을 증명하는 과정

스프링 시큐리티가 지원하는 권한 계층

웹 계층 - URL 요청에 따른 메뉴 혹은 화면 단위의 레벨 보안

서비스 계층 - 화면단위가 아닌 메소드 같은 기능 단위의 레벨 보안

도메인 계층 - 객체 단위의 레벨 보안


FilterSecurityInterceptor

HTTP 자원의 보안을 처리하는 필터로 권한처리를 AccessDecisionManager 에게 맡긴다.

마지막에 위치한 필터로 인증된 사용자에 대하여 특정 요청의 승인/거부 를 결정한다

인증객체 없이 보호자원에 접근할 경우 AuthentictaionException 발생시킨다.

인증 후 자원에 접근 가능한 권한이 존재하지 않을 경우 AccessDeniedException 발생시킨다.

즉 인증객체가 있는지, 있으면 인가 권한이 있는지에 대해 모두 체크한다

FilterSecurityInterceptor 동작 과정

author1

(1) 사용자의 /user 접근 요청

(2) FilterSecurityInterceptor 가 인증여부를 먼저 체크!

(3) /user 리소스에 대해 권한이 필요한지 체크

(4) 권한이 필요 없다면 응답 / 특정 권한이 있어야 한다면 AccessDecisionManager로 전달

(5) AccessDecisionManager 와 Voter를 통해 사용자가 권한을 가지고 있는지 판단!

(6) 해당 자원(/user) 에 접근이 가능한 사용자라면 인가 성공! 접근 불가능한 유저라면 인가 실패!


AccessDecisionManager

인증,요청,권한 정보를 이용해 사용자의 자원접근 허용여부를 최종결정하는 주체

여러개의 Voter들을 가질 수 있고 Voter들로부터 접근허용,거부,보류에 해당하는 각 값들을 리턴받고 판단

최종 접근 거부 시 예외가 발생한다.

접근결정 유형

  • AffirmativeBased - 여러 Voter들 중 하나라도 접근 허가라면 허가한다.

  • ConsensusBased - 다수결

  • UnanimousBased - 만장일치

AccessDecisionVoter

판단을 심사하는 것

권한부여 과정에서 판단하는 자료

  • Authentication - 인증정보 User

  • FilterInvocation - 요청정보 antMatcher(“/user”)

  • ConfigAttributes - 권한정보 hasRole(“USER”)

결정방식

  • ACCESS_GRANTED(1)

  • ACCESS_DENIED(-1)

  • ACCESS_ABSTAIN(0) - voter가 해당 요청에 대해 결정을 내릴 수 없는 경우


인가 API 권한 설정방법

선언적 방식

  • URL
  • Method

동적 방식

  • DB 연동 프로그래밍

URL 선언 방식 예시

  • 설정 시 구체적인 경로가 먼저 오고 더 넓은 범위의 경로가 뒤에 오도록 해야한다.(순차적으로 처리한다)
http
    .antMatcher("/shop/**")  // 여기 설정된 경로로만 보안기능이 작동합니다.
    .authorizeRequests()
        .antMatchers("/shop/login","/shop/users/**").permitAll() // login 해서 접근하는 요청에 대해 일치하면 users의 모든 리소스를  허용시킨다
        .antMatchers("/shop/mypage").hasRole("USER")  // mypage 에는 USER 롤이 있어야 접근할 수 있다.
        .ant ...


표현식 메서드

  • autheticated()
  • fullyAutheticated() - RememberMe 인증제외
  • permitAll()
  • denyAll()
  • anonymous() - 익명사용자 접근 허용
  • access(String) - SpEL 표현식
  • hasRole(String) - 역할
  • hasAuthority(String) - 권한
  • hasIpAddress(String) - 주어진 IP로부터 요청만 허용


테스트 사용자 만들고 인가 구현하기

    // 테스트 사용자 만들기
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception { //noop = 암호화 안하고 평문사용
        auth.inMemoryAuthentication().withUser("user1").password("{noop}1111").roles("USER");
        auth.inMemoryAuthentication().withUser("sys").password("{noop}1111").roles("SYS");
        auth.inMemoryAuthentication().withUser("admin").password("{noop}1111").roles("ADMIN");
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests() //모든요청에 대해 인가를 할 건데
            // 유저 페이지에는 USER롤이 있는 사용자 접근 허용시키고
           .antMatchers("/user").hasRole("USER")
           // /admin/pay 에는 ADMIN 롤이 있는 사용자  접근 허용
           .antMatchers("/admin/pay").hasRole("ADMIN")
           // 그 외 /admin 의 모든 페이지는 ADMIN이나 SYS 롤이 있으면 접근 허용
           .antMatchers("/admin/**").access("hasRole('ADMIN') or hasRole('SYS')")
           // 그 외의 모든 요청에 대해서는 인증이 필요하다.
           .anyRequest().authenticated();
        http
           .formLogin();
    }
  • 이렇게 해서 테스트 해볼 경우 User로 로그인하고 admin 페이지로 접속 시도하면 403 에러가 나오며
    /admin/pay 에는 admin 사용자만 접속할 수 있고 다른 /admin 페이지에는 sys, admin 모두 접속할 수 있다.

  • 하지만 유저 페이지에도 admin , sys 가 접속할 수 없다. sys, admin 권한이 user보다 상위 권한이라는 설정을 하지 않았기 때문이다. 향후 권한계층을 따로 구성해주어야 할 것이다.

  • 또한 서비스 확장,변경 시 계속해서 권한설정을 수동으로 변경하거나 선언해줘야 하는 방식이라 비효율적인 방식이라고 볼 수 있다.

    • 예) /admin/aaa 라는 기능을 추가했는데 이것도 ADMIN만 접근해야 한다면 설정에 한 줄 추가해주어야 한다.

글의 내용은 계속해서 추가될 예정입니다.


Reference