자바 백엔드 입문 37편 — Spring Security 기초

2026-05-17자바 백엔드 입문

자바 백엔드 입문 37편. Spring Security가 무엇이고 SecurityFilterChain·인증·인가·JWT 기초 패턴을 회사 출입증 시스템 비유로 풀어쓴 학습 노트.

📚 자바 백엔드 입문 · 37편 — Spring Security 기초

이 글은 자바 백엔드 입문 시리즈 59편 중 37편이에요. 백엔드의 가장 큰 영역 — Spring Security를 입문 수준에서 풀어 가요. 이 한 글로 다 다루긴 어렵지만, "전체 그림" 잡는 게 목표.

Spring Security가 어렵게 들리는 이유

처음 Spring Security 의존성 추가하면 — 모든 API가 갑자기 401. 무엇이 어디서 막혔는지 안 잡혀요. 또 SecurityFilterChain·UserDetailsService·AuthenticationManager·PasswordEncoder 같은 단어가 한꺼번에.

이 글에서는 회사 출입증 시스템 비유로 풀어요. Spring Security = "사옥 정문 + 출입카드 발급 + 부서별 출입 통제 시스템". 끝까지 따라오시면 입문자 수준에서 전체 그림이 잡혀요.

인증 vs 인가 — 두 개념 분리

개념 영어 한 줄
인증 (Authentication) 누구인지 확인 로그인 — "당신은 alice인가?"
인가 (Authorization) 무엇을 할 수 있는지 확인 권한 — "alice가 관리자 페이지 들어올 수 있나?"

회사 사옥 비유 — 인증 = 출입증 발급("당신 신원 확인 완료"), 인인 = 부서별 출입 통제("개발자라 회의실 OK, 임원실 X"). 둘은 다른 단계.

Spring Security 의존성

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-security'
}

이 한 줄 추가하면 — 모든 API가 자동 보호. 기본 ID·비밀번호로만 접근 가능 (콘솔에 임시 비밀번호 출력).

"기본 동작" 을 우리 정책에 맞게 커스터마이즈하는 게 Spring Security 사용의 핵심.

SecurityFilterChain — 핵심 부품

Spring Security는 26편 Filter 의 연쇄로 동작. 십수 개의 Filter가 차례로 요청을 검사해요.

[요청]
  ↓
[CsrfFilter]
  ↓
[CorsFilter]
  ↓
[UsernamePasswordAuthenticationFilter]    ← 로그인 처리
  ↓
[AuthorizationFilter]                       ← 권한 검사
  ↓
[DispatcherServlet]
  ↓
[컨트롤러]

이 필터들의 체인 설정SecurityFilterChain Bean.

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())                          // CSRF 비활성 (REST API)
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/public/**").permitAll()     // 공개 경로
                .requestMatchers("/api/admin/**").hasRole("ADMIN") // 관리자만
                .anyRequest().authenticated())                       // 나머지 인증 필요
            .formLogin(form -> form.disable())                      // form 로그인 끄기
            .httpBasic(basic -> basic.disable())                    // basic 인증 끄기
            .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

        return http.build();
    }
}

REST API에 자주 박는 표준 골격. CSRF·form·basic 끄고 STATELESS(세션 안 만듦) — JWT 같은 토큰 인증 가정.

권한 검사 — @PreAuthorize

컨트롤러 메서드 단위 권한 검사. 22편 SpEL 표현식 활용.

@EnableMethodSecurity                              // 메인 클래스 또는 SecurityConfig
public class SecurityConfig { ... }

@RestController
public class OrderController {

    @GetMapping("/orders/{id}")
    @PreAuthorize("hasRole('USER')")              // USER 권한 필요
    public Order get(@PathVariable Long id) { ... }

    @DeleteMapping("/orders/{id}")
    @PreAuthorize("hasRole('ADMIN')")             // ADMIN만
    public void delete(@PathVariable Long id) { ... }

    @PatchMapping("/orders/{id}")
    @PreAuthorize("hasRole('USER') and #userId == authentication.principal.id")
    public Order update(@PathVariable Long id, ...) { ... }
}

@PreAuthorize23편 AOP 기반. 메서드 호출 직전 권한 검사.

PasswordEncoder — 비밀번호 해싱

비밀번호는 절대 평문 저장 X. Spring Security 표준 = BCrypt.

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

@Service
@RequiredArgsConstructor
public class UserService {
    private final PasswordEncoder encoder;

    public User signup(String email, String rawPassword) {
        String hashed = encoder.encode(rawPassword);    // BCrypt 해싱
        return userRepo.save(new User(email, hashed));
    }

    public boolean checkPassword(User user, String rawPassword) {
        return encoder.matches(rawPassword, user.getPassword());
    }
}

BCrypt는 매번 다른 salt 생성 — 같은 비밀번호도 매번 다른 해시. 현대 표준.

JWT — Stateless 토큰 인증

세션 대신 JWT(JSON Web Token) 사용이 REST API 표준.

[로그인 흐름]
1. POST /login {email, password}
2. 서버: 비밀번호 검증 → JWT 생성 → 응답
3. 클라이언트: JWT를 localStorage·쿠키 저장

[이후 API 호출]
4. GET /api/orders, Header: Authorization: Bearer <JWT>
5. 서버: JWT 검증 → 사용자 추출 → API 처리

JWT 구조 — header.payload.signature 세 부분. payload에 사용자 ID·만료시간 등 박힘. 서명으로 위변조 차단.

JWT 처리는 — 별도 Filter 로 구현. SecurityFilterChain 안에 끼워 넣음. 라이브러리는 jjwt (io.jsonwebtoken:jjwt-api) 표준.

@Component
public class JwtFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) {
        String token = req.getHeader("Authorization");
        if (token != null && token.startsWith("Bearer ")) {
            try {
                Claims claims = jwtParser.parseClaimsJws(token.substring(7)).getBody();
                String email = claims.getSubject();
                // SecurityContext에 인증 정보 박기
                UserDetails user = userDetailsService.loadUserByUsername(email);
                Authentication auth = new UsernamePasswordAuthenticationToken(
                        user, null, user.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(auth);
            } catch (Exception ignore) { /* 토큰 무효 — 인증 안 됨 */ }
        }
        chain.doFilter(req, res);
    }
}

