공부기록/Spring Security

[이론] 스프링 시큐리티3

jhs0129 2022. 12. 26. 17:31
320x100
반응형

목차

서론

지금까지 인증(Authentication)에 대해 다뤄봤다면 이제는 권한 부여 즉 인가(Authorization)에 대해서 다뤄볼 차례이다

앞서서도 설명을 했지만 다시 한번 두개에 대해 말해보자면 다음과 같은 차이가 있다

  • 인증: 접근하는 사용자가 누구인지 확인(현관문 벨을 눌렀을 때 '누구세요?'라고 묻기)
  • 인가: 인증된 사용자에 대해서 권한을 확인하고 허락(누구인지 듣고 알고 있는 사람이라면 문 열어주기)

처음 Spring Security를 공부할 때 이 부분에 대해서 머리로는 이해하나 직접 작성하고 사용할 때에는 뭔가 혼란이 오기도 했다 꼭 차이를 알고 넘어가면 좋겠다

우리가 UsernamePasswordAuthenticationFilter를 사용하여 인증에 대한 처리를 한 것 처럼 인가(Authorization)에 대해서도 AuthorizationFilter가 존재하여 해당 필터에서 앞선 필터에서 제공하는 Authentication 객체를 사용하여 인가 처리를 하게 된다

GrantedAuthority

권한: 사용자가 시스템 자원을 사용하여 수행할 수 있는 작업

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

이러한 권한은 UserDetails에서 사용한다

public interface UserDetails extends Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();
    //생략
}

EndPoint 접근 설정

EndPoint는 Controller에 매핑하는 주소라고 생각하면 된다

이 부분에 대해서 설명이 되어있는 글 아래에 블로그 링크를 달아 뒀으니 확인해보면 되겠다

EndPoint 접근을 설정하는 방법에는 다음 두가지의 방법이 있다

  • 권한: 읽기, 쓰기, 삭제 등
  • 역할: User, Manager, 권한의 집합체라고 생각하면 된다
    • user는 읽기 쓰기만 가능한 권한을 가진 역할
    • manager는 읽기, 쓰기, 삭제가 가능한 권한을 가진 역할

우선은 모든 EndPoint에 대해서 설정을 해보자

특정 부분이 이난 모든 곳에 설정을 하고 싶으면 .anyRequest()를 사용하면 된다

권한 기반 접근 설정

  • hasAuthority(): 단일 권한
  • hasAnyAuthority(): 복수 권한
@RequiredArgsConstructor
public class CustomUser implements UserDetails {
    private final User user;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return List.of(() -> "권한_작성");
    }
    //생략
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests()
            .anyRequest()
            .hasAuthority("권한_작성");

    http.authorizeHttpRequests()
            .anyRequest()
            .hasAnyAuthority("권한_작성1", "권한_작성2", ...);
    return http.build();
}

UserDetails에 관한 객체 생성시 권한_작성 부분에 원하는 권한의 이름을 작성을 하면 된다

현 예시에는 객체 자체에 작성을 해 생성되는 모든 user에 대하여 동일한 권한을 주게 되지만 각 다른 권한을 주기 원한다면 AuthenticationProvider에서 반환하는 객체에 대해서 적용을 해주게 되면 된다

public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = authentication.getPrincipal().toString();
        UserDetails user = userService.loadUserByUsername(username);

        if (passwordEncoder.matches(password, user.getPassword())) {
            return new UsernamePasswordAuthenticationToken(
                    user.getUsername(),
                    user.getPassword(),
                    user.getAuthorities() // DB에 저장된 권한 가져옴
            );
        }
        throw new BadCredentialsException("BAD Credentials");
    }
}

역할 기반 접근 설정

권한 기반 설정과 거의 모든 것이 똑같고 설정시 사용되는 메소드만 다르다

  • hasRole()
  • hasAnyRole()

EndPoint 설정

위 코드에서 anyRequest부분을 변경해주면 된다

mvcMathcers() 권장

경로별, HttpMethod + 경로별로 적용할 수 있다

  • 해당 경로 이하의 모든 경로를 표시할라면 ** 접미사 사용 ex) /user/**
  • @pathVariable 사용시 ex) /user/{pathVariableName: 정규식}

antMatchers()

경로별, HttpMethod + 경로별, HttpMethod별로 적용할 수 있다

작성된 경로 그대로만 접근 설정이 된다

antMathcers("/a").authenticated() 와 같이 설정을 하였을 때 /a에 대해서는 권한이 요구되지만 /a/에 대해서는 요구되지 않는다

이러한 문제점 때문에 가능하면 mvcMathers()를 사용하는 것을 권장한다

regexMatchers()

정규식을 사용해서 경로를 지정하는 것인데 가능하면 사용하지 말자

혹시라도 위의 두개로도 해결이 안 될때 그때 찾아서 사용하는 것으로 하자

정규식을 조금이라도 잘못 작성하게 되면 찾기 어려운 오류로 화면을 만나게 될 것이다

참고 링크

EndPoint1

EndPoint2

320x100
반응형