현대 웹 애플리케이션에서는 보안이 중요한 요소이며, Spring Security는 강력한 인증 및 권한 관리 기능을 제공합니다. 이번 글에서는 Spring Security를 활용하여 사용자 인증(Authentication) 및 **권한 관리(Authorization)**를 구현하는 방법을 심층적으로 분석하고, 실무에서의 활용 방안을 설명하겠습니다.
Spring Security는 Spring 기반 애플리케이션의 보안을 강화하는 프레임워크로, 다음과 같은 기능을 제공합니다.
- 사용자 인증(Authentication): 로그인 및 세션 관리합니다.
- 권한 부여(Authorization): 역할(Role) 기반 접근 제어하게 합니다.
- 보안 기능 내장: CSRF, CORS, 세션 관리, 암호화 지원합니다.
- OAuth2 및 JWT 지원: 소셜 로그인 및 토큰 기반 인증을 지원합니다.
Spring Boot 프로젝트에서 손쉽게 적용할 수 있도록 기본적인 보안 설정을 제공합니다.
Spring Security를 프로젝트에 추가하려면 의존성을 추가해야 합니다.
1) Spring Boot 프로젝트에 Spring Security 추가
pom.xml
에 Spring Security 관련 의존성을 추가합니다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2) 기본 보안 설정
Spring Security를 추가하면 기본적인 로그인 인증이 자동 적용됩니다. 기본적으로 /login
페이지가 제공되며, user
계정과 자동 생성된 비밀번호로 로그인할 수 있습니다.
로그인 시 생성된 기본 비밀번호 확인
Using generated security password: a1b2c3d4e5
사용자 정의 보안 설정을 위해 SecurityConfig 클래스를 작성해야 합니다.
사용자 인증을 위해 Spring Security의 UserDetailsService를 활용하여 사용자 정보를 저장하고 검증할 수 있습니다.
1) 사용자 정보 저장 (UserDetailsService 구현)
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.Collections;
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if ("admin".equals(username)) {
return new User("admin", "{noop}password", Collections.emptyList());
}
throw new UsernameNotFoundException("User not found");
}
}
{noop}
: 비밀번호를 암호화하지 않음을 의미하며, 실제 환경에서는 BCrypt 등의 암호화를 적용해야 합니다.loadUserByUsername
: 사용자의 인증 정보를 데이터베이스에서 가져오는 역할을 합니다.
2) 보안 설정 (SecurityConfig 작성)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin").hasRole("ADMIN")
.requestMatchers("/user").hasRole("USER")
.anyRequest().authenticated()
)
.formLogin()
.and()
.logout();
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
authorizeHttpRequests()
: 특정 URL에 대한 접근 권한을 설정합니다.formLogin()
: 기본 로그인 폼을 활성화합니다.logout()
: 로그아웃 기능을 활성화합니다.BCryptPasswordEncoder()
: 비밀번호를 안전하게 저장하도록 설정합니다.
4. 권한(Role) 기반 접근 제어 (Authorization)
Spring Security는 **역할(Role)**을 기반으로 사용자의 접근을 제어할 수 있습니다.
1) 사용자 역할 설정
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.List;
public class CustomUserDetails extends User {
public CustomUserDetails(String username, String password, String role) {
super(username, password, List.of(new SimpleGrantedAuthority(role)));
}
}
2) 권한 확인 예제
컨트롤러에서 @PreAuthorize
애너테이션을 사용하여 역할 기반 접근을 제한할 수 있습니다.
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("/api")
public class ApiController {
@GetMapping("/admin")
@PreAuthorize("hasRole('ADMIN')")
public String adminEndpoint() {
return "Admin Access";
}
@GetMapping("/user")
@PreAuthorize("hasRole('USER')")
public String userEndpoint() {
return "User Access";
}
}
5. 문의 내용 예시 및 답변
1. Spring Security에서 JWT를 활용한 인증을 적용하려면 어떻게 해야 하나요?
답변: JWT(JSON Web Token)를 활용하면 세션을 유지하지 않고 인증할 수 있습니다. spring-security-oauth2
및 jjwt
라이브러리를 활용하여 JWT 인증을 구현할 수 있습니다.
2. OAuth2 기반의 소셜 로그인을 Spring Security에서 어떻게 적용하나요?
답변: Spring Security의 spring-security-oauth2-client
라이브러리를 활용하면 Google, Facebook 등의 OAuth2 인증을 쉽게 구현할 수 있습니다.
3. Spring Security에서 CSRF 보호를 비활성화할 수 있나요?
답변: 기본적으로 Spring Security는 CSRF 보호를 활성화합니다. API 서버에서 CSRF가 필요 없을 경우 http.csrf().disable();
을 설정하여 비활성화할 수 있습니다.