[Spring Security] 4. 권한 처리

Beomsu Son·2023년 6월 23일
11
post-thumbnail

0. 이미 권한 처리는 하지 않았나?


이미 SecurityConfig에서 특정 url에 대한 권한처리는 해줬다.

그러나 모든 권한 처리를 SecurityConfig에서 한다 가정하면,

  1. Controller 코드 작성…
  2. 작성 완료
  3. SecurityConfig 파일 이동 후 권한 추가

이런 프로세스로 할 것이다.

이런 프로세스 대로 권한을 일일히 추가하는 것은 꽤나 피로도가 쌓이는 일이다. 그리고 만약 url을 다르게 작성하기라도 하면 귀찮아진다.

Controller에서 코드를 작성하고 바로 그 자리에서 권한 처리를 해줄 수는 없을까?

물론 Spring Security에서 이런 어려움을 해결하기 위한 애노테이션을 제공한다.

이번 파트에선 권한 처리에 대한 애노테이션들에 대해 알아보도록 하겠다.

1. EnableMethodSecurity


// SecurityConfig.java

@Configuration
@EnableWebSecurity // 스프링 시큐리티 필터가 스프링 필터체인에 등록이 됨.
//@EnableGlobalMethodSecurity // deprecated 됨. EnableMethodSecurity 를 대신 사용하라고 한다.
@EnableMethodSecurity(securedEnabled = true, prePostEnabled = true) // securedEnabled => Secured 애노테이션 사용 여부, prePostEnabled => PreAuthorize 어노테이션 사용 여부.
public class SecurityConfig {
//...

우선 SecurityConfig.java 파일로 가서 해당 클래스에 @EnableMethodSecurity 애노테이션을 달아준다. 이 애노테이션은 함수에 권한 처리 애노테이션을 붙일 수 있게할지 말지에 관한 설정을 담당한다.

각각의 속성에 대해서 알아보자.

  • securedEnabled: @Secure 애노테이션 사용가능 여부에 대한 속성이다.
  • prePostEnabled: @PreAuthorize, @PostAuthorize 애노테이션 사용가능 여부에 대한 속성이다.
    • default는 true로 되어있다. 따라서 위의 두 애노테이션을 사용할 생각이라면 굳이 속성을 안 적어도 된다.

❓ 주석처리된 @EnableGlobalMethodSecurity는 뭐지?
원래는 @EnableGlobalMethodSecurity 라는 애노테이션을 썼었던 모양이지만 얘도 Deprecated 되었다. 이 애노테이션 대신 스프링 시큐리티 5.6 버전부터 추가된 @EnableMethodSecurity 를 사용하는 것이 메인이 된 것 같다.

2. @Secured()


// IndexController.java
//...

		@Secured("ROLE_ADMIN")
    @GetMapping("/info")
    public @ResponseBody String info() {
        return "개인정보";
    }

이런 식으로 컨트롤러의 함수에 @Secured 애노테이션을 붙여주면, 애노테이션에 인자로 받은 권한이 유저에게 있을 때만 실행하도록 할 수 있다.

이런 식으로 편하게 컨트롤러 함수 개발하고, 바로 권한을 붙여주는 식으로 개발이 가능하다.

3. @PreAuthorize, @PostAuthorize


// IndexController.java
// ...
		@PreAuthorize("hasAnyRole('ROLE_MANAGER', 'ROLE_ADMIN')")
    @GetMapping("/data")
    public @ResponseBody String data() {
        return "데이터정보";
    }
  • @PreAuthorize: 메서드가 실행되기 전에 인증을 거친다.
  • @PostAuthorize: 메서드가 실행되고 나서 응답을 보내기 전에 인증을 거친다.

Secured 애노테이션과는 다르게 함수를 String 형태로 넣어줄 수 있고, and나 or 등 논리 연산자도 넣어줄 수 있다.

💡 애노테이션 내에서 사용가능한 함수/기능들

