Spring Security란? 🤔
Spring Security는 스프링 기반 애플리케이션의 보안(인증, 인가, 권한)을 담당하는 스프링 하위 프레임워크입니다. 인증(Authentication)과 인가(Authorization)를 통해 애플리케이션을 보호하며, CSRF, 세션 고정 공격 등 다양한 보안 공격으로부터 애플리케이션을 방어합니다.
인증(Authentication)과 인가(Authorization)
- 인증(Authentication): 사용자의 신원을 확인하는 과정입니다. 예를 들어, 사용자가 로그인할 때 그 사용자가 누구인지 확인하는 절차입니다.
- 인가(Authorization): 사용자가 애플리케이션의 특정 리소스나 기능에 접근할 수 있는지 확인하는 과정입니다. 예를 들어, 관리자만 관리자 페이지에 접근할 수 있도록 제한하는 작업이 인가에 해당합니다.
스프링 시큐리티의 필터 기반 동작 🧑🏻⚖️
Spring Security는 필터 기반으로 동작합니다. 다양한 필터들이 순차적으로 인증과 인가 작업을 처리하며, 각 필터는 특정 역할을 수행합니다. 대표적인 필터로는 UsernamePasswordAuthenticationFilter와 FilterSecurityInterceptor가 있습니다.
- UsernamePasswordAuthenticationFilter: 폼 기반 로그인 시 사용되는 필터로, 아이디와 패스워드를 받아서 인증을 요청합니다. 인증이 성공하면 AuthenticationSuccessHandler가 실행되고, 실패하면 AuthenticationFailureHandler가 실행됩니다.
- FilterSecurityInterceptor: 권한 부여를 처리하는 필터로, AccessDecisionManager를 통해 사용자가 요청한 리소스에 접근할 권한이 있는지 판단합니다.
스프링 시큐리티의 인증 처리 흐름
- 사용자가 폼에 아이디와 패스워드를 입력합니다.
- UsernamePasswordAuthenticationFilter가 아이디와 패스워드를 검증하고, UsernamePasswordAuthenticationToken을 생성합니다.
- 생성된 인증용 토큰을 AuthenticationManager에게 전달하여 인증을 진행합니다.
- AuthenticationProvider가 사용자 아이디를 UserDetailsService로 전달하여 사용자의 정보를 UserDetails 객체로 가져옵니다.
- 인증이 성공하면 SecurityContextHolder에 인증 정보를 저장하고, 인증 성공/실패 여부에 따라 AuthenticationSuccessHandler 또는 AuthenticationFailureHandler가 실행됩니다.
Spring Security 구현 예시
다음은 Form 기반의 로그인과 로그아웃을 구현하는 간단한 예시입니다.
1. 의존성 추가 (build.gradle)
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
testImplementation 'org.springframework.security:spring-security-test'
}
2. 엔티티 작성
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Table(name = "users")
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String email;
@Column(nullable = false)
private String password;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(new SimpleGrantedAuthority("user"));
}
@Override
public String getUsername() {
return email;
}
@Override
public String getPassword() {
return password;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
UserDetails인터페이스: 사용자의 인증 정보를 나타내며, 이를 구현하여 사용자 정보를 제공하는 엔티티를 정의합니다.
3. 리포지터리 작성
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
}
4. 서비스 작성 (UserDetailService)
@Service
@RequiredArgsConstructor
public class UserDetailService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
return userRepository.findByEmail(email).orElseThrow(() -> new IllegalArgumentException(email));
}
}
UserDetailsService인터페이스를 구현하여 사용자의 정보를 가져오는 메서드를 작성합니다.
5. 시큐리티 설정 (WebSecurityConfig)
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class WebSecurityConfig {
private final UserDetailsService userService;
@Bean
public WebSecurityCustomizer configure() {
return (web) -> web.ignoring().requestMatchers(new AntPathRequestMatcher("/static/**"));
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers(
new AntPathRequestMatcher("/login"),
new AntPathRequestMatcher("/signup"),
new AntPathRequestMatcher("/user")
).permitAll()
.anyRequest().authenticated())
.formLogin(formLogin -> formLogin
.loginPage("/login")
.defaultSuccessUrl("/articles")
)
.logout(logout -> logout.logoutSuccessUrl("/login")
.invalidateHttpSession(true)
)
.csrf(AbstractHttpConfigurer::disable)
.build();
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
SecurityFilterChain: 인증, 인가, 로그인, 로그아웃 관련 설정을 정의합니다.
6. 회원 가입 구현
서비스 메서드 작성:
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
public Long save(AddUserRequest dto) {
return userRepository.save(User.builder()
.email(dto.getEmail())
.password(bCryptPasswordEncoder.encode(dto.getPassword()))
.build()).getId();
}
}
컨트롤러 작성:
@Controller
@RequiredArgsConstructor
public class UserApiController {
private final UserService userService;
@PostMapping("/user")
public String signup(AddUserRequest request) {
userService.save(request);
return "redirect:/login";
}
@GetMapping("/logout")
public String logout(HttpServletRequest request, HttpServletResponse response) {
new SecurityContextLogoutHandler().logout(request, response, SecurityContextHolder.getContext().getAuthentication());
return "redirect:/login";
}
}
Spring Security의 활용 🧐
- 로그인/로그아웃 기능 구현
- 특정 페이지에 대한 접근 권한 설정 (예: 관리자 페이지 접근 제한)
- CSRF 방어 및 세션 관리
Spring Security는 필터 기반으로 동작하며, 사용자 인증과 인가 정보를 UserDetails 객체에 담아 관리합니다. 이를 통해 애플리케이션의 보안을 강화하고, 특정 사용자만 리소스에 접근할 수 있도록 제어할 수 있습니다.
'🌱 Spring > Spring Boot' 카테고리의 다른 글
| [Spring] JWT (0) | 2024.10.16 |
|---|---|
| [Spring] Spring Batch - Batch와 Job, Step (1) | 2024.10.16 |
| [Spring] Spring MVC (0) | 2024.10.16 |
| [Spring] @Annotation (0) | 2024.10.16 |
| [Spring] Spring AOP (0) | 2024.10.16 |
