본문 바로가기
public void static main/Java

[Spring Security] Spring Security에 대해서 알아보자!

by 햄리뮤 2025. 1. 13.
반응형

프로젝트가 시작되었따... 빠르게 알고가야하는 개념에 대해서 정리하면서 공부해보자!
구조로 보면 더 이해가 쉬워서 다양한 spring security 구조 자료를 가져왔다!

Spring Security

Spring Framework 기반 애플리케이션에 인증(Authentication)과 권한 부여(Authorization)를 처리하기 위한 보안 프레임 워크이다!

구성 요소

https://assu10.github.io/dev/2023/11/12/springsecurity-basic-2/

  1. Authentication (인증)
    • 사용자가 누구인지 확인하는 과정을 거친다.
    • 사용자 이름과 비밀번호, 토큰 등을 사용한다.
  2. Authorization (권한 부여)
    • 인증된 사용자가 어떤 작업을 수행할 권한이 있는지 확인한다.
    • URL, 메서드, 리소스 접근 제어한다.
  3. Filter Chain
    • 보안 로직은 일련의 필터 체인으로 작동 한다.
    • 요청이 들어오면 필터 체인을 거치면서 인증과 권한 검사를 수행한다.
  4. Security Context
    • 인증된 사용자의 정보를 저장하는 컨테이너 역할을 한다.
    • 애플리케이션 전반에서 인증 정보를 쉽게 사용할 수 있도록 제공한다.
  5. UserDetailsService
    • 사용자 정보를 가져오는 인터페이스이다.
    • 커스텀 구현으로 데이터베이스 연동이 가능하다.
  6. SecurityBuilder
    • 빌더 클래스로서 웹 보안을 구성하는 빈 객체와 설정 클래스들을 생성하는 역할을 하며 WebSecurity, HttpSecurity가 있다. 
    • SecurityConfiguer를 포함하고 있다.
  7. SecurityConfiguer
    • Http 요청과 관련된 보안 처리를 담당하는 필터들을 생성하고 여러 초기화 설정에 관여한다.
    • 인증 및 인가 초기화 작업은 SecurityConfigurer에 의해 진행된다.

내부 동작 과정

https://medium.com/@greekykhs/springsecurity-part-3-spring-security-flow-7da9cc3624ab
https://riteshpanigrahi.com/spring-security-architecture-and-internal-workflow

  1. 클라이언트 요청
    • 사용자가 URL 또는 API에 요청을 보낸다.
  2. Filter Chain 작동
    • Spring Security의 필터 체인이 요청을 가로채 처리.
    • UsernamePasswordAuthenticationFilter 등 다양한 필터가 있다.
    • 요청의 인증 정보를 파싱하고 처리 한다.
  3. AuthenticationManager
    • 요청을 AuthenticationManager에 전달한다.
    • 인증 정보를 검증하고 결과를 반환 한다.
  4. UserDetailsService와 PasswordEncoder
    • UserDetailsService는 데이터베이스에서 사용자 정보를 로드 한다.
    • 비밀번호 검증 시 PasswordEncoder로 암호화된 비밀번호를 비교 한다.
  5. Security Context에 저장
    • 인증 성공 시 SecurityContext에 인증 정보를 저장 한다. (이정보는 애플리케이션 전반에서 참조 가능하다)
  6. 요청 처리 후 응답
    • 필터 체인을 통과한 요청은 컨트롤러에 도달하고 컨트롤러에서 비즈니스 로직 처리 후 응답을 반환 한다.

Spring Security 기본 설정

Security Configuration 클래스

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .antMatchers("/public/**").permitAll() // 공용 리소스
            .anyRequest().authenticated()        // 인증 필요
            .and()
            .formLogin();                        // 기본 로그인 페이지
    }
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            // 1. 선언적인 보안 설정
            .authorizeHttpRequests(authorize -> authorize
                .requestMatchers("/public/**").permitAll()
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            // 2. 다양한 보안 기능 기본 제공
            .formLogin(form -> form
                .loginPage("/login")
                .permitAll()
            )
            // 3. CSRF 보호 자동 활성화
            .csrf(csrf -> csrf.enable());
        
        return http.build();
    }
}

UserDetailsService 구현

@Service
public class CustomUserDetailsService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 예: 사용자 정보 DB 조회
        return User.builder()
            .username("user")
            .password(new BCryptPasswordEncoder().encode("password"))
            .roles("USER")
            .build();
    }
}

 


Spring Security를 사용하는게 뭐가 좋을까?

Spring Secufity를 사용하지 않은 경우

@RestController
public class UserController {
    @PostMapping("/login")
    public String login(HttpSession session, String username, String password) {
        // 1. 직접 비밀번호를 평문으로 저장하거나, 암호화 방식을 직접 구현해야 함
        User user = userRepository.findByUsername(username);
        if (user != null && password.equals(user.getPassword())) {
            // 2. 세션에 직접 사용자 정보를 저장
            session.setAttribute("user", user);
            return "로그인 성공";
        }
        return "로그인 실패";
    }

