환경

Spring Boot 3.3.3

Spring Security 6.3.3

 

문제

@PreAuthorize/@Secured - NullPointerException


  
//WebSecurityConfig.java
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)
@RequiredArgsConstructor
public class WebSecurityConfig {
....
}

  
//UserController.java
...uesrService 주입..
@PostMapping(value = "/master/create", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@PreAuthorize("hasRole(T(~~.UserRoleEnum).MASTER.getAuthority())")
private ResponseEntity<String> createUserByMaster(
@ModelAttribute @Valid UserCreateDto requestDto
) {
userService.createUserByMaster(requestDto);
return ResponseEntity.ok().body("success");
}

User Role에 따라 Controller에서 차단할 때,

대략 위와 같이 Config에 EnableMethodSecurity를 설정하고 @PreAuthorize 어노테이션을 적용하면, 주입한 userService가 null로 뜨는 에러이다.

 

+)

hasRole에 Enum 객체 이용하던 다르게 값을 넣던, 아무튼 String이 들어가도록

  • hasRole - A shortcut for hasAuthority that prefixes ROLE_ or whatever is configured as the default prefix

예를 들어 "ROLE_MASTER"인 경우

  • hasRole('MASTER') : O
  • hasRole('ROLE_MASTER') : O
  • hasAuthority('MASTER') : X
  • hasAuthority('ROLE_MASTER') : O

Expressing Authorization with SpEL 참고

https://docs.spring.io/spring-security/reference/servlet/authorization/method-security.html#using-authorization-expression-fields-and-methods

 

Method Security :: Spring Security

There are some scenarios where you may not wish to throw an AuthorizationDeniedException when a method is invoked without the required permissions. Instead, you might wish to return a post-processed result, like a masked result, or a default value in cases

docs.spring.io

 

해결

https://stackoverflow.com/questions/77083624/nullpointerexception-when-using-preauthorize-in-spring-boot

 

NullPointerException when using @PreAuthorize in Spring Boot

I am working on a Spring Boot project where I have implemented Spring Security to manage JWT authentication. I have a ProductController with a ProductService autowired in it. When I add the @PreAut...

stackoverflow.com

https://github.com/spring-projects/spring-security/issues/8879

 

NPE when using @PreAuthorize in a Controller inheriting from a base class · Issue #8879 · spring-projects/spring-security

Describe the bug When using @PreAuthorize("isAuthenticated()") on a Controller that inherits from a base class, where both controller classes define methods with a @RequestMapping, dependencies tha...

github.com


  
//UserController.java
...uesrService 주입..
@PostMapping(value = "/master/create", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@PreAuthorize("hasRole(T(~~.UserRoleEnum).MASTER)")
public ResponseEntity<String> createUserByMaster( //<---public
@ModelAttribute @Valid UserCreateDto requestDto
) {
System.out.println("pass");
userService.createUserByMaster(requestDto);
return ResponseEntity.ok().body("success");
}

security는 proxy를 만들어 메서드 호출을 가로챈다.

controller 메서드 접근자가 공개되어야 security proxy가 접근할 수 있다. private을 public으로 바꿔주면 해결된다.

 

private으로 하면 proxy가 안되고 권한 접근 제어를 할 수 없다.

그래서 마스터가 아니어도 일단 패스가 되고, 거기에 더해 NPE 에러도 발생한다.

 

이슈탭 대화에도 나와있는데, 해당 케이스를 설명하는 에러 메시지를 받으면 더 좋을 텐데.

stack trace에서 security proxy를 보고 짐작하기에는, NPE이 짚어볼 부분도 많아서 헛다리 짚기 딱 좋다.