Home > SpringSecurity > πŸ”[Spring Security] Spring Securityλž€?

πŸ”[Spring Security] Spring Securityλž€?
Spring Security Framework Authentication Authorization

πŸ”[Spring Security] Spring Securityλž€?

λͺ©μ°¨


Spring Security κ°œμš”

Spring SecurityλŠ” Spring 기반 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ λ³΄μ•ˆ(인증과 인가)을 μ „λ‹΄ν•˜λŠ” κ°•λ ₯ν•˜κ³  ν•„μˆ˜μ μΈ ν”„λ ˆμž„μ›Œν¬μž…λ‹ˆλ‹€.

🎯 핡심 μ—­ν• 

β€œμš°λ¦¬ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ λ¬Έμ§€κΈ°μ΄μž μ ‘κ·Ό μ œμ–΄ κ΄€λ¦¬μžβ€

Spring Securityκ°€ μ—†λ‹€λ©΄?

  • κ°œλ°œμžκ°€ 직접 μ„Έμ…˜ 관리 μ½”λ“œ μž‘μ„±
  • URLλ§ˆλ‹€ κΆŒν•œ 체크 둜직 쀑볡 μž‘μ„±
  • λͺ¨λ“  μ»¨νŠΈλ‘€λŸ¬μ™€ μ„œλΉ„μŠ€μ— λ³΄μ•ˆ μ½”λ“œκ°€ λ’€μ„žμž„

Spring SecurityλŠ” 이 λͺ¨λ“  것을 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ 핡심 둜직과 λΆ„λ¦¬ν•˜μ—¬ μ²΄κ³„μ μœΌλ‘œ μ²˜λ¦¬ν•©λ‹ˆλ‹€.


핡심 κ°œλ…: 인증과 인가

Spring Security의 쑴재 μ΄μœ λŠ” λ°”λ‘œ 이 두 κ°€μ§€ κ°œλ…μ„ ν•΄κ²°ν•˜κΈ° μœ„ν•¨μž…λ‹ˆλ‹€.

1️⃣ 인증 (Authentication)

β€œλ‹Ήμ‹ μ€ λˆ„κ΅¬μ‹­λ‹ˆκΉŒ?”

μ‚¬μš©μžκ°€ μžμ‹ μ˜ 신원을 증λͺ…ν•˜λŠ” κ³Όμ •μž…λ‹ˆλ‹€.

인증 방법 μ˜ˆμ‹œ

  • ID/Password μž…λ ₯
  • μ†Œμ…œ 둜그인 (OAuth2)
  • JWT 토큰 제좜
  • 생체 인증

λ™μž‘ κ³Όμ •

  1. μ‚¬μš©μžκ°€ 둜그인 μš”μ²­
  2. Spring Securityκ°€ μš”μ²­μ„ κ°€λ‘œμ±„μ„œ Credentials 검증
  3. 인증 성곡 μ‹œ SecurityContext에 Authentication 객체 μ €μž₯
// 인증 정보 쑰회 μ˜ˆμ‹œ
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String username = authentication.getName();

2️⃣ 인가 (Authorization)

β€œλ‹Ήμ‹ μ΄ 이 μž‘μ—…μ„ ν•  κΆŒν•œμ΄ μžˆμŠ΅λ‹ˆκΉŒ?”

인증된 μ‚¬μš©μžκ°€ νŠΉμ • λ¦¬μ†ŒμŠ€μ— μ ‘κ·Όν•  κΆŒν•œμ΄ μžˆλŠ”μ§€ ν™•μΈν•˜λŠ” κ³Όμ •μž…λ‹ˆλ‹€.

κΆŒν•œ 체크 μ˜ˆμ‹œ

  • /admin β†’ ROLE_ADMIN κΆŒν•œ ν•„μš”
  • /my-page β†’ ROLE_USER κΆŒν•œμœΌλ‘œ μΆ©λΆ„
  • /public β†’ λˆ„κ΅¬λ‚˜ μ ‘κ·Ό κ°€λŠ₯

μ„€μ • 방법

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
                .requestMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
            );
        return http.build();
    }
}
// λ©”μ†Œλ“œ 레벨 λ³΄μ•ˆ
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long userId) {
    // ADMIN만 μ‹€ν–‰ κ°€λŠ₯
}

λ™μž‘ 원리: μ„œλΈ”λ¦Ώ ν•„ν„° 체인

이것이 Spring Security의 κ°€μž₯ μ€‘μš”ν•œ μ•„ν‚€ν…μ²˜μž…λ‹ˆλ‹€.

πŸ“Œ 핡심 κ°œλ…

