➡️ @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다.
- @EnableMethodSecurity의 속성
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