Spring and Spring Boot/Class, Annotation, Library

Annotation : 스프링 시큐리티 관련 어노테이션 (@EnableWebSecurity, @EnableMethodSecurity, @AuthenticationPricipal)

마이트너 2024. 8. 22. 17:13

 


➡️ @EnableWebSecurity

Spring Security에서 웹 애플리케이션의 보안 구성을 활성화하는 데 사용되는 어노테이션으로, 웹 애플리케이션의 보안 설정을 구성할 수 있다. 또한, HTTP 요청에 대한 인증 및 권한 부여를 처리하는 WebSecurityConfigurerAdapter 클래스를 활성화시킨다.

 

 

 

✅ @EnableWebSecurit의 역할

 

  • 웹 보안 활성화 : Spring Security의 웹 보안 기능을 활성화하여, HTTP 요청에 대한 보안 설정을 구성할 수 있는 기반을 제공한다.
  • 보안 설정 커스터마이즈 : 예전에는 WebSecurityConfigurerAdapter를 상속하여 프로젝트의 특성에 따라 보안 설정을 커스터마이즈하는 방법을 많이 사용했으나, 최근에는 SecurityFilterChain과 UserDetailsService를 직접 정의하는 방식을 많이 사용한다. 

 

 

 

✅ @ EnableWebSecurity의 사용 방법

 

  • 기본적인 사용 방법 : @EnableWebSecurity를 설정 클래스에 추가하여 Spring Security의 기본 보안 기능을 활성화하는 방법이다. 이 기본 설정에서는 모든 요청이 인증을 필요로 하며, 로그인 및 로그아웃 기능을 허용한다.
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .anyRequest().authenticated()  // 모든 요청에 대해 인증 필요
            .and()
            .formLogin().permitAll()  // 모든 사용자가 로그인 페이지에 접근할 수 있도록 허용
            .and()
            .logout().permitAll();  // 모든 사용자가 로그아웃할 수 있도록 허용
    }
}

 

  • 사용자 정의 보안 설정
    • WebSecurityConfigurerAdapter를 상속받는 방식 : @EnableWebSecurity와 함께 WebSecurityConfigurerAdapter를 상속받아 보다 구체적인 보안 설정을 할 수 있다. 참고로 아래의 예시 코드 설정에서는 inMemoryAuthentication을 사용하여 메모리에 사용자 정보를 저장하고, PasswordEncoder를 사용하여 비밀번호를 인코딩하고 있다. 
    • SecurityFilterChain과 UserDetailsService를 직접 정의하는 방식 : 스프링시큐리티 5.7xx 부터WebSecurityConfigurerAdapter 보다는SecurityFilterChain Bean을 정의하는 것으로 권장하고 있다고 한다. 그 이유는 SecurityFilterChain Bean을 정의하는 것이 WebSecurityConfigurerAdapter를 상속받는 방식보다 더 명시적이고, 커스터마이징하기 쉬울 뿐만 아니라 필요한 보안 구성만을 선별해 적용할 수 있게 해주기 때문이다.
// WebSecurityConfigurerAdapter를 상속받는 방식

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .antMatchers("/public/**").permitAll()  // /public 경로는 인증 없이 접근 가능
            .antMatchers("/admin/**").hasRole("ADMIN")  // /admin 경로는 ADMIN 역할을 가진 사용자만 접근 가능
            .anyRequest().authenticated()  // 그 외 모든 요청은 인증 필요
            .and()
            .formLogin().permitAll()  // 로그인 페이지는 모든 사용자에게 허용
            .and()
            .logout().permitAll();  // 로그아웃은 모든 사용자에게 허용
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
            .withUser("user").password(passwordEncoder().encode("password")).roles("USER")
            .and()
            .withUser("admin").password(passwordEncoder().encode("admin")).roles("ADMIN");
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();  // 비밀번호 인코더를 정의
    }
}
// SecurityFilterChain과 UserDetailsService를 직접 정의하는 방식


@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .anyRequest().authenticated() // 모든 요청에 대해 인증 요구
                .and()
            .formLogin(); // 폼 로그인 활성화
        return http.build();
    }
}


@Bean
public UserDetailsService userDetailsService() {
    UserDetails user = User.withDefaultPasswordEncoder()
        .username("user")
        .password("password")
        .roles("USER")
        .build();
    return new InMemoryUserDetailsManager(user);
}

 

 

 


➡️ @EnableMethodSecurity

Spring Security에서 메소드 수준의 보안을 활성화하는 어노테이션으로, Spring Security가 메소드 수준에서 권한 부여 및 인증을 처리할 수 있게 한다.

 

 

