본문 바로가기
개발 이론/Spring Security

[Spring Security] 권한 부여 구성 : 액세스

by dal_been 2023. 11. 10.
728x90

앞에 얘기한 스프링 시큐리티는 회원가입할때 비밀번호 해시화, 로그인시 비밀번호 확인하고 토큰 지급 까지의 과정이다.

이제는 인증이 완료된후 사용자 세부정보가 보안 컨텍스트에 저장된후 요청이 권한 부여 필터로 위임되어 어떤 단계를 걸치는지 알아볼 것이다.

 

간단하게 얘기하자면 권한 부여 필터가 요청을 허용할지 결정하고 권한이 부여되며 요청이 컨트롤러로 전달된다

 

앞의 UserDetails 구현한 클래스를 보면

 

Collections<? extends GrantedAuthority> getAuthorities(){};

 

메서드가 있을 것이다 이것이 바로 사용자에게 허가된 모든 권한을 반환하도록하는 메서드이다.

 

여기서 GrantedAuthority란 권한을 String 값으로 반환해주는 인터페이스다.

다시말해 애플리케이션은 사용자를 성공적으로 인증한후 GrantedAuthority인터페이스로 나타내는 권한으로 권한 부여를 수행한다

public interface GrantedAuthority extends Serializable{
	String getAuthority();
}

 


권한과 역할에 따라 접근 제한

 

사용자 권한 기준으로 모든 엔드포인트에 접근 제한

 

사용자에따라 제품에 대해 삭제를 할 권한과 쓸 권한이 있을 수 있고 어떤 사용자는 문서에 대해 업데이트하고 삭제할 권한이 있을 수 있다.

그렇다면 이런 권한은 어떻게 부여하는 것일까?

 

 

@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {

    @Override
    @Bean
    public UserDetailsService userDetailsService() {
        var manager = new InMemoryUserDetailsManager();

        var user1 = User.withUsername("john")
                        .password("12345")
                        .authorities("READ")
                        .build();

        var user2 = User.withUsername("jane")
                        .password("12345")
                        .authorities("WRITE")
                        .build();

        manager.createUser(user1);
        manager.createUser(user2);

        return manager;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic();

        //http.authorizeRequests().anyRequest().hasAnyAuthority("WRITE", "READ");
        //http.authorizeRequests().anyRequest().hasAuthority("WRITE");
        http.authorizeRequests().anyRequest().access("hasAuthority('WRITE')");
    }
}

 

  • InMemoryUserDetailsManager를 UserDetailsService로 등록하고 관련 인스턴스를 추가한다
  • http.authorizeRequests() : 엔드포인트에 권한 부여 규칙을 지정
  • anyRequest() : 이용된 URL이나 HTTP 방식과 관계없이 모든 요청에 대한 규칙을 적용
  • hasAuthority(): 해당 요청처리시 권한을 제한
  • access : 사용자가 권한을 가지고 있는지 확인하는 논리표현식

엔드포인트호출하여 인증및 권한이 성공하면 상태코드 200이 뜨고

권한이 실패하면 403(금지) 가 뜬다

 

hasAuthority 와 access
- 둘다 비슷하게 작용한다
- 다만 access는 스프링 식을 매개변수로 받아 and,or,not등 조합해 강력하고 유연한 표현식이 가능하다

@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {

    @Override
    @Bean
    public UserDetailsService userDetailsService() {
        var manager = new InMemoryUserDetailsManager();

        var user1 = User.withUsername("john")
                .password("12345")
                .authorities("read")
                .build();

        var user2 = User.withUsername("jane")
                .password("12345")
                .authorities("read", "write", "delete")
                .build();

        manager.createUser(user1);
        manager.createUser(user2);

        return manager;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic();


        String expression = "hasAuthority('read') and !hasAuthority('delete')";
        http.authorizeRequests()
                .anyRequest().access(expression);
    }
}

 

 

 

사용자 역할을 기준으로 모든 엔드포인트에 대한 접근을 제한

 

역할은 사용자가 수행할 수 있는 작업을 나타내는 다른 방법이다. 실 애플리케이션에서는 권한과 함께 역할도 이용되므로 역할의 개념과, 권한과의 차이점을 이해하는 것이 중요

 

역할과 권한??
예를 들어 운영자는 읽기와 쓰기가 가능하고 관리자는 읽기,쓰기,삭제,업데이트 가능하다고 하자

여기서 마리아에게는 운영자 역할을 부여하고, 재임스에게는 관리자 역할을 부여한다.

즉 역할은 운영자또는 관리자이고, 권한은 읽기,쓰기,삭제등이라고 할 수 있다
그래서 애플리케이션에 역할을 이용하면 더는 권한을 정의할 필요가 없다. 

 

 

 

역할을 이용한 예제를 보자

역할도 스프링 시큐리티에서 같은 GrantedAuthority계약으로 나타낸다. 역할을 정의할때 역할이름은 " ROLE_ " 접두사로 시작해야한다

 

@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {

    @Override
    @Bean
    public UserDetailsService userDetailsService() {
        var manager = new InMemoryUserDetailsManager();

        var user1 = User.withUsername("john")
                        .password("12345")
                        .roles("ADMIN")
                        .build();

        var user2 = User.withUsername("jane")
                        .password("12345")
                        .roles("MANAGER")
                        .build();

        manager.createUser(user1);
        manager.createUser(user2);

        return manager;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic();

        http.authorizeRequests().anyRequest().hasRole("ADMIN");
    }
}

 

  • hasRole() : 애플리케이션이 요청을 승인할 하나의 역할 이름을 매개변수로 받음
  • hasAnyRole()
  • access () : 요쳥을 승인할 역할을 스프링식으로 지정
위의 코드는 roles()로 되어있는데 만약 authorities()를 호출한다면 ROLE_ 접두사를 지정해야한다

 

 

 


정리

  • 권한 부여는 애플리케이션이 인증된 요청을 허가할지 결정하는 프로세스(항상 인증후에 권한 부여)
  • 애플리케이션은 인증된 사용자의 권한과 역할에따라 권한을 부여하는 방법 구성가능
  • 매플리케이션에서 인증되지 않은 사용자가 특정 요청을 수행할 수 있게 지정가능