이미 SecurityConfig에서 특정 url에 대한 권한처리는 해줬다.
그러나 모든 권한 처리를 SecurityConfig에서 한다 가정하면,
Controller
코드 작성…SecurityConfig
파일 이동 후 권한 추가이런 프로세스로 할 것이다.
이런 프로세스 대로 권한을 일일히 추가하는 것은 꽤나 피로도가 쌓이는 일이다. 그리고 만약 url을 다르게 작성하기라도 하면 귀찮아진다.
Controller에서 코드를 작성하고 바로 그 자리에서 권한 처리를 해줄 수는 없을까?
물론 Spring Security에서 이런 어려움을 해결하기 위한 애노테이션을 제공한다.
이번 파트에선 권한 처리에 대한 애노테이션들에 대해 알아보도록 하겠다.
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
애노테이션 사용가능 여부에 대한 속성이다.❓ 주석처리된
@EnableGlobalMethodSecurity
는 뭐지?
원래는@EnableGlobalMethodSecurity
라는 애노테이션을 썼었던 모양이지만 얘도 Deprecated 되었다. 이 애노테이션 대신 스프링 시큐리티 5.6 버전부터 추가된@EnableMethodSecurity
를 사용하는 것이 메인이 된 것 같다.
@Secured()
// IndexController.java
//...
@Secured("ROLE_ADMIN")
@GetMapping("/info")
public @ResponseBody String info() {
return "개인정보";
}
이런 식으로 컨트롤러의 함수에 @Secured
애노테이션을 붙여주면, 애노테이션에 인자로 받은 권한이 유저에게 있을 때만 실행하도록 할 수 있다.
이런 식으로 편하게 컨트롤러 함수 개발하고, 바로 권한을 붙여주는 식으로 개발이 가능하다.
@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])
: 현재 사용자의 권한이 파라미터의 권한과 동일한 경우 truehasAnyRole([role1,role2 ...])
: 현재 사용자의 권한 파라미터의 권한 중 일치하는 것이 있는 경우 trueprincipal
: 사용자를 증명하는 주요객체(User)를 직접 접근할 수 있다.authentication
: SecurityContext에 있는 authentication 객체에 접근 할 수 있다.permitAll
: 모든 접근 허용denyAll
: 모든 접근 비허용isAnonymous()
: 현재 사용자가 익명(비로그인)인 상태인 경우 trueisRememberMe()
: 현재 사용자가 RememberMe 사용자라면 trueisAuthenticated()
: 현재 사용자가 익명이 아니라면 (로그인 상태라면) trueisFullyAuthenticated()
: 현재 사용자가 익명이거나 RememberMe 사용자가 아니라면 true@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
부분 내용을 번역해보자면,
정도로 이해할 수 있다.
한 줄로 이를 다 구현할 수 있다는 뜻이다. 심지어 직관적이기까지 하다! 따라서 이런 복잡한 권한 인증처리는 @Pre/PostAuthorize
로, 그렇지 않고 권한에 대한 인증만 필요할 땐 @Secure
를 사용하도록 하자.
💡 요약!
- 권한 인증만 할 때:
**@Secured**
- 복잡한 인증과정이 필요할 때:
**@Pre/PostAuthorize**
Spring Security @PreAuthorize, @PostAuthorize 를 사용하는 신박한 전처리 후처리 기법 — Steemit