    @GetMapping("/admin")
    public String adminPage(HttpSession session) {
        // 3. 매 요청마다 권한 체크를 직접 구현
        User user = (User) session.getAttribute("user");
        if (user != null && user.hasRole("ADMIN")) {
            return "관리자 페이지";
        }
        return "접근 거부";
    }
}
  1. 보안 취약점 발생 위험
    1. SQL 인젝션에 취약할 수 있다.
    2. 세션 하이재킹에 취약할 수 있다.
    3. CSRF(Cross-Site Request Forgery) 공격에 노출될 수 있다.
    4. XSS(Cross-Site Scripting) 공격에 취약할 수 있다.
  2. 개발 생산성 저하
    1. 모든 보안 관련 로직을 직접 구현해야 한다.
    2. 각 컨트롤러마다 권한 체크 코드가 중복된다.
    3. 비밀번호 암호화, 인증 토큰 관리 등을 직접 구현해야한다.

Spring Security... 좀 더 알고싶어!

아쉬운 부분을 좀더 채워보았다!

URL 패턴(antMatchers 등)

  • Spring Security에서는 URL 경로에 따라 권한을 설정할 수 있다.
  • 특정 URL은 누구나 접근 가능하게 하지만, 다른 URL은 인증된 사용자만 접근 가능하게 하고싶을 때 사용한다.

antMatchers란?

  • antMatchers는 URL 패턴을 정의하는 데 사용된다.
  • 특정 URL 경로에 대해 허용 여부나 권한 조건을 설정한다.
  • 경로 패턴은 Ant-style 패턴을 사용한다.
    • * : 현재 경로의 모든 항목과 매칭 (한 단계)
    • ** : 하위 모든 경로와 매칭 (여러 단계)
@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
        .antMatchers("/public/**").permitAll()   // "/public/" 이하 모든 경로는 누구나 접근 가능
        .antMatchers("/admin/**").hasRole("ADMIN") // "/admin/" 이하 경로는 ADMIN 역할만 접근 가능
        .anyRequest().authenticated();           // 나머지 요청은 인증 필요
}

메서드 수준(@PreAuthorize, @PostAuthorize) 권한 설정

왜 메서드 수준 권한 설정이 필요할까?

  • URL 단위 권한 설정만으로는 모든 보안 요구를 충족하기 어렵다.
  • 특정 메서드(비즈니스 로직)에 대해 더 세부적으로 접근 권한을 제어할 수 있다.

@PreAuthorize와 @PostAuthorize 란?

  • @PreAuthorize: 메서드 호출 전에 조건을 확인하여 권한을 검사한다.
  • @PostAuthorize: 메서드 실행 후 반환값을 기반으로 권한을 검사한다.
@Service
public class AdminService {

    @PreAuthorize("hasRole('ADMIN')") 
    public void performAdminTask() {
        // 이 메서드는 ADMIN 역할을 가진 사용자만 호출 가능
        System.out.println("Admin task executed.");
    }

    @PostAuthorize("returnObject.owner == authentication.name")
    public MyResource getResource(Long id) {
        // 반환된 리소스의 소유자(owner)가 현재 사용자와 일치하는 경우만 접근 허용
        return new MyResource("exampleResource", "user123");
    }
}

CSRF란 무엇일까?

https://www.imperva.com/learn/application-security/csrf-cross-site-request-forgery/

CSRF(Cross-Site Request Forgery)는 웹 애플리케이션에서 발생할 수 있는 보안 취약점 중 하나이다.

공격자가 사용자를 속여 원치 않는 요청을 서버로 보내도록 유도하는 공격 방식이다.

CSRF 공격의 작동 방식

  1. 사용자가 신뢰할 수 있는 사이트에 로그인하고 세션을 유지한다.
  2. 공격자가 악의적인 웹사이트(또는 이메일)를 준비하고 사용자가 해당 웹사이트를 방문하도록 유도한다.
  3. 악의적인 웹사이트에서 사용자의 인증 정보를 활용해 신뢰할 수 있는 사이트로 비정상적인 요청을 보낸다.
  4. 서버는 요청이 사용자의 브라우저에서 온것이므로 이를 신뢰하고 처리하게 된다.

예제 시나리오

  1. 피해자는 은행 사이트에 로그인 중이다.
  2. 공격자가 피해자를 속여 악성 링크를 클릭하도록 유도한다.
  3. 브라우저는 로그인 상태를 유지하고 있으므로, 요청이 정상으로 보인다.
  4. 결과적으로, 피해자의 계좌에서 공격자 계좌로 돈이 이체된다.

Spring Security에서 CSRF 방어

Spring Security는 기본적으로 CSRF 보호를 활성화 합니다.

 

CSRF 활성화의 동작

  1. 서버는 CSRF 토큰을 생성하고 클라이언트에 전달한다.
  2. 클라이언트는 요청 시 이 토큰을 함께 서버로 보낸다.
  3. 서버는 요청에 포함된 토큰과 저장된 토큰을 비교해서 유효성을 확인한다.
  4. 토큰이 없거나 일치하지 않으면 요청을 거부한다.

하ㅏ하하ㅏㅏ.. Spring Security 알아야할께 더 있지만 프로젝트를 하면서 더 알아보자!

 

** 그냥 하루하루 개인 공부한 것을 끄적 거리는 공간입니다.

이곳 저곳에서 구글링한 것과 강의 들은 내용이 정리가 되었습니다.

그림들은 그림밑에 출처표시를 해놓았습니다.

문제가 될시 말씀해주시면 해당 부분은 삭제 하도록하겠습니다. **

반응형

댓글