Spring SecurityλŠ” FilterChainProxyλΌλŠ” ν•˜λ‚˜μ˜ κ±°λŒ€ν•œ ν•„ν„°λ₯Ό μ„œλΈ”λ¦Ώ μ»¨ν…Œμ΄λ„ˆ(Tomcat)에 λ“±λ‘ν•©λ‹ˆλ‹€.

이 ν•„ν„° λ‚΄λΆ€μ—λŠ” μ—¬λŸ¬ 개의 λ³΄μ•ˆ ν•„ν„°λ“€λ‘œ κ΅¬μ„±λœ 체인(Chain)이 μ‘΄μž¬ν•©λ‹ˆλ‹€.

πŸ”„ μš”μ²­ 처리 흐름

λͺ¨λ“  μ›Ή μš”μ²­μ€ μ»¨νŠΈλ‘€λŸ¬μ— λ„λ‹¬ν•˜κΈ° 전에 λ°˜λ“œμ‹œ 이 ν•„ν„° 체인을 ν†΅κ³Όν•©λ‹ˆλ‹€.

Client Request
    ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Spring Security Filter Chain   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  1. CorsFilter                  β”‚
β”‚  2. CsrfFilter                  β”‚
β”‚  3. JwtAuthenticationFilter     β”‚
β”‚  4. UsernamePasswordAuthFilter  β”‚
β”‚  5. ExceptionTranslationFilter  β”‚
β”‚  6. AuthorizationFilter         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    ↓
DispatcherServlet
    ↓
Controller

πŸ’‘ JWT 기반 인증 μ‹œ ν•„ν„° 체인 λ™μž‘ μ˜ˆμ‹œ

1단계: μš”μ²­ μ‹œμž‘

ν΄λΌμ΄μ–ΈνŠΈκ°€ Authorization 헀더에 JWTλ₯Ό λ‹΄μ•„ μš”μ²­
GET /api/user/profile
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

2단계: ν•„ν„° 체인 톡과

β‘  CorsFilter

  • CORS μ •μ±… 확인
  • Cross-Origin μš”μ²­ ν—ˆμš© μ—¬λΆ€ 검증

β‘‘ CsrfFilter

  • CSRF 토큰 검증 (Stateless ν™˜κ²½μ—μ„œλŠ” 보톡 λΉ„ν™œμ„±ν™”)

β‘’ JwtAuthenticationFilter (μ»€μŠ€ν…€ ν•„ν„°)

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) {
        // 1. ν—€λ”μ—μ„œ JWT μΆ”μΆœ
        String token = extractToken(request);

        // 2. JWT μœ νš¨μ„± 검증
        if (token != null && jwtTokenProvider.validateToken(token)) {
            // 3. ν† ν°μ—μ„œ μ‚¬μš©μž 정보 μΆ”μΆœ
            Authentication auth = jwtTokenProvider.getAuthentication(token);

            // 4. SecurityContext에 인증 정보 μ €μž₯
            SecurityContextHolder.getContext().setAuthentication(auth);
        }

        filterChain.doFilter(request, response);
    }
}

β‘£ ExceptionTranslationFilter

  • 인증/인가 κ³Όμ •μ—μ„œ λ°œμƒν•˜λŠ” μ˜ˆμ™Έ 처리
  • AuthenticationException, AccessDeniedException 처리

β‘€ AuthorizationFilter (κ³Όκ±° FilterSecurityInterceptor)

// μ‚¬μš©μžκ°€ μš”μ²­ν•œ URL에 μ ‘κ·Ό κΆŒν•œμ΄ μžˆλŠ”μ§€ μ΅œμ’… 확인
// 예: /admin/** β†’ ROLE_ADMIN κΆŒν•œ ν•„μš”

3단계: 컨트둀러 도달

λͺ¨λ“  ν•„ν„°λ₯Ό ν†΅κ³Όν•˜λ©΄ DispatcherServlet을 거쳐 Controller의 둜직이 μ‹€ν–‰λ©λ‹ˆλ‹€.

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

    @GetMapping("/profile")
    public UserProfile getProfile() {
        // SecurityContextμ—μ„œ 인증된 μ‚¬μš©μž 정보 쑰회
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        String username = auth.getName();

        return userService.getProfile(username);
    }
}

μ‚¬μš©ν•΄μ•Ό ν•˜λŠ” 이유

βœ… 1. ν‘œμ€€ν™”λœ λ³΄μ•ˆ

κ²€μ¦λ˜κ³  ν‘œμ€€ν™”λœ λ°©μ‹μœΌλ‘œ λ³΄μ•ˆμ„ μ μš©ν•˜μ—¬ 개발자의 μ‹€μˆ˜λ‘œ μΈν•œ λ³΄μ•ˆ ν—ˆμ μ„ μ΅œμ†Œν™”ν•©λ‹ˆλ‹€.

βœ… 2. κ΄€μ‹¬μ‚¬μ˜ 뢄리 (Separation of Concerns)