SecurityConfig에 박기.

http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);

SecurityContextHolder — 현재 사용자

인증 끝나면 — SecurityContextHolder 에 현재 사용자 박힘. 어디서나 꺼내 쓸 수 있어요.

Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String email = auth.getName();
Collection<? extends GrantedAuthority> roles = auth.getAuthorities();

30편 ArgumentResolver@LoginUser 또는 Spring Security 내장 @AuthenticationPrincipal 로 자동 주입 가능.

입문자가 알아야 할 것 — 큰 그림만

Spring Security는 매우 깊은 주제예요. 입문에서 잡아야 할 것:

  1. 의존성 추가 = 자동 보호 활성
  2. SecurityFilterChain Bean으로 정책 박기
  3. 인증 vs 인가 구분
  4. PasswordEncoder 표준 = BCrypt
  5. REST API 표준 = JWT + Stateless
  6. @PreAuthorize 메서드 단위 권한
  7. SecurityContextHolder 로 현재 사용자

깊은 학습은 별도 "Spring Security 시리즈" 로 따로. 이 시리즈에서는 "있다" 정도.

🎯 다음 단계 — OAuth2·소셜 로그인

자체 인증 + JWT 다음 단계 = 카카오·구글·네이버 소셜 로그인. Spring Security OAuth2 Client 로 표준 처리. 회사 시스템 거의 표준이지만 이 시리즈 범위 밖.

한 줄 정리 — Spring Security = SecurityFilterChain 기반 인증·인가. REST API 표준 = JWT + Stateless. 비밀번호 = BCrypt 해싱. 권한 = @PreAuthorize 메서드 단위.

시험 직전 한 번 더 — Spring Security 입문자가 매번 헷갈리는 것

  • 인증(Authentication) = 누구인지 확인 (로그인)
  • 인가(Authorization) = 무엇을 할 수 있는지 (권한)
  • spring-boot-starter-security 추가 = 모든 API 자동 보호
  • SecurityFilterChain Bean = 정책 박는 핵심
  • @EnableWebSecurity = Spring Security 활성
  • REST API 표준 = csrf().disable() + STATELESS
  • authorizeHttpRequests = 경로별 권한 설정
  • permitAll() / authenticated() / hasRole("ADMIN")
  • @PreAuthorize = 메서드 단위 권한 (AOP 기반)
  • @EnableMethodSecurity 필요
  • PasswordEncoder = 비밀번호 해싱 표준
  • 표준 = BCryptPasswordEncoder
  • encoder.encode(password) = 해싱, encoder.matches(raw, hashed) = 검증
  • 비밀번호 평문 저장 절대 X
  • JWT = REST API 표준 토큰 인증
  • 구조 = header.payload.signature
  • Authorization: Bearer <JWT> 헤더로 전송
  • 라이브러리 = jjwt
  • SecurityContextHolder = 현재 사용자 저장소
  • OncePerRequestFilter = JWT 검증 Filter 패턴
  • @AuthenticationPrincipal = 현재 사용자 자동 주입
  • 소셜 로그인 = Spring Security OAuth2 Client
  • 깊은 학습은 별도 시리즈 필요 — 이 글은 큰 그림만

시리즈 다른 편 (앞뒤 글 모음)

이전 글:

다음 글:

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!