  • hasRole([role]) : 현재 사용자의 권한이 파라미터의 권한과 동일한 경우 true
  • hasAnyRole([role1,role2 ...]) : 현재 사용자의 권한 파라미터의 권한 중 일치하는 것이 있는 경우 true
  • principal: 사용자를 증명하는 주요객체(User)를 직접 접근할 수 있다.
  • authentication : SecurityContext에 있는 authentication 객체에 접근 할 수 있다.
  • permitAll : 모든 접근 허용
  • denyAll : 모든 접근 비허용
  • isAnonymous() : 현재 사용자가 익명(비로그인)인 상태인 경우 true
  • isRememberMe() : 현재 사용자가 RememberMe 사용자라면 true
  • isAuthenticated() : 현재 사용자가 익명이 아니라면 (로그인 상태라면) true
  • isFullyAuthenticated() : 현재 사용자가 익명이거나 RememberMe 사용자가 아니라면 true

4. @Pre/PostAuthorize, 왜 사용할까?


@Secured 애노테이션을 이용하면 사용자의 권한정보에 따라 자동으로 해당 메서드의 접근을 제한할 수 있게 된다. 굉장히 편한 방식이다. 이 애노테이션 만으로도 권한 처리를 해도 괜찮을 것 같은 기분이 들 수 있다.

그러나 실제 개발에서는 @Secured만 사용하기엔 조금 힘들 수 있다. 이해를 하기 위해 예를 들어보자.

“사용자 본인과 관리자만 접근할 수 있는 메서드로 지정하고 싶다” 라는 요구사항이 있다고 가정해보자. @Secured 애노테이션은 “권한”만 인증하지, 사용자가 누구인지 “식별”은 하지 않는다.

예를 통해 설명하자면 요청에서 글의 id를 받아 글을 삭제하는 기능일때, @Secure("ROLE_USER") 형식으로 뒀다고 가정해보자. 이러면 내가 로그인 하던가 해서 어떻게든 ROLE_USER 권한만 가지고 있다면, 내가 작성한 글이 아니더라도 삭제가 가능하다는 말이다.

즉 이런 기능들에는 사용자가 누구인지에 대한 “식별과정”도 필요하다.

일반적으로 어떻게 이를 개발할까 생각을 해보자.

이 애노테이션들에 대해 잘 모르는 상황이라면, 아마SecurityContextHolder.getContext().getAuthentication().getPrincipal()

이런 식으로 유저 정보를 받아와 내가 삭제/수정/etc… 하려는 컨텐츠의 작성자와 같은 지 비교를 할 것이다. 그러나 이런 식의 코드는 너무 길고 복잡하며 알아보기 힘들다.

다시말해 더럽다.

이를 더 깔끔하게, 더 알아보기 쉽게 만들기 위해서 @Pre/PostAuthorize 애노테이션에서 사용할 수 있는 함수들을 사용해 편하게 구현하는 것이다.

**@PostAuthorize("isAuthenticated() and (( returnObject.name == principal.name ) or hasRole('ROLE_ADMIN'))")**
@RequestMapping( value = "/{seq}", method = RequestMethod.GET )
public User getuser( @PathVariable("seq") long seq ){
    return userService.findOne(seq);
}

이 코드의 @PostAuthorize 부분 내용을 번역해보자면,

  • 잠깐, 클라이언트한테 응답하기 전에 검문이 있겠습니다. 로그인상태 입니까?
  • 그리고 반환되는 사용자의 이름과 현재 사용자의 이름이 일치합니까? 또는 현재 사용자가 관리자 권한(ROLE_ADMIN)을 들고있습니까?
  • 이 조건을 만족하는 사용자의 경우에만 응답할 수 있습니다. 아니라면 403 에러로 응답해드립니다.

정도로 이해할 수 있다.

한 줄로 이를 다 구현할 수 있다는 뜻이다. 심지어 직관적이기까지 하다! 따라서 이런 복잡한 권한 인증처리는 @Pre/PostAuthorize 로, 그렇지 않고 권한에 대한 인증만 필요할 땐 @Secure 를 사용하도록 하자.

💡 요약!

  • 권한 인증만 할 때: **@Secured**
  • 복잡한 인증과정이 필요할 때: **@Pre/PostAuthorize**

참고한 자료


Spring Security @PreAuthorize, @PostAuthorize 를 사용하는 신박한 전처리 후처리 기법 — Steemit

profile
생각날 때마다 기록하기

0개의 댓글