λ³΄μ•ˆ 둜직이 λΉ„μ¦ˆλ‹ˆμŠ€ 둜직(컨트둀러, μ„œλΉ„μŠ€)κ³Ό μ™„λ²½ν•˜κ²Œ λΆ„λ¦¬λ©λ‹ˆλ‹€.

// ❌ Spring Security 없이
@GetMapping("/admin/users")
public List<User> getUsers(HttpSession session) {
    // λ³΄μ•ˆ 둜직이 λΉ„μ¦ˆλ‹ˆμŠ€ 둜직과 μ„žμž„
    if (session.getAttribute("user") == null) {
        throw new UnauthorizedException();
    }
    User user = (User) session.getAttribute("user");
    if (!"ADMIN".equals(user.getRole())) {
        throw new ForbiddenException();
    }
    return userService.getAllUsers();
}

// βœ… Spring Security μ‚¬μš©
@GetMapping("/admin/users")
@PreAuthorize("hasRole('ADMIN')")
public List<User> getUsers() {
    return userService.getAllUsers(); // λΉ„μ¦ˆλ‹ˆμŠ€ 둜직만 집쀑
}

βœ… 3. κ°•λ ₯ν•œ λ°©μ–΄ κΈ°λŠ₯

μ›Ήμ˜ 고전적인 λ³΄μ•ˆ 곡격을 기본적으둜 λ°©μ–΄ν•©λ‹ˆλ‹€.

  • CSRF (Cross-Site Request Forgery) λ°©μ–΄
  • μ„Έμ…˜ κ³ μ • (Session Fixation) 곡격 λ°©μ–΄
  • ν΄λ¦­μž¬ν‚Ή (Clickjacking) λ°©μ–΄
  • XSS (Cross-Site Scripting) 보호 헀더 μžλ™ μ„€μ •

βœ… 4. 높은 ν™•μž₯μ„±

ν•„ν„°λ₯Ό 직접 μΆ”κ°€ν•˜κ±°λ‚˜ 인증 방식을 μ»€μŠ€ν„°λ§ˆμ΄μ§•ν•˜λŠ” 것이 맀우 μœ μ—°ν•©λ‹ˆλ‹€.

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            // μ»€μŠ€ν…€ ν•„ν„° μΆ”κ°€
            .addFilterBefore(jwtAuthenticationFilter(),
                           UsernamePasswordAuthenticationFilter.class)

            // OAuth2 둜그인 μ„€μ •
            .oauth2Login(oauth2 -> oauth2
                .userInfoEndpoint(userInfo -> userInfo
                    .userService(customOAuth2UserService)
                )
            )

            // JWT 인증 μ„€μ •
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            );

        return http.build();
    }
}

μš”μ•½

🎯 Spring Securityλž€?

Spring SecurityλŠ” Spring μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λ³΄μ•ˆμ˜ 사싀상 ν‘œμ€€μ΄λ©°, 인증/인가λ₯Ό μœ„ν•œ ν•„μˆ˜ 인프라λ₯Ό μ œκ³΅ν•˜λŠ” ν”„λ ˆμž„μ›Œν¬μž…λ‹ˆλ‹€.

πŸ“‹ 핡심 포인트

ꡬ뢄 μ„€λͺ…
핡심 κΈ°λŠ₯ 인증(Authentication) + 인가(Authorization)
λ™μž‘ 방식 μ„œλΈ”λ¦Ώ ν•„ν„° 체인을 ν†΅ν•œ μš”μ²­ κ°€λ‘œμ±„κΈ°
μ£Όμš” μž₯점 ν‘œμ€€ν™”, 관심사 뢄리, κ°•λ ₯ν•œ λ°©μ–΄, 높은 ν™•μž₯μ„±
적용 λ²”μœ„ URL 기반, λ©”μ†Œλ“œ 기반 λ³΄μ•ˆ λͺ¨λ‘ 지원

πŸš€ λ‹€μŒ ν•™μŠ΅ 주제

  • Spring Security Configuration 심화
  • JWT 인증 κ΅¬ν˜„
  • OAuth2 / OpenID Connect
  • Method Security (@PreAuthorize, @Secured)
  • Custom Filter κ΅¬ν˜„
  • Password Encoding (BCrypt)
  • Remember-Me 인증

μ°Έκ³ : 이 λ¬Έμ„œλŠ” Spring Security의 κΈ°λ³Έ κ°œλ…μ„ λ‹€λ£Ήλ‹ˆλ‹€. μ‹€μ œ ν”„λ‘œμ νŠΈ 적용 μ‹œμ—λŠ” λ³΄μ•ˆ μš”κ΅¬μ‚¬ν•­μ— 맞게 μ„ΈλΆ€ 섀정을 μ‘°μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€.