16. Spring Security


<aside> 1️⃣ 스프링 시큐리티란

</aside>

<aside> 2️⃣ 주요 기능

</aside>

  1. 인증(Authentication): 인증은 사용자가 누구인지 확인하는 과정이다. Spring Security에서는 AuthenticationManager 인터페이스가 이 역할을 담당한다. **AuthenticationManager**는 다음과 같은 단일 메서드를 가지고 있다.

    Authentication authenticate(Authentication authentication) throws AuthenticationException;
    

    여기서 Authentication 객체는 Principal(주체, 보통 사용자 ID 또는 사용자명)과 Credentials(자격 증명, 보통 비밀번호)를 포함한다. **AuthenticationManager**는 여러 **AuthenticationProvider**를 사용하여 인증을 처리할 수 있다. 각 **AuthenticationProvider**는 특정 유형의 인증(예: LDAP, DAO, SSO 등)을 처리할 수 있다.

  1. 인가(Authorization): 인가는 인증된 사용자가 애플리케이션의 어떤 리소스에 접근할 수 있는지 결정하는 과정이다. 이는 **AccessDecisionManager**를 사용하여 처리된다. **AccessDecisionManager**는 주로 URL 기반의 인가 또는 메서드 기반의 인가를 처리하는데 사용된다. 예를 들어, 어떤 사용자가 특정 URL에 접근하려고 할 때 해당 사용자가 그 리소스에 접근할 권한이 있는지를 검사하거나, 특정 메서드를 호출하려고 할 때 해당 사용자가 그 메서드를 호출할 권한이 있는지를 검사한다.

    @PreAuthorize("hasRole('ROLE_ADMIN')")  // 이 메서드는 'ROLE_ADMIN' 역할을 가진 사용자만 호출할 수 있습니다.
    public void someMethod() {
        //...
    }
    
  2. 세션 관리: 세션 관리 기능은 사용자 세션을 안전하게 관리하는 데 도움이 된다. 이에는 세션 생성 정책, 세션 고정 공격 방지, 세션 만료 처리 등이 포함된다. 예를 들어, Spring Security는 사용자가 인증된 후에 기존 세션을 무효화하고 새 세션을 생성하도록 설정할 수 있다.

  3. CSRF (Cross-Site Request Forgery) 방지: CSRF는 사용자가 자신의 의지와 무관하게 공격자가 원하는 작업을 수행하게 하는 웹 애플리케이션의 취약점을 이용한 공격이다. Spring Security는 CSRF 토큰을 사용하여 이러한 종류의 공격을 방지한다. HTML 폼에 자동으로 CSRF 토큰을 추가하고, 폼 제출 시 이 토큰이 포함되어 있는지를 검증하여 공격을 방어한다.

    <form action="/someAction" method="post">
        <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
        <!--...-->
    </form>
    

<aside> 3️⃣ 스프링 시큐리티 사용방법

</aside>

  1. 먼저 **build.gradle**에 Spring Security 의존성을 추가한다.

    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-security'
    }
    
  2. 그 다음 **SecurityConfig**라는 이름의 Java 설정 클래스를 생성한다. 이 클래스에서는 HTTP 보안, 사용자 인증 정보 등을 설정할 수 있다.

    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    
    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
            auth
                .inMemoryAuthentication()
                .withUser("user").password("{noop}password").roles("USER")
                .and()
                .withUser("admin").password("{noop}password").roles("USER", "ADMIN");
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/anonymous*").anonymous()
                .antMatchers("/login*").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .defaultSuccessURL("/homepage.html", true)
                .failureURL("/login.html?error=true")
                .and()
                .logout()
                .logoutUrl("/perform_logout")
                .deleteCookies("JSESSIONID");
        }
    }
    

    위의 코드는 다음과 같은 보안 설정을 수행한다.

    // "/admin/**" 경로는 "ADMIN" 역할을 가진 사용자만 접근할 수 있습니다.
    .antMatchers("/admin/**").hasRole("ADMIN")
    
    // "/anonymous*" 경로는 익명 사용자만 접근할 수 있습니다.
    .antMatchers("/anonymous*").anonymous()
    
    // "/login*" 경로는 모든 사용자가 접근할 수 있습니다.
    .antMatchers("/login*").permitAll()
    
    // 로그인 페이지는 "/login.html"이며, 
    .loginPage("/login.html")
    // 로그인 성공 시 "/homepage.html"로 리다이렉트합니다.
    .defaultSuccessURL("/homepage.html", true)
    //로그인 실패 시 "/login.html?error=true"로 리다이렉트합니다.
    .failureURL("/login.html?error=true")
    
    // 로그아웃 URL은 "/perform_logout"이며
    .logoutUrl("/perform_logout")
    // 로그아웃 시 "JSESSIONID" 쿠키를 삭제합니다.
    .deleteCookies("JSESSIONID");
    

<aside> 3️⃣ 스프링 시큐리티가 지원하는 특수한 기능

</aside>

  1. OAuth 2.0 및 OpenID Connect 지원: 스프링 시큐리티는 OAuth 2.0 클라이언트와 리소스 서버를 지원한다. OpenID Connect를 지원하여 싱글 사인온(SSO) 기능을 구현할 수도 있다.
  2. JWT 토큰 지원: JSON Web Token(JWT)은 자가 포함된 방식으로 정보를 안전하게 전송하는 데 사용되는 열린 표준이다. 스프링 시큐리티는 JWT 토큰을 지원한다.
  3. CORS 지원: Cross-Origin Resource Sharing(CORS)는 브라우저가 자바스크립트를 사용하여 다른 도메인의 리소스에 접근할 수 있도록 허용하는 메커니즘이며, 스프링 시큐리티는 이를 지원한다.