✅ @EnableMethodSecurity의 용도

 

  • 메소드 보안 활성화 : 메소드에 대한 접근 제어를 설정할 수 있도록 Spring Security를 구성한다.
  • @Secured 및 @PreAuthorize와 @PostAuthorize 지원 : 메소드 호출 전후에 권한을 검사할 수 있게 해준다.

 

 

✅ @EnableMethodSecurity의 사용방법

  • @EnableMethodSecurity 설정 : Spring Security 설정 클래스에 @EnableMethodSecurity 어노테이션을 추가하여 메소드 보안을 활성화한다. 이 어노테이션은 securedEnabled 및 prePostEnabled와 같은 속성을 통해 다양한 메소드 보안 기능을 구성할 수 있다.
    • @EnableMethodSecurity의 속성
      • securedEnabled : @Secured 어노테이션의 사용을 활성화한다. 기본값은 false다.
      • prePostEnabled : @PreAuthorize 및 @PostAuthorize 어노테이션의 사용을 활성화한다. 기본값은 false다.
      • jsr250Enabled : @RolesAllowed 어노테이션의 사용을 활성화한다. 기본값은 false다.
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true, prePostEnabled = true)  // 메소드 보안 활성화
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .anyRequest().authenticated();
    }
}

 

  • @Secured 어노테이션 사용 : @Secured 어노테이션을 사용하여 특정 역할이나 권한을 가진 사용자만 메소드에 접근할 수 있도록 한다. 아래의 예제 코드에서 getAdminDashboard 메소드는 ROLE_ADMIN 권한을 가진 사용자만 접근할 수 있게 하고 있다.
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/admin")
public class AdminController {

    @GetMapping("/dashboard")
    @Secured("ROLE_ADMIN")
    public String getAdminDashboard() {
        return "Admin Dashboard";
    }
}

 

  • @PreAuthorize 및 @PostAuthorize 어노테이션 사용 : @PreAuthorize와 @PostAuthorize를 사용하여 메소드 호출 전후에 보다 복잡한 보안 표현식을 사용하여 접근을 제어할 수 있다. 아래 예제 코드에서 getUserProfile 메소드는 현재 인증된 사용자가 'USER' 역할을 가지고 있고, username 파라미터가 현재 인증된 사용자의 이름과 일치할 때만 접근할 수 있게 접근을 제어하고 있다.
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {

    @GetMapping("/profile")
    @PreAuthorize("hasRole('USER') and #username == authentication.name")
    public String getUserProfile(String username) {
        return "User Profile";
    }
}

 


➡️ @AuthenticationPrinciple

Spring Security에서 제공하는 어노테이션으로, 현재 인증된 사용자의 정보를 메소드 인자로 주입받기 위해 사용된다. 이 어노테이션을 사용하면 컨트롤러 메소드에서 현재 로그인한 사용자 정보에 접근할 수 있다.

 

✅ @AuthenticationPrinciple의 용도

 

  • 현재 인증된 사용자 정보 접근 : 인증된 사용자 객체를 메소드의 파라미터로 직접 주입
  • 편리한 사용자 정보 활용 : 인증된 사용자에 대한 세부 정보(예: 사용자 이름, 권한 등) 사용

✅ @AuthenticationPrinciple의 사용방법

  • Spring Security 설정 : Spring Security와 인증된 사용자 정보를 제공할 수 있는 설정이 되어 있어야만 @AuthenticaionPrinciple 어노테이션을 사용할 수 있다.

 

  • 컨트롤러에서 메소드의 파라미터로 주입받기 (with UserDetails) 
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/profile")
public class UserProfileController {

    @GetMapping
    public String getUserProfile(@AuthenticationPrincipal UserDetails userDetails) {
        // UserDetails는 현재 인증된 사용자의 정보를 포함
        String username = userDetails.getUsername();
        // 추가적으로 userDetails에서 권한, 비밀번호 등의 정보도 얻을 수 있음
        return "Current user: " + username;
    }
}

 

  • UserDetails 대신 커스텀 Principal 객체를 사용하는 경우 : User와 관련된 객체가 아닌 사용자가 정의한 객체를 반환하는 @AuthenticationPrincipal을 사용할 수 있다.
import org.springframework.security.core.userdetails.UserDetails;

public class CustomUser implements UserDetails {
    private String username;
    private String email;
    private Collection<? extends GrantedAuthority> authorities;
    
    // Getters, Setters, and other methods
}
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/custom-profile")
public class CustomUserProfileController {

    @GetMapping
    public String getCustomUserProfile(@AuthenticationPrincipal CustomUser customUser) {
        String username = customUser.getUsername();
        String email = customUser.getEmail();
        return "Custom user: " + username + " with email: " + email;
    }
}
728x90