Spring security/01.Security

12. Controller에서의 Security

  • -

이번 포스트에서는 @Controller에서 Spring Security 사용에 대해 알아보자.

Controller에서의 Spring Security

 

UserDetails 활용

Controller의 handler method에서 UserDetails를 파라미터로 받아서 활용할 수 있다. 이때 파라미터에는 @AuthenticationPrincipal 이라는 애너테이션이 선언되어 있어야 한다. 따라서 로그인 사용자 정보를 찾아서 더이상 직접 HttpSession을 사용하지 않아도 된다.

@GetMapping({"/", "/index"})
public String home(Model model, HttpSession session, 
                                @AuthenticationPrincipal UserDetails userDetails) {
  model.addAttribute("type", "home");
  Object user = session.getAttribute("SPRING_SECURITY_CONTEXT");
  Optional.ofNullable(user)
          .ifPresent(
            u -> {
              Object principal = ((SecurityContext) u).getAuthentication().getPrincipal();
              System.out.println(principal == userDetails);
            });
  Optional.ofNullable(userDetails)
          .ifPresent(
            u -> {
              log.debug(
                  "사용자명: {}, 권한: {}", userDetails.getUsername(), userDetails.getAuthorities());
            });
  return "index";
}

 

Controller의 메서드 레벨에서 접근 제어

 

전역 레벨 설정 vs 메서드 레벨 설절

HttpSecurity를 통한 설정은 전역 레베렝서 Spring Security를 선언하는 방식이다. 이 방식은 다음의 특징을 갖는다.

  • 전역적이고 선언적인 보안 규칙 설정
  • URL 기반의 접근 제어
  • 필터 체인을 통한 전체 애플리케이션 보안 설정

추가로 Controller의 요청 처리 메서드 호출 전/후에 권한을 체크하기 위해서는 @PreAuthorize와 @PostAuthorize를 사용할 수도 있다. 메서드 레벨로 접근할 때는 다음의 특징을 갖는다.

  • 메서드 레벨의 세밀한 접근 제어
  • 비지니스 로직 내부의 구체적인 권한 체크
  • 메서드 실행 전/후의 권한 검증 가능

따라서 단순한 URL 기반의 보안이라면 HttpSecurity로 충분하지만 좀 더 세밀하고 복잡한 권한 제어가 필요하다면 @PreAuthorize등의 메서드 레벨 보안을 추가로 사용하는 것이 좋다.

@PreAuthorize와 @PostAuthorize

메서드 수준에서 Spring Security를 사용하기 위해서는 @EnableMethodSecurity 애너테이션이 설정되어있어야 한다. 

@Configuration
@EnableWebSecurity(debug = true)
@EnableMethodSecurity(prePostEnabled = true) // 메서드 레벨의 제어를 위한 설정
public class SecurityConfig { . . . }

@PreAuthorize와 @PostAuthorize 애너테이션들은 SPEL이라는 것을 이용해서 복합적으로 인가 정보를 확인한다.  대부분 메서드가 문자열로 사용되는 형태라 낯설지 않을 것이다.

표현식 내용
hasRole(role) 역할이 부여된 권한과 일치하는지 확인함
예) "hasRole('ADMIN')"
hasAnyRole(role, ... ) 부여된 역할 중 일치하는 항목이 있는지 확인함
예) "hasAnyRole('ADMIN','STAFF')"
isAuthenticated() 인증된 사용자에 한함
예) "isAuthenticated()"
authentication Authentication 객체
예) "authentication.credentials", "authentication.authenticated"
principal Authentication의 getPrincipal() 실행 결과
예) "principal.username", "principal.enabled"


Spring Security에서 SPEL은 and나 or를 이용한 연산이 가능해서 복잡한 권한 설정에 아주 유용하다.

"hasRole('admin') and #user == principal.username"

다음은 @PreAuthorize가 사용되는 형태이다.

@PreAuthorize("isAuthenticated()")
@GetMapping("/userinfo")
public @ResponseBody UserDetails printUserInfo(@AuthenticationPrincipal UserDetails user) {
    return user;
}

/user는 인증이 완료된 경우만 동작하며 인증자의 정보를 REST 형태로 반환한다. 인증이 안된 경우라면 login 페이지로 이동한다.

 

@PreAuthorize("hasRole('ADMIN') and #user == principal.username")
@GetMapping("/update")
public @ResponseBody String updateUser(@RequestParam String user, @RequestParam String pass) {
    return String.format("%s의 비밀번호를 %s로 변경합니다.", user, pass);
}

위 예는 ADMIN role이 있고 파라미터인 user가 로그인 사용자의 username과 같다면 사용할 수 있다는 뜻이다. 메서드의 파라미터에 접근하기 위해서는 #파라미터 명 형태로 사용한다. 만약 권한이 없는 경우라면 Access-Denied가 발생한다.

@PostAuthorize는 메서드 실행 후에 권한을 확인한다. 메서드 내에서 인증 정보를 변경한 후 그 결과에 대해 접근 권한을 확인할 때 유용하다.

@Secured는 레거시 옵션으로 @PreAuthorize의 사용이 권장된다.
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.