[Spring] Spring Security

1. 소개

Spring Security란?

Spring 기반 애플리케이션의 보안을 담당하는 프레임워크

인증(Authentication)과 인가(Authorization)를 중심으로 애플리케이션을 보호하는 역할을 한다.

한마디로 인증, 권한 관리 그리고 데이터 보호 기능을 포함하여 웹 개발 과정에서 필수적인 사용자 관리 기능 ex) 로그인 등을 구현하는데 도움을 주는 프레임워크

 

주요기능

1. 인증 (Authentication)

사용자가 누구인지 확인하는 과정으로, 일반적으로 로그인 과정을 의미한다.

  • 사용자 정보 확인: ID/PW, OAuth2, JWT, LDAP, SAML 등 다양한 인증 방식을 지원
  • 인증 관리자(AuthenticationManager): 사용자의 인증 정보를 검증하고 인증 여부를 결정하는 핵심 컴포넌트
  • UserDetailService: 사용자 정보를 데이터베이스나 메모리에서 가져오는 역할

 

2. 인가 (Authorization)

인증된 사용자가 특정 리소스에 접근할 수 있는 권한을 확인하는 과정을 의미한다.

  • URL 기반 권한 부여: 특정 URL에 접근할 수 있는 권한을 설정
  • 메서드 기반 권한 부여: @PreAuthorize, @Secured 등을 이용해 메서드 수준에서 접근 제어
  • RBAC (Role-Based Access Control): 역할 단위로 권한을 관리

 

3. 보안 필터

Spring Security는 여러 개의 필터 체인을 통해 요청을 검사합니다.

  • UsernamePasswordAuthenticationFilter: 기본적인 로그인 처리
  • JwtAuthenticationFilter: JWT  토큰을 검증하는 필터
  • CsrfFilter: CSRF 공격 방지

 

2.  Security의 작동 흐름

 

  1. 사용자가 로그인 요청(http request)을 보냄
  2. SecurityFilterChain 에서 요청을 가로채고 인증을 진행
  3. AuthenticationManager가 등록된 Authotication Provider를 조회하며 인증 검사
  4. Authotication Provider가 실제 데이터를 조회하여 UserDetails 결과를 돌려줌
  5. 인증이 성공하면 SecurityContextHolder에 인증 정보를 저장
  6. 권한이 있으면 요청을 허용, 없으면 AccessDeniedException을 발생시킨다.

 

출처 https://www.elancer.co.kr/blog/detail/235

 

 

3.  Spring Security 설정

Spring Boot 프로젝트에서 Security 설정하는 법

 

의존성 추가 - gradle 사용

implementation 'org.springframework.boot:spring-boot-starter-security'

 

 

추가하고 프로젝트를 실행하고 접속해보면 원래 들어가지던 페이지에 들어가지지 않고 아래같은 화면이 나온다

 

 

서버 로그를 살펴보면 패스워드가 하나 찍혀있다.

 

아이디는 user, 발급받은 패스워드를 사용해 로그인을 하면 로그인이 된다.

그럼 페이지에 접속이 잘 된다.

 

이렇게 Spring Security는 웹사이트 보안에 가장 기본적인 기능인 아이디/패스워드 인증을 화면까지 지원한다.

 

그런데 우리는 이걸 그대로 쓸게 아니니까 상속받아서 필요한 메서드들을 오버라이딩해 입맛에 맞게 쓰면 될 것이다.

그럼 어떻게 사용할까?

 

1. 기본설정 (SecurityConfig.java)

@EnableWebSecurity 어노테이션으로 간편하게 이용가능하다

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(AbstractHttpConfigurer::disable) // CSRF 보호 비활성화 (테스트용)
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
                .anyRequest().authenticated()
            )
            .formLogin(Customizer.withDefaults()) // 기본 로그인 폼 사용
            .logout(logout -> logout.logoutUrl("/logout").logoutSuccessUrl("/"));
        return http.build();
    }
}

 

 

  • /admin/** → ADMIN 권한 필요
  • /user/** → USER 또는 ADMIN 권한 필요
  • 나머지 모든 요청은 인증된 사용자만 접근 가능
  • 기본 로그인 폼 활성화 (/login 경로 자동 생성)

 

2. 사용자 인증 설정 (UserDetailsService)

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 실제 DB에서 사용자 정보 조회 (예제에서는 하드코딩)
        if ("admin".equals(username)) {
            return User.withUsername("admin")
                    .password("{noop}password") // {noop} → 암호화 안 함
                    .roles("ADMIN")
                    .build();
        } else if ("user".equals(username)) {
            return User.withUsername("user")
                    .password("{noop}password")
                    .roles("USER")
                    .build();
        }
        throw new UsernameNotFoundException("User not found");
    }
}

 

 

  • loadUserByUsername() 메서드는 DB에서 사용자 정보를 조회
  • {noop}는 비밀번호 암호화 없이 사용하겠다는 의미 (실제 서비스에서는 BCrypt 사용 권장)

3. JWT 기반 인증 설정

JWT(Json Web Token)를 활용하면 세션 없이 인증을 유지할 수 있음

 

JWT 토큰 필터 구현

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        String token = resolveToken(request);
        
        if (token != null && validateToken(token)) {
            Authentication auth = getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(auth);
        }
        
        chain.doFilter(request, response);
    }

    private String resolveToken(HttpServletRequest request) {
        return request.getHeader("Authorization");
    }

    private boolean validateToken(String token) {
        // JWT 검증 로직 (예제에서는 단순 true 반환)
        return true;
    }

    private Authentication getAuthentication(String token) {
        UserDetails userDetails = new User("user", "", List.of(new SimpleGrantedAuthority("ROLE_USER")));
        return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
    }
}

 

 

 

 

  • 요청에서 Authorization 헤더에서 JWT 토큰을 추출
  • 토큰이 유효하면 SecurityContextHolder에 인증 정보 저장
  • 이후의 요청은 세션 없이 JWT 토큰으로 인증 유지

 

JWT 필터 등록

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .csrf(AbstractHttpConfigurer::disable)
        .authorizeHttpRequests(auth -> auth
            .anyRequest().authenticated()
        )
        .addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    return http.build();
}

 

  • JwtAuthenticationFilter를 UsernamePasswordAuthenticationFilter 앞에 등록하여 JWT 인증 적용

 

4. 정리

 

Spring Security는 인증 & 권한 부여를 담당하는 강력한 보안 프레임워크

기본 로그인 기능을 쉽게 설정할 수 있고, 필요에 따라 커스텀 가능

JWT, OAuth2, LDAP 등 다양한 인증 방식을 지원

보안 필터 체인을 활용하여 유연한 보안 정책 적용 가능

 

인증, 인가와 관련된 부분을 시간을 많이 들이지 않고 깔끔하게 개발하는 걸 도와주는 프레임워크!

'Spring' 카테고리의 다른 글

[Spring] Redis를 이용한 캐싱 기법  (0) 2025.02.08
[Spring] Spring Boot Starter 이해하기  (0) 2025.02.08
[Spring] N + 1 문제  (0) 2025.02.06
[Spring] 관계 매핑 어노테이션  (2) 2025.02.06
[Spring] 스프링 부트 구조 정리  (0) 2025.02.04