728x90
반응형

[Spring Security] Spring Security FilterChain 동작 원리 완전 해부

 

[Spring Security] Spring Security FilterChain 동작 원리 완전 해부

1. Security Filter1. Security Filter 개요1. Security Filter 주요 역할일련의 서블릿 필터 체인을 자동으로 구성한다.일반적으로 웹 환경에서 브라우저가 서버에 요청을 보내게 되면 DispatcherServlet이 요청 받

crushed-taro.tistory.com

1. Security Filter

1. Security Filter 개요

1. Security Filter 주요 역할

  • DefaultLoginPageGeneratingFilter
    1. FormLogin() 메서드 호출 시 양식을 기반으로 인증을 활성화하는 필터로 로그인된 사용자의 url에 사용자 이름과 암호를 제출하는 로그인 양식으로 기본 로그인 페이지를 생성하게 되며 실패한 로그인 시도에 대한 사용자 정의할 수 있는 오류 메시지를 포함하는 기능을 제공하고 있다.
    2. 로그인 요청 시 기본 로그인 페이지 생성을 담당하는 필터이다.
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {

	http.formLogin().loginPage("/auth/login") // 해당 요청시 로그인 페이지로 이동됨
			.defaultSuccessUrl("/") // 로그인 성공시 해당 페이지로 이동함
			.failureUrl("/") // 로그인 실패시 해당 페이지로 이동됨
}

 

  • BasicAuthenticationFilter
    1. 기본 인증을 처리하는 Spring security에서 제공하는 필터로 기본 인증은 사용자 자격 증명(사용자 이름 및 암호)이 인코딩되어 각 요청에 HTTP 헤더로 전송되는 간단한 인증 메커니즘이다.
    2. 인증이 필요한 요청을 가로채고 기본 인증 체계로 Authorization 헤더가 있는지 확인하여 헤더가 있는 경우 필터는 인코딩된 자격 증명을 추출하여 사용자 인증을 시도한다.해당 필터는 base 64 인코딩 형식으로 자격 증명을 전송하기 때문에 HTTPS를 사용한 양식 기반 인증을 하는 것이 더욱 안전하다.
    3. 실제 인증은 AuthenticationProvider에서 담당하며 AuthenticationManger에서 구성이 되어 있다. 이러한 방식으로 BasicAuthenticationFilter를 구성하게 되면 SpringSecurity는 요청이 있을 때 Authorization 헤더에서 인코딩된 자격 증명을 추출하여 사용자 인증을 시도할 때 기본 인증 프로세스를 처리하게 된다.
    4. 해당 필터는 base 64 인코딩 형식으로 자격 증명을 전송하기 때문에 HTTPS를 사용한 양식 기반 인증을 하는 것이 더욱 안전하다.
public class BasicAuthenticationCustomFilter extends BasicAuthenticationFilter{
// 로직 작성
}

http.addFilter(new BasicAuthenticationCustomFilter()) // 필터 커스텀

 

  • RememberMeAuthenticationFilter
    1. 세션이 만료된 사용자에게 Remember-me 토큰을 기반으로 사용자를 자동으로 인증할 수 있도록 한다.
    2. 해당 필터를 구현하게 되면 RememberMeServices를 상속받아 구현해야 하며 토큰 유효성 검사 및 인증 설정을 해주어야 한다. PersistentTokenBasedRememberMeServices 구현이 사용되며 UserDetailService 구현이 필요하다.
    3. 해당 필터는 적절한 보안 조치가 필요하며 토큰은 애플리케이션의 보안을 보장하기 위해 안정하게 저장되고 적절하게 검증되어야 한다.
public class RememberMeAuthenticationCustomFilter extends RememberMeAuthenticationFilter {
// 로직 작성
}

http.addFilter(new RememberMeAuthenticationCustomFilter ()) // 필터 커스텀

 

  • SecurityContextHolderAwareRequestFilter
    1. HttpServletRequest 에 액세스하는 컨트롤러 또는 기타 필터와 같은 후속 구성 요소에서 보안 컨텍스트를 사용할 수 있으며 해당 구성 요소가 보안 컨텍스트에서 현재 사용자의 인증 및 권한 부여 정보를 검색할 수 있다.
    2. 애플리케이션이 활성화 되어 있을 때 Security에 의해 자동으로 추가된다.
    3. HttpServletRequest 에 SecurityContextHolder를 설정하는 필터로 현재 보안 컨텍스트를 인식하도록 보장하는 Spring Security에서 제공하는 필터이다. 텍스트를 인식하도록 보장하는 Spring Security 에서 제공하는 필터로 사용자 요청에 대한 보안 컨텍스트를 설정하여 후속 처리 구성 요소가 보안 정보에 액세스할 수 있도록 한다.
public class SecurityContextHolderAwareRequestCustomFilter extends SecurityContextHolderAwareRequestFilter{
// 로직 작성
}

http.addFilter(new SecurityContextHolderAwareRequestCustomFilter()) // 필터 커스텀

 

  • AnonymousAuthenticationFilter
    1. 익명 사용자에게 인증을 요구하지 않고 애플리케이션에 특정 부분을 액세스할 수 있도록 하는 filter이다.
    2. 익명 사용자의 인증 정보를 관리하는 객체는 AnonymousAuthenticationToke 구성되어 있으며 기본 설정으로 이름은 anonymous User 권한 및 역할로 Granted Authority 목록을 생성하여 ROLE_ANONYMOUS로 익명 사용자에게 할당된다.
    3. 인증 객체를 생성하여 다른 인증이 설정되지 않으면 SecurityContextHolder 에 설정하는 역할을 하게 된다.
public class AnonymousAuthenticationCustomFilter extends AnonymousAuthenticationFilter{
// 로직 작성
}

http.addFilter(new AnonymousAuthenticationCustomFilter ()) // 필터 커스텀

 

  • sessionManagementFilter
    • 사용자의 요청에 따라 세션을 효과적으로 관리하기 위해서 사용하는 필터이다.
    • 사용자의 세션을 관리하는 필터로 새로운 세션을 생성하거나 무효화, 고정 세션 공격 보호 등 세션의 동시성 제어를 포함한 세션 정책을 시행하게 된다.
public class sessionManagementCustomFilter extends sessionManagementFilter{
// 로직 작성
}

http.addFilter(new sessionManagementCustomFilter()) // 필터 커스텀


http..sessionManagement() // 세션 설정을 해주겠다는 메소드
                .maximumSessions(1) // 동일 세션으로 1명의 사용자만 로그인 허용
                .expiredUrl("/") // 이미 로그인이 되어 세션 생성에 제한이 되었을 경우 리다이렉트 할 주소
                .maxSessionsPreventsLogin(false); // boolean 값으로 false : 이전 사용자의 강제 로그아웃 true : 신규 사용자의 로그인 실패

 

  • ExceptionTranslationFilter
    1. 액세스가 거부될 때 인증 프로세스를 시작하는 역할을 하는 AuthenticatiojnEntryPoint의 인스턴스로 구성되어 있다. 인증 및 권한 부여 관련 예외를 포착하여 로그인 페이지로 리다이렉션을 하거나 오류를 반환하는 것과 같은 응답을 트리거하도록 되어 있다.
    2. 인증 및 권한 부여 프로세스 중에 발생하는 예외를 포착하여 이를 로그인 페이지로 리다이렉션 처리하거나 오류 응답을 반환하는 것과 같은 적절한 변환을 하는 역할을 하게 된다.
public class ExceptionTranslationCustomFilter extends ExceptionTranslationFilter{
// 로직 작성
}

http.addFilter(new ExceptionTranslationCustomFilter()) // 필터 커스텀

http.exceptionHandling()
    .accessDeniedPage("/access-denied") // Exception 발생시 이동할 페이지

 

  • FilterSecurityInterceptor
    1. 보안 규칙을 기반으로 액세스 제어를 수행하는 SpringSecurity에서 제공하는 키 필터이다. 권한 부여 규칙을 적용하고 사용자가 특정 리소스에 액세스하거나 특정 작업을 수행할 수 있는지를 결정하게 된다. FilterSecurityInterceptor는 SpringSerucity 필터 체인에 있으며 보호된 리소스에 도달하기 전에 요청을 가로채게 된다.
    2. 정의된 보안 구성에 대한 요청된 url 및 관련 HTTP 메서드를 평가하여 사용자가 진행할 수 있는 권한이 있는지 확인한다.
public class FilterSecurityCustomInterceptor extends FilterSecurityInterceptor{
// 로직 작성
}

http.addFilter(new FilterSecurityCustomInterceptor()) // 필터 커스텀

http.authorizeRequests()
     .antMatchers("/public").permitAll() // 해당 요청은 모두가 사용이 가능함
     .anyRequest().authenticated()
728x90
반응형
728x90
반응형

[Spring Security] Spring Security 내부 구조 총정리 | 필터 체인부터 인증 과정까지 완벽 분석 - 2

 

[Spring Security] Spring Security 내부 구조 총정리 | 필터 체인부터 인증 과정까지 완벽 분석 - 2

[Spring Security] Spring Security 내부 구조 총정리 | 필터 체인부터 인증 과정까지 완벽 분석 [Spring Security] Spring Security 내부 구조 총정리 | 필터 체인부터 인증 과정까지 완벽 분석[Spring Security] Spring Boot

crushed-taro.tistory.com

1. Security Filter

1. Security Filter 개요

1. Security Filter 주요 역할

  • 일련의 서블릿 필터 체인을 자동으로 구성한다.
  • 일반적으로 웹 환경에서 브라우저가 서버에 요청을 보내게 되면 DispatcherServlet이 요청 받기 이전에 많은 servlet filter를 거치게 된다.

Spring Security

 

  • SecurityContextPersistenceFilter
    1. 해당 필터는 Security에서 제공하는 중요한 필터로 요청 사이에 보안 컨텍스트를 유지하는 임무를 수행하게 된다. 인증된 사용자의 보안 컨텍스트가 여러 요청에서 유지되어 사용자의 인증 상태 및 부여된 권한을 유지하도록 한다.
    2. 애플리케이션에 요청이 들어오면 해당 요청을 가로채서 요청과 관련된 보안 컨텍스트가 있는지 확인하게 되며 존재하는 경우 요청 동안(session) SecurityContextHolder에 설정된다.
    3. 만약 비어있는 경우 새로운 보안 context를 생성한다.
http
            // ...
            .addFilterBefore(new SecurityContextPersistenceFilter(securityContextRepository), BasicAuthenticationFilter.class);
            // ...

 

  • LogutFilter
    1. 인증된 사용자의 로그아웃 프로세스를 처리하는 Spring Security 필터이다. 로그아웃 요청을 가로채고 세션 무효화, 인증 지우기, 지정된 로그아웃 성공 url로 리다이렉션과 같이 사용자를 로그아웃하는데 필요한 작업을 수행한다.
      1. 사용자 인증 취소 : contextHolder에서 사용자 인증을 삭제하여 사용자를 로그아웃
      2. 사용자 세션 무효화 : http 세션과 연결된 경우 logoutFilter는 해당 세션을 무효로 하여 사용자가 세션 내에서 더 이상 인증되지 않도록 할 수 있다.
      3. 로그아웃 관련 작업 수행 : LogoutFilter는 쿠키 지우기, Remember-me 토큰 제거 또는 사용자 정의 로그아웃 핸들러 호출과 같은 추가 작업을 실행할 수 있다.
http
            // ...
            .logout()
                .logoutUrl("/logout") // Configure the logout URL
                .logoutSuccessHandler(logoutSuccessHandler) // Specify the LogoutSuccessHandler implementation
                .and()
            // ...

// Add the LogoutFilter to the filter chain
        http.addFilterBefore(new LogoutFilter(logoutSuccessHandler, new SecurityContextLogoutHandler()), LogoutFilter.class);

 

  • UsernamePasswordAuthenticationFilter
    1. 사용자 이름과 암호를 포함하는 인증 요청 처리를 하기 위해 Spring security에서 제공하는 핵심 구성 요소이다. 요청을 가로채고 제공된 자격 증명을 사용해서 사용자의 인증을 시도하게 된다. 사용자 인증을 위해 AuthenticationManager에서 인증 절차를 진행하게 된다. 
public class UsernamePasswordAuthenticationCustomFilter extends UsernamePasswordAuthenticationFilter {
// 로직 작성
}

http.addFilter(new UsernamePasswordAuthenticationCustomFilter()) // 필터 커스텀
728x90
반응형
728x90
반응형

[Spring Security] Spring Security 내부 구조 총정리 | 필터 체인부터 인증 과정까지 완벽 분석

 

[Spring Security] Spring Security 내부 구조 총정리 | 필터 체인부터 인증 과정까지 완벽 분석

[Spring Security] Spring Boot 보안 설정 방법 Spring Security 핵심 개념과 설정 [Spring Security] Spring Boot 보안 설정 방법 Spring Security 핵심 개념과 설정1. Security 개요1. Security1. Security란?Spring Security는 스프링

crushed-taro.tistory.com

1. SpringSecurity architecture

1. Security architecture

1. Security 개념

  • Autorization & Authentication인가를 위해서 Principal을 아이디로, Credential을 비밀번호로 사용하는 Credential 기반의 인증 방식을 사용하게 된다.
    • Authentication (인증) : 해당 사용자가 본인이 맞는지를 확인하는 절차
    • Autorization (인가) : 인증된 사용자가 요청한 자원에 접근 가능한지를 결정하는 절차
    • 접근 주체 (Principal) : 보호받는 Resource에 접근하는 대상
    • 비밀번호 (Credential) : Resource에 접근하는 대상의 비밀번호
  • Spring security는 인증 절차를 거친 후에 인가 절차를 진행하게 되며 인가 과정에서 해당 리소스에 대한 접근 권한이 있는지를 확인하게 된다.
  • UsernamePasswordAuthenticationToken
    1. RememberMeAuthenticationToken
    2. JwtAuthenticationToken
    3. OAuth2AuthenticationToken
  • 해당 클래스는 Spring Security에서 제공하는 Authentication 인터페이스의 특정 구현이다. 사용자 이름과 암호를 자격 증명으로 포함하는 인증 요청을 나타낸다. 클래스는 사용자의 이름 및 암호 기반 인증을 수행할 때 사용자의 자격 증명을 캡슐화하는데 사용되며 일반적으로 인증 프로세스 중에 생성되며 인증을 위해 인증 관리자에게 전달된다. 해당 토큰은 인증을 위해 관리자에게 전달되며 일반적으로 사용자 데이터베이스 또는 기타 인증 메커니즘에 대해 자격 증명을 유효성 검사를 진행한다. 인증에 성공하면 인증된 Authentication 개체가 반환되며 이 개체는 securityContextHolder에서 설정하여 보안 컨텍스트 내에서 사용자의 인증을 설정할 수 있다. 이외로 다음과 같은 클래스를 활용할 수 있다.
  • AuthenticationMananger
  • 해당 인터페이스는 Authentication 객체 인증을 담당하는 Spring Security의 핵심 구성 요소로 사용자 자격 증명의 유효성을 검사하고 보안 컨텍스트 내에서 사용자의 id를 설정하는데 사용된다. AuthenticationManager 인터페이스의 기본 메소드는 authenticate(Authentication authentication)이다. 이 매서드는 Authentication 개체를 입력으로 사용하고 인증 프로세스를 수행하고 인증에 성공하면 Authentication 개체를 반환하고 인증에 실패하면 예외를 throw한다.
  • ProviderManagerProviderManager는 복합 인증 공급자 역할을 진행하게 되며 즉, AuthenticationProvider 인스턴스 목록을 보유하고 주어진 인증 요청에 대하여 적절한 인증 객체를 찾기 위해 반복한다. ProviderManager의 Authenticate 메서드가 호출되면 해당 목록의 각 AuthenticationProvider에 인증 요청을 순차적으로 위임하며 각 공급자는 인증 요청에 따라 특정 인증 논리를 수행한다. 공급자가 요청을 성공적으로 인증하면 인증된 Authentication 개체를 반환하여 공급자가 요청을 인증할 수 없는 경우 null을 반환하게 된다. AuthenticationException을 발생시킨다.
  • Spring Security의 AuthenticationManager 인터페이스 구현으로 인증 요청을 AuthenticationProvider 구현 체인에 위임하는 핵심 구성 요소이다.
  • UserDetailsService
  • 인증 프로세스 중에 사용자별 데이터를 로드하는 Spring Security의 핵심 구성 요소로 백엔드 데이터 소스에서 사용자 아이디, 암호 및 권한과 같은 사용자 세부 정보를 검색하는데 사용하게 된다. 인터페이스의 기본 메서드는 사용자 이름을 입력으로 사용하고 개체를 반환하는 UserDetailsService이다. 개체는 사용자의 세부 정보를 나타내며 인증 및 권한 부여 목적으로 사용된다. loadUserByUsername(String username)을 상속받아서 사용한다. 해당 예제에서 CustomUserDetailsService 클래스 인터페이스를 구현한다. userDetailsService 메서드 loaUserByUsername 일반적으로 데이터베이스 LDAP 서버 또는 기타 사용자 리포지토리와 같은 데이터 소스에서 사용자 세부 정보를 검색한다. userDetails 사용자가 발견되면 사용자의 세부 정보를 나타내는 개체를 만들고 반환한다. 인터페이스 UserDetails는 사용자 이름, 암호(인코딩됨) 및 GrantedAuthority 사용자의 권한이나 역할을 나타내는 객체 모음을 검색하기 위한 메서드를 제공한다.
  • UserDetails
    • 메서드
      1. getUsername() : 사용자의 이름을 반환
      2. getPassword() : 사용자의 비밀번호를 반환한다.
      3. getAuthorities() : 사용자와 관련된 권한 또는 역할 나타내는 GrantedAuthority 객체 모음이다.
      4. isEnabled() : 사용자 계정의 활성화 또는 비활성화 여부를 나타낸다.
      5. isAccountNonExpired() : 사용자의 계정이 만료되었는지 여부를 나타낸다.
      6. isAuccountNonLocked() : 사용자 계정이 잠겨 있는지 여부를 나타낸다.
      7. iscredentialsNonExpired() : 사용자의 자격 증명(암호) 만료 여부를 나타낸다.
  • 인터페이스는 핵심 사용자 정보를 나태는 객체로 사용자 이름, 암호, 권한 및 사용자의 관련 속성을 포함하여 사용자에 대한 세부 정보를 캡슐화 하여 관리한다. UserDetails 개체는 일반적으로 인증 중인 사용자를 나타내기 위해 인증 프로세스 중에 사용되며 userDetails 인터페이스에서 제공하는 몇 가지 중요한 특성 및 메서드이다.
  • SecurityContextHolder
    • SecurityContextHolder의 주요 역할
      1. 보안 컨텍스트 : SecurityContextHolder는 현재 사용자의 보안 관련 정보를 보유하는 SecurityContext 객체를 유지한다.
      2. 보안 컨텍스트 모드 : securityContextHolder는 보안 컨텍스트를 관리하기 위해 다양한 모드를 지원한다.
        • MODE_THREADLOCAL : 이 모드에서 보안 컨텍스트는 ‘ThreadLocal’을 사용하여 현재 스레드에 바인딩 된다. 각 스레드에는 자체 보안 컨텍스트가 있으며 다른 스레드에서 코드를 실행할때 컨텍스트가 자동으로 전파된다.
        • MODE_INHERITABLETHREADLOCAL : MODE_THERADLOCAL과 유사하지만 보안 컨텍스는 원래 스레드에서 생성된 자식 스레드에 의해 상속된다.
      3. 보안 컨텍스트 설정 : 정적 SecurityContextHolder.setContext(SecurityContext context) 메서드를 사용하여 보안 컨텍스트를 설정하는 것이 가능하다. 일반적으로 이는 수동 또는 AuthenticationManager 또는 UserDetailsService와 같은 Spring Security 구성 요소에 의해 성공적인 인증 후에 수행된다.
      4. 보안 컨텍스트 검색: 현재 보안 컨텍스트는 SecurityContext 개체를 반환하는 정적 SecurityContextHolder.getContext() 메서드를 사용하여 액세스하는 것이 가능하다. SecurityContext에서 권한과 함께 인증된 사용자를 나타내는 Authentiacation 객체를 얻는 것이 가능하다.
      5. 보안 컨텍스트 지우기 : 보안 컨텍스트를 정리하면 정적 SecurityContextHolder.cleacontext() 메서드를 호출학 되며 이것은 일반적인 로그아웃 중 혹은 보안 컨테스트가 더 이상 필요하지 않을 때 수행된다.
  • 애플리케이션 내에서 현재 사용자의 보안 컨텍스트에 대한 액세스를 제공하는 중앙 구성 요소이다. 인증된 사용자 세부 정보 및 부여된 권한과 같은 보안 관련 정보를 저장하고 액세스하는 컨테이너 역할을 하게 된다. Security 프레임워크의 일부이며 보안 컨텍스트와 상호 작용하는 정적 메서드를 제공한다.
728x90
반응형
728x90
반응형

[Spring Security] Spring Boot 보안 설정 방법 Spring Security 핵심 개념과 설정

 

[Spring Security] Spring Boot 보안 설정 방법 Spring Security 핵심 개념과 설정

1. Security 개요1. Security1. Security란?Spring Security는 스프링 기반 애플리케이션의 보안(인증과 권한, 인가 등)을 담당하는 스프링 프레임워크이다.Security는 인증과 권한에 대한 부분을 Filter의 흐름에

crushed-taro.tistory.com

1. Spring Security Architecture

1. Security Archtecture

1. Security Architecture

Spring Security

 

  • 미니멈 버전
    • 사용자의 로그인 요청 → 이를 AuthenticationFilter가 가로챔.
    • AuthenticationFilter에서 사용자의 아이디와 비밀번호를 기반으로 UserPasswordAuthentiactionToken을 발행한다.
    • 생성된 AuthenticationManager에 전달
    • AuthenticationManager 등록된 AuthenticationProvider에 사용자 정보조회 요청
    • UserDetailsService에서 사용자의 정보를 조회하고 UserDetails에 정보를 저장하여 반환함
    • 조회시 입력된 username을 기반으로 조회함
    • AuthenticationProvider에 조회된 사용자의 username, password를 비교
    • 일치하는 경우 AuthenticationManager에 객체를 반환함
    • 해당 인증 정보를 SecurityContextHolder에 저장함

  • 맥시멈 버전
    • 클라이언트의 form 기반 인증 요청을 AuthenticationFilter가 가로챈다.
      • 일반적으로 클라이언트의 요청은 Dispatcher Servlet에게 전달되기 전에 여러가지 Servlet Filter를 거친다.
      • Spring Security도 Serlvet Filter로서 동작하여 인증, 인가와 관련된 프로세스를 진행한다.
      • Spring Security에는 별도의 인증/인가 관련 서블릿 필터들을 엮어 놓은 필터 체인(SecurityFilterChian)이 존재한다.
      • 이 필터 체인들 중 인증을 담당하는 필터를 AuthenticationFilter라고 한다.
      • AuthenticationFilter는 사용자의 세션 ID(JSESSIONID)가 SecurityContext(인증된 사용자의 정보(인증 객체)를 저장하는 위치)에 존재하는지 확인한다. 존재하지 않는다면 다음 로직을 수행한다.
      • 사용자의 요청 종류(form submit, Basic, Digest 등)에 따라 해당 요청과 관련된 인증 필터를 만날 때 까지 필터 체인을 통과한다.
      • Form 기반의 submit 요청의 경우 UsernamePasswordAuthenticationFilter에 도달하게 된다.
    • 사용자 자격 증명을 기반으로 토큰 생성.
      • 인증 요청을 가로챈 AuthenticationFilter는 요청에서 사용자 이름(principal)과 비밀번호(credential)를 추출한다.
      • 추출한 사용자 자격 증명을 기반으로 인증 객체인 토큰을 생성한다.
      • 해당 방식은 UsernamePasswordAuthenticationToken 타입의 인증 객체를 생성한다.
    • 토큰을 AuthenticationManager에게 위임해 인증 메소드를 호출
      • AuthenticationManager는 단순 인터페이스에 불과하고, 실제 구현은 ProviderManager에 되어 있다.
      • 참고로 실질적인 인증/인가 작업은 대부분 ProviderManager가 한다.
    • ProviderManager는 AuthenticationProvider 리스트를 순회하며 인증을 시도함.
      • ProviderManager는 토큰을 순차적으로 AuthenticationProvider 리스트에 전달해 실제 인증 과정을 수행한다.
    • AuthenticationProvider는 토큰을 가지고 DB를 조회해 해당 사용자의 인증을 완료함.
      • 일부 AuthenticationProvider는 사용자 이름(principal)을 가지고 세부 정보를 검색하기 위해 UserDetailService를 사용할 수 있다.
      •  UserDetailService는 Spring Security가 제공하는 인터페이스로, 해당 로직은 개발자가 직접 작성해야 한다.
      • AuthenticationProvider는 인증 작업이 성공적으로 완료되면 완전히 채워진 인증 객체를 반환한다.
      • 그렇지 않으면 AuthenticationException 예외가 발생한다.
    • AuthenticationProvider는 인증이 완료된 인증 객체를 다시 AuthenticationFilter로 반환한다.
    • AuthenticationFilter는 인증이 완료된 인증 객체를 SecurityContext에 저장한다.
    • 추후 동일한 요청 시, 필터는 SecurityContext를 참조해 인증 객체의 존재 여부에 따라 인증 절차를 생략할 수도 있다.
728x90
반응형
728x90
반응형

1. Security 개요

1. Security

1. Security란?

  • Spring Security는 스프링 기반 애플리케이션의 보안(인증과 권한, 인가 등)을 담당하는 스프링 프레임워크이다.
  • Security는 인증과 권한에 대한 부분을 Filter의 흐름에 따라서 처리를 하게 되며 보안과 관련된 옵션을 체계적으로 제공해주기 때문에 개발자가 일일이 보안과 관련된 로직을 작성하지 않아도 되는 장점을 가지고 있다.
  • 용어 정의
    1. Principal : 접근 주체(= 아이디)
      1. 사용자, 디바이스, 시스템 등의 행위를 하는 주체를 의미
    2. Credential : 자격 증명(=비밀번호)
    3. Secured Resource : 보안이 적용되는 리소스
      1. 일반적으로 메서드를 통해 보호가 된다.
    4. Authentication : 인증
      1. Principal이 믿을 수 있는지 파악하는 것으로 일반적으로 id/password를 검사하게 된다.
    5. Authorization : 인가
      1. 인증이 완료된 principal이 어떤 행위를 할 권한이 있는지 확인한다.
      2. ROLE_ADMIN, ROLE_GUEST, ROLE_MEMBER의 role에 기반해서 접근이 가능하다.
    6. Authority, Role : 권한
      1. 리소스에 대한 접근 제한. 모든 리소스는 접근 제어 권한이 존재한다.

2. Security 기능

  1. 인증 및 접근 제어
  2. 사용자의 인증과 접근 제어를 처리하여 인증되지 않는 액세스를 방지한다. 사용자의 자격 증명을 확인하고 권한 부여 및 접근 권한 검사를 수행하여 인가된 사용자만 보호된 리소스에 액세스가 가능하다.
  3. CSRF(Cross-Site-Request Forgery) 방어
  4. csrf 공격을 방지하기 위해 요청에 csrf 토큰을 추가하고, 이를 사용하여 유효한 요청인지 검증을 하게된다. 이를 통해 악성 사용자의 요청 위조를 방지할 수 있다.
  5. XSS(Cross-Site Scripting) 방어 : XSS 공격을 방지하기 위해 출력 데이터의 이스케이프 처리를 제공하고 있으며 이를 통해 악성 스크립트가 삽입되는 것을 방지하고 사용자의 브라우저에서 안전하게 실행을 보장한다.
  6. 세션 관리 : Spring Security는 세션 관리를 통해 세션 하이재킹 및 세션 고정 공격을 방지한다. 적절한 세션 id 생성, 세션 유효성 검사, 세션 제한 시간 설정 등의 기능을 제공하여 세션의 안전성을 강화한다.
  7. 보안 헤더 설정 : Spring Security는 보안 헤더를 설정하여 다양한 보안 취약점을 방지한다. Content Security Policy (CSP), X-Content-Type-Options, X-XSS-Protection 등의 헤더를 설정하여 악성 스크립트 실행, mime 타입 스니핑을 방지한다.
  8. 로그인 실패 및 계정 잠금 : Spring Security는 로그인 실패 횟수 제한, 계정 잠금등의 기능을 제공하여 무차별 대입 공격 및 계정 탈취를 방지한다.
  9. 보안 취약점 패치 : 보안 취약점에 대한 업데이트와 패치를 지속적으로 제공하여 애플리케이션의 보안을 유지하고 개선한다. 취약점에 대한 경고 및 권장 사항을 제공하여 개발자가 적절한 조치를 취할 수 있도록 한다.

3. 웹 해킹 종류

  • CSRF (Cross-Site Request Forgery)
    • 공격 흐름
      1. 피해자가 악의적인 웹사이트 방문
      2. 악의적인 웹사이트는 피해자의 브라우저에 실제로 수행되어야 하는 요청에 위조하여 피해자 사이트로 전송한다.
      3. 피해자의 브라우저는 위조된 요청을 실행하고, 이때 피해자의 세션 인증 정보가 함께 전송된다.
      4. 서버는 피해자의 세션 인증 정보를 확인하여 해당 요청을 처리하게 되어 악의적인 동작을 실행한다.
    • Security의 CSRF 공격 방지를 위한 기능
      1. CSRF 토큰 : CSRF 토큰을 포함해 위조된 요청인지 확인한다. 서버는 요청을 처리하기 전에 토큰의 일치 여부를 확인하여, 일치하지 않는 경우 요청을 거부
      2. CSRF 헤더 : X-CSRF-Token 이라는 이름의 헤더를 요청에 추가하여 CSRF 공격을 방지한다. 클라이언트는 서버에서 받은 토큰을 헤더에 포함해 요청 시 전송하며 서버는 이를 확인하여 변조를 확인
      3. CSRF 보호 설정 : CSRF 보호 설정을 활성화할 수 있으며 이를 통해 기본적인 CSRF 보호 기능을 제공하여, 토큰 생성, 토큰 저장소, 토큰 전송 방식 등을 구성할 수 있다.
  • 악의적인 사용자가 피해자의 인증된 세션을 이용하여 비정상적인 요청을 실행하는 공격 형태이다. 공격자는 사용자가 이미 인증된 세션을 가지고 있을 때, 피해자가 의도하지 않은 동작을 수행하도록 요청을 위조한다.

 

  • XSS
    • Stored Xss
    • 저장형 xss 공격은 보안이 취약한 서버에 악의적인 사용자가 악성 스크립트를 저장함으로써 발생하며 비정상적인 방법이 아닌 서버에서 제공하는 게시판, 사용자 프로필에 악의적으로 동작하는 스크립트가 그대로 저장된 후 클라이언트의 브라우저로 전달되어 문제가 발생하게 된다.
    • Refiected Xss
    • 요청 메시지에 입력된 스크립트 코드가 즉시 응답 메시지를 통해 출력되는 취약점으로 입력된 스크립트가 반사되는 것처럼 동작하기 때문에 붙은 이름이다.
  • SQL INJECTION과 함께 웹상에서 가장 기초적인 취약점 공격 방법의 일종으로, 악의적인 사용자가 공격하려는 사이트에 스크립트를 넣는 기법을 말하며 공격에 성공하면 사이트에 접속한 사용자는 삽입된 코드를 실행하게 되어 의도하지 않은 행동을 수행하게 되거나 쿠키 및 세션 등 민감한 정보를 탈취당하게 된다. 크로스 사이트 스크립팅은 자바스크립트를 사용하여 공격하는 경우가 많으며 공격 방법이 단순하고 가장 기초적이나 많은 웹사이트가 xss에 대한 방어 조치를 하지 않아 공격받는 경우가 많으며 여러 사용자가 접근할 수 있는 게시판 등에 코드를 삽입하는 경우가 많으며 때에 따라서는 메일과 같은 매체를 통해서 전파된다. 공격 방법에 따라 명칭이 분류되며 다음과 같은 기법이 있다.

 

  • 세션 하이제킹
    • 공격 흐름
      • 사용자 세션 생성됨
      • 사용자가 웹 애플리케이션이나 서비스에 로그인하면 서버에 세션이 생성된다. 서버는 고유한 세션 식별자(세션 쿠키)를 클라이언트 측에 저장되는 사용자 세션에 할당하게 된다.
      • 세션 토큰 교환
      • 사용자의 브라우저와 서버 간의 후속 상호 작용 중에 클라이언트는 요청을 인증하고 서버에 올바른 세션과 연결하기 위해 http 요청에 세션 식별자를 쿠키에 포함한다.
      • 세션 가로채기
      • 공격자는 네트워크 트래픽 스니핑, 맬웨어 사용 또는 중간자 공격 실행과 같은 네트워크의 취약성을 악용하여 세션 식별자를 가로채게 된다.
      • 세션 하이재킹
      • 공격자가 세션 식별자를 획득하면 이를 사용하여 사용자 세션을 가장 할 수 있으며 세션 식별자를 직접 사용하거나 자신의 브라우저 또는 스크립트에 삽입하여 대상 세션에 대한 무단 액세스를 얻을 수 있다.
      • 하이재킹된 세션 악용
      • 공격자는 하이재킹된 세션을 제어하여 손상된 세션과 관련된 권한 및 기능에 따라 다양한 악의적인 작업을 수행할 수 있다. 여기에는 민감한 정보 도용, 무단 거래 수행 또는 사용자 계정 설정 조작이 포함됨
  • 세션 하이재킹 또는 세션 도용이라 불리는 공격은 공격자가 컴퓨터 네트워크에서 사용자의 세션을 장악하려고 시도하는 보안 공격이다. 이 공격에서 공격자는 인증 및 세션 관리에 사용되는 세션 토큰 또는 세션 식별자를 가로채고 조작하여 사용자 세션에 대한 무단 액세스 권한을 얻는 것을 목표로 한다.

 

  • 세션 고정공격
    • 공격 흐름
      • 초기 세션 식별자
      • 공격자는 세션 식별자 또는 토큰을 생성하고 사용자가 이를 인증에 사용하도록 속인다.
      • 사용자 인증
      • 사용자가 악성 링크를 클릭하여 웹 애플리케이션 또는 서비스에 액세스한다. 응용 프로그램은 공격자가 제공한 세션 식별자를 합법적인 세션으로 받아들인다.
      • 세션 하이재킹
      • 사용자가 인증되면 공격자는 고정된 세션 식별자를 악용하여 사용자 세션에 대한 무단 액세스 권한을 얻을 수 있다. 네트워크 트래픽을 가로채거나 XSS 취약점을 활용하는 등 다양한 방법을 사용하여 세션을 하이재킹할 수 있다.
      • 하이재킹된 세션 악용
      • 공격자는 세션을 제어하여 사용자를 대신해 악의적인 활동을 수행하게 됨
  • 공격자가 미리 결정된 세션 식별자 또는 세션 토큰을 사용하도록 사용자를 속이는 보안 공격 유형이다. 이 공격은 사용자가 공격자가 제어하는 세션으로 인증하도록 강제하여 사용자의 브라우저와 웹 응용 프로그램 간의 신뢰를 악용하는 것이다.
  • Content Security Policy (CSP)
  • 콘텐츠 보안 정책은 xss 및 데이터 주입과 같은 다양한 유형의 공격을 완화하고 보호하기 위한 웹 애플리케이션에서 구현하는 보안 메커니즘으로 csp는 웹 개발자가 웹 페이지를 로드하고 실행할 수 있는 콘텐츠를 지정하는 일련의 규칙 또는 정책을 정의하고 시행할 수 있도록 해주는 http 헤더이다.

 

  • X-Content-Type-Options
    1. "nosniff": 이 지시문이 "nosniff" 값이 있는 헤더에 포함된 경우(예: "X-Content-Type-Options: nosniff") 브라우저가 선언된 콘텐츠 유형을 엄격하게 준수하도록 지시합니다. 응답하고 콘텐츠 유형 스니핑을 수행하지 않습니다. 서버가 콘텐츠 유형을 지정하면 브라우저는 이를 신뢰할 수 있는 값으로 취급하고 콘텐츠 유형을 추측하려고 시도하지 않습니다.
    2. "none": 이 지시문은 더 이상 사용되지 않으며 사용해서는 안 됩니다. 이전에는 MIME 스니핑을 완전히 비활성화하는 데 사용되었습니다. 그러나 이제 동일한 효과를 얻기 위해 "nosniff" 지시어를 사용하는 것이 좋습니다.
  • 브라우저 응답의 콘텐츠 유형을 해석하는 방법을 제어하기 위해 웹 서버에서 사용할 수 있는 http 응답 헤더이다. 브라우저가 선언된 콘텐츠 유형에 의존하는 대신 응답 콘텐츠 유형을 추측하고자 할 때 발생하는 mime 스피닝 공격에서 보호하는 데 도움이 되는 보안 헤더이다.

 

  • X-XSS-Protection
    1. "0": 브라우저에서 XSS 보호 기능을 비활성화합니다. 브라우저는 XSS 공격을 감지하거나 차단하려고 시도하지 않습니다.
    2. "1": 브라우저에서 XSS 보호 기능을 활성화합니다. 브라우저가 잠재적인 XSS 공격을 감지하면 악성 스크립트를 차단하고 페이지를 실행하지 않고 렌더링하려고 시도합니다.
    3. "1; mode=block": 브라우저에서 XSS 보호 기능을 활성화하고 잠재적인 XSS 공격이 감지되면 페이지가 차단되도록 합니다. 이 지시문은 더 엄격하며 공격이 의심되는 경우 페이지가 렌더링 되지 않도록 합니다.
  • XSS-Protection은 최신 웹 브라우저에 제공하는 내장 xss 보호 메커니즘을 활성화 또는 비활성화하기 위해 웹 서버에서 사용할 수 있는 HTTP 응답 헤더이다. XSS 공격은 공격자가 악의적인 스크립트를 웹 페이지에 삽입한 뒤 다음 다른 사용자의 브라우저 컨텍스트에 실행되어 잠재적으로 무단 액세스 또는 데이터 도난으로 이어질 때 발생한다. X-XSS-Protection 헤더를 통해 웹 개발자는 브라우저의 XSS 보호 기능 동작을 제어할 수 있으며 다음과 같은 방식을 사용한다.

 

  • 스니핑
    • 스니핑 종류
      • 패킷 스니핑
      • 개별 네트워크 패킷 캡처와 검사가 포함되며 네트워크 인터페이스를 무차별 모드로 설정하면 스니퍼 도구 또는 소프트웨어가 네트워크의 다른 장치로 향하는 패킷을 캡처하여 사용자가 포함된 데이터를 분석
      • 이더넷 스니핑
      • 이더넷 스니핑은 특히 이더넷 네트워크를 통한 데이터 전송의 기본 단위인 이더넷 프레임 캡처 및 분석에 중점을 둔다. 네트워크 모니터링 또는 보안 분석에 활용
      • 무선 스니핑
      • wi-fi 스니핑이라고 하는 무선 스니핑에는 무선 네트워크 트래픽 캡처 및 분석이 포함된다. 올바른 도구와 기능을 사용하면 공격자 또는 보안 전문가가 장치 간의 무선 통신을 가로채 무선으로 전송되는 패킷을 캡처한다.
      • 네트워크 트래픽
      • 네트워크 통신 패턴, 흐름 및 콘텐츠 모니터링 및 검사가 포함된다. 패턴을 분석하고 이상을 식별하여 보안 전문가는 네트워크 내에서 잠재적인 위협이나 비정상적인 동작을 탐지한다.
  • 컴퓨터 네트워크 및 보안의 맥락에서 스니핑은 분석 또는 승인되지 않는 목적을 위해 네트워크 트래픽을 가로채고 캡처하는 행위를 이야기한다. 여기서 데이터 패킹이 네트워크를 통과할 때 캡처하여 공격자나 승인된 엔티티가 패킷의 내용을 검사 할 수 있도록 한다. 네트워크 스피닝은 이를 수행하는 개인의 상황과 의도에 따라 합법적일 수 있고 악의적일 수 있다.
728x90
반응형
728x90
반응형

[Spring Boot] Spring Boot에서 Thymeleaf 사용하는 이유와 기본 문법 정리

 

[Spring Boot] Spring Boot에서 Thymeleaf 사용하는 이유와 기본 문법 정리

[Spring Boot] Spring Boot 프로젝트에 Thymeleaf 템플릿 엔진 적용하기 [Spring Boot] Spring Boot 프로젝트에 Thymeleaf 템플릿 엔진 적용하기[Spring Boot] Spring Boot에서 다중 파일 업로드(Multi File Upload) 구현 가이드 [

crushed-taro.tistory.com

1. Thymeleaf

1. 타임리프의 문법

1. 제어문

종류 문법 설명
if문 th:if="${ONGL을 통한 조건식}" 변수 표현식의 OGNL을 활용한 조건식으로 조건문을 작성하면 결과가 true일 때 해당 태그 범위가 처리된다.
else문 th:unless="${ONGL을 통한 조건식}" 변수 표현식의 OGNL을 활용한 결과가 false일 때 해당 태그 범위가 처리된다.
다중조건처리문 th:if="${ONGL을 통한 조건식 and ONGL을 통한 조건식...}" 변수 표현식의 OGNL을 활용한 조건식들과 and 또는 or를 통해 다중 조건문을 작성하고 결과가 true일 때 해당 태그 범위가 처리된다.
switch문 th:swith="${...}과 th:case="리터럴" th:switch와 th:case를 통해 해당 조건의 값이 어떤 case에 해당되는지에 따라 태그를 선택할 수 있다.
each문 th:each="변수 : ${collection값}" 컬렉션에 해당하는 값들을 하나씩 변수에 담아 collection의 크기만큼 반복하여 태그를 처리한다.

 

if문 / else문 / 다중조건처리문

// ModelAndView의 Model에 추가
mv.addObject("num", 1);
mv.addObject("str", "바나나");
<p th:if="${ num > 0 }">넘어온 값은 0보다 크다.</p>		<!-- 조건에 해당되면 -->
<p th:if="${ num < 0 }">넘어온 값은 0보다 작다.</p>
<p th:unless="${ num < 0 }">넘어온 값은 0보다 크다.</p>	<!-- 조건에 해당하지 않으면 -->

<th:block th:if="${ str == '사과' }">				<!-- th:block을 사용할 수도 있다. -->
	<p>사과 좋아요!</p>
</th:block>
<th:block th:if="${ str == '바나나' }">
	<p>바나나 좋아요!</p>
</th:block>

<!-- and나 or를 사용해서 다중 조건 처리도 가능하다. -->
<p th:if="${ num > 0 or num <= 10 }">1부터 10까지의 양수</p>
<p th:if="${ str != null and str == '바나나' }">바나나 좋아요!</p>
<!-- #strings라는 타임리프에서 제공하는 Utility Objects에서 제공하는 메소드를 통해서도 null에 대한 처리를 할 수 있다. -->
<p th:if="${ !#strings.isEmpty(str) and str == '바나나' }">바나나 좋아요!</p>

 

switch문

// ModelAndView의 Model에 추가
mv.addObject("str", "바나나");
<th:block th:switch="${ str }">
	<span th:case="사과">사과가 선택되었습니다</span>
  <span th:case="바나나">바나나가 선택되었습니다</span>
</th:block>

 

each문

// ModelAndView의 Model에 추가 (name, age, gender, address)
List<MemberDTO> memberList = new ArrayList<>();
memberList.add(new MemberDTO("홍길동", 20, '남', "서울시 서초구"));
memberList.add(new MemberDTO("유관순", 22, '여', "서울시 노원구"));
memberList.add(new MemberDTO("장보고", 40, '남', "서울시 종로구"));
memberList.add(new MemberDTO("신사임당", 30, '여', "서울시 성북구"));

mv.addObject("memberList", memberList);
<table>
	<tr>
		<th>이름</th>
		<th>나이</th>
		<th>성별</th>
		<th>주소</th>
	</tr>
	<tr th:each="member : ${ memberList }">
		<td th:text="${ member.name }"></td>
		<td th:text="${ member.age }"></td>
		<td th:text="${ member.gender }"></td>
		<td th:text="${ member.address }"></td>
	</tr>
</table>
<!-- th:each에 stat을 추가해서 반복상태를 확인할 수 있다. -->
<table>
  <tr>
	  <th>이름</th>
    <th>나이</th>
    <th>성별</th>
    <th>주소</th>
    <th>INDEX</th>
    <th>COUNT</th>
    <th>SIZE</th>
    <th>CURRENT</th>
    <th>EVEN</th>
    <th>ODD</th>
    <th>FIRST</th>
    <th>LAST</th>
  </tr>
  <tr th:each="member, stat : ${ memberList }">
    <td th:text="${ member.name }"></td>
    <td th:text="${ member.age }"></td>
    <td th:text="${ member.gender }"></td>
    <td th:text="${ member.address }"></td>
    <td th:text="${ stat.index }"></td>
    <td th:text="${ stat.count }"></td>
    <td th:text="${ stat.size }"></td>
    <td th:text="${ stat.current }"></td>
    <td th:text="${ stat.even }"></td>
    <td th:text="${ stat.odd }"></td>
    <td th:text="${ stat.first }"></td>
    <td th:text="${ stat.last }"></td>
  </tr>
</table>
<!-- th:each에 stat을 추가하지 않으면 '변수명+Stat'으로 반복상태를 확인할 수 있다. -->
<table>
  <tr>
	  <th>이름</th>
    <th>나이</th>
    <th>성별</th>
    <th>주소</th>
    <th>INDEX</th>
    <th>COUNT</th>
    <th>SIZE</th>
    <th>CURRENT</th>
    <th>EVEN</th>
    <th>ODD</th>
    <th>FIRST</th>
    <th>LAST</th>
  </tr>
  <tr th:each="member: ${ memberList }">
    <td th:text="${ member.name }"></td>
    <td th:text="${ member.age }"></td>
    <td th:text="${ member.gender }"></td>
    <td th:text="${ member.address }"></td>
    <td th:text="${ memberStat.index }"></td>
    <td th:text="${ memberStat.count }"></td>
    <td th:text="${ memberStat.size }"></td>
    <td th:text="${ memberStat.current }"></td>
    <td th:text="${ memberStat.even }"></td>
    <td th:text="${ memberStat.odd }"></td>
    <td th:text="${ memberStat.first }"></td>
    <td th:text="${ memberStat.last }"></td>
  </tr>
</table>

 

2. SpringEL

  • 변수 표현식(${...})에서 SpringEL을 사용하여 단순한 변수가 아닌 Object, List, Map같은 객체의 값들을 불러올 수 있다.
종류 문법 설명
Object ${객체명.속성명} 해당 객체의 속성값을 불러온다.
  ${객체명['속성명']}  
  ${객체명.속성의 getter()}  
List ${List객체명[index번째 객체].속성명} List에서 index번째 객체의 속성을 불러온다.
  ${List객체명[index번째 객체]['속성명']}  
  ${List객체명[index번째 객체].속성의 getter()}  
  ${List객체명.get(index번째 객체).속성의 getter()}  
  ${List객체명.get(index번째 객체).속성명}  
Map ${Map객체명['객체의 키값']['속성명']} Map에서 키값에 해당하는 객체의 속성을 불러온다.
  ${Map객체명['객체의 키값']['속성명']}  
  ${Map객체명['객체의 키값'].속성의 getter()}
 

 

// ModelAndView의 Model에 추가 (name, age, gender, address)
MemberDTO member = new MemberDTO("홍길동", 20, '남', "서울시 서초구");

mv.addObject("member", member);

List<MemberDTO> memberList = new ArrayList<>();
memberList.add(new MemberDTO("홍길동", 20, '남', "서울시 서초구"));
memberList.add(new MemberDTO("유관순", 22, '여', "서울시 노원구"));
memberList.add(new MemberDTO("장보고", 40, '남', "서울시 종로구"));
memberList.add(new MemberDTO("신사임당", 30, '여', "서울시 성북구"));

mv.addObject("memberList", memberList);

Map<String, MemberDTO> memberMap = new HashMap<>();
memberMap.put("m01", new MemberDTO("홍길동", 20, '남', "서울시 서초구"));
memberMap.put("m02", new MemberDTO("유관순", 22, '여', "서울시 노원구"));
memberMap.put("m03", new MemberDTO("장보고", 40, '남', "서울시 종로구"));
memberMap.put("m04", new MemberDTO("신사임당", 30, '여', "서울시 성북구"));

mv.addObject("memberMap", memberMap);
<p>Object</p>
<ul>
	<li th:text="${ member.name }"></li>
	<li th:text="${ member['age'] }"></li>
		<!-- 위 두가지 방식은 getter가 필요 없지만 getGender()는 반드시 해당 클래스에 getter가 있어야 한다. -->
	<li th:text="${ member.getGender() }"></li>
</ul>
<p>List</p>
<ul>
	<li th:text="${ memberList[1].name }"></li>
	<li th:text="${ memberList[1]['age'] }"></li>
	<!-- 위 두가지 방식은 getter가 필요 없지만 getGender()는 반드시 해당 클래스에 getter가 있어야 한다. -->
	<li th:text="${ memberList[1].getGender() }"></li>
	<li th:text="${ memberList.get(1).getGender() }"></li>
	<li th:text="${ memberList.get(1).address }"></li>
</ul>
<p>Map</p>
<ul>
	<li th:text="${ memberMap['m03'].name }"></li>
	<li th:text="${ memberMap['m03']['age'] }"></li>
	<!-- 위 두가지 방식은 getter가 필요 없지만 getGender()는 반드시 해당 클래스에 getter가 있어야 한다. -->
	<li th:text="${ memberMap['m03'].getGender() }"></li>	
</ul>

 

3. 기타

종류 문법 설명
타임리프 네임스키마 xmlns:th="http://www.thymeleaf.org" 타임리프를 활용하기 위해서는 html의 html태그에 네임스키마로 선언해 주어야 한다.
escape 적용 th:text="${...}" 변수 표현식의 값을 불러오지만 escape가 적용되어 태그를 단순 문자열로 처리하고 html에 표현한다.
escape 미적용 th:utext="${...}" 변수 표현식의 값을 불러오지만 escape가 적용되지 않아 태그를 태그로써 인식하게 처리하고 html에 표현한다.
value 속성 적용 th:value="${...}" 변수 표현식의 값을 불러와 태그의 value값을 지정한다.
리터럴 치환
th:text=”|리터럴${…}리터럴|”
‘| ‘를 양 옆에 사용하면 ‘+’를 쓰지 않고 문자열 합치기를 할 수 있다.
블럭태그 th:block 범위를 지정하고 싶을 때 사용한다.
th:block을 통해 해당 범위에 변수나 객체를 적용하거나 조건에 해당되는지에 따라 해당 범위를 보여주거나 보여주지 않을 때 사용할 수 있다.
지역변수 th:with="변수명1 = ${...}, 변수명2 =${...}, ..." 변수 표현식(${...})을 통해 불러온 값을 해당하는 변수명으로 해당 태그 범위의 지역변수가 되게 한다.
security 인증 정보 여부 sec:authorize="isAuthenticated()” 타임리프에서 시큐리티 적용 시 로그인, 로그아웃에 대한 이벤트를 줄 수 있다.

 

타임리프 네임스키마

<html xmlns:th="http://www.thymeleaf.org">

 

escape 적용/미적용, value 속성 적용

태그의 값을 태그 내부의 값으로 작성하기 위해서는 th:text 또는 th:utext를 사용할 수 있다. th:text는 escape가 적용되어 태그를 단순 문자열로 처리하지만 th:utext는 escape가 적용되지 않아 태그를 태그로써 인식할 수 있다. (DB에 태그가 포함된 문자열을 저장했을 시 유용

th:value는 태그의 value값을 지정할 수 있다.

// ModelAndView의 Model에 추가
mv.addObject("hello", "hello!<h3>Thymeleaf</h3>");
<ul>
	<li th:text="${ hello }"></li>
	<li th:utext="${ hello }"></li>
	<li><input type="text" th:value="타임리프"></li>
</ul>

 

리터럴 치환

// ModelAndView의 Model에 추가 (name, age, gender, address)
mv.addObject("member", new MemberDTO("홍길동", 20, '남', "서울시 서초구"));
<p th:object="${ member }" th:text="|name = '*{ name }'|"></p>
<p th:object="${ member }" th:text="|age = '*{ age }'|"></p>
<p th:object="${ member }" th:text="|gender = '*{ gender }'|"></p>
<p th:object="${ member }" th:text="|address = '*{ address }'|"></p>

 

블럭 태그

// ModelAndView의 Model에 추가 (name, age, gender, address)
mv.addObject("member", new MemberDTO("홍길동", 20, '남', "서울시 서초구"));
<th:block th:object="${ member }" >
	<p th:text="*{ age }"></p>
</th:block>

 

지역 변수

// ModelAndView의 Model에 추가 (startPage, endPage, pageNo)
SelectCriteria selectCriteria = new SelectCriteria(1, 10, 3);
mv.addObject(selectCriteria);
<th:block th:with="start = ${ selectCriteria.startPage }, last = ${ selectCriteria.endPage }">
  <th:block th:each="p : ${ #numbers.sequence(start, last) }">
    <th:block th:if="${ selectCriteria.pageNo eq p }">
        <button th:text="${ p }" disabled></button>
    </th:block>
    <th:block th:if="${ selectCriteria.pageNo ne p }">
        <button th:text="${ p }"></button>
    </th:block>
  </th:block>
</th:block>

 

728x90
반응형
728x90
반응형

[Spring Boot] Spring Boot 프로젝트에 Thymeleaf 템플릿 엔진 적용하기

 

[Spring Boot] Spring Boot 프로젝트에 Thymeleaf 템플릿 엔진 적용하기

[Spring Boot] Spring Boot에서 다중 파일 업로드(Multi File Upload) 구현 가이드 [Spring Boot] Spring Boot에서 다중 파일 업로드(Multi File Upload) 구현 가이드[Spring Boot] Spring Boot File Upload 예제 | 이미지 및 파일 업

crushed-taro.tistory.com

1. Thymeleaf

1. 타임리프의 사용 이유

1. 타임리프의 장점

  • Natural Templates를 제공한다.(HTML의 기본 구조를 그대로 사용할 수 있으며 HTML파일을 직접 열어도 동작한다.)
  • [Natural Templates]: 기존 HTML의 마크업 언어를 사용한 디자인 구조로 되어 있는 템플릿으로 서버를 구동하지 않으면 순수 HTML을, 서버를 구동하면 동적으로 HTML이 생성된다. 즉, 순수 HTML을 그대로 유지하면서 뷰 템플릿도 사용할 수 있는 특징이 있다.
  • 개발 시 디자인과 개발이 분리되어 작업 효율이 좋다.
  • WAS를 통하지 않고도 파일을 웹 브라우저를 통해 열 수 있어 퍼블리셔와 협업이 용이하다.

2. 타임리프의 단점

  • jsp태그 라이브러리와 custom태그들을 사용할 수 없어 기존 JSP코드를 재사용할 수 없다.
  • 기존 태그를 유지하고 속성으로 템플릿 구문을 넣는데 있어 어느정도 한계가 있고 자바스크립트나 jQuery의 도움이 필요할 수 있다.

2. 타임리프의 문법

1. 주석

종류 문법 설명
parser-level 주석 <!--/* 주석내용 */--> parser-level 주석은 정적인 페이지에서는 주석으로 있다가 thymeleaf가 처리될 때 제거되어 클라이언트에게 노출되지 않는다는 장점이 있다.
prototype-only 주석 <!--/*/ 주석내용 /*/--> prototype-only 주석은 정적 페이지에서 주석으로 있다가 thymeleaf 처리 후에는 화면에 보여지게 되는 주석
<ul>
  <li>parser-level 주석</li>
  <!--/* 주석내용 */-->
  <li>prototype-only 주석</li>
  <!--/*/ 주석내용 /*/-->
</ul>

 

2. 표현식

종류 문법 설명
변수 표현식 ${...} parameter, session, model 등에 저장되어 있는 변수의 값들을 문자열로 변환하여 불러온다.
메시지 표현식 #{...} message source로부터 키에 해당하는 메시지를 가져온다.
링크 표현식 @{...} th:href, th:src, th:action 등과 같이 URL이 지정되는 속성에 사용한다.
선택 변수 표현식 *{...} 부모 태그의 th:object에 지정된 객체를 기준으로 해당 객체의 속성에 접근한다.
인라인 표현식 [[...]], [(...)] 텍스트 모드나 자바스크립트 모드로 내부에 선언한 변수 표현식의 값을 가져와서 html에 직접 표시한다.

 

변수 표현식

<button onclick="location.href='/lecture/expression?title=표현식&no=5&no=6'">
	표현식
</button>

<p th:text="${ param.title }"></p>
<p th:text="${ param.no[0] }"></p>
<p th:text="${ param.no[1] }"></p>
<!-- 파라미터가 존재하지 않으면 무시하지 않고 에러 발생함 -->
<!--<p th:text="${ param.no[2] }"></p>-->
  • parameter ⇒ param
  • session attribute ⇒ session
  • request attribute ⇒ 따로 작성하지 않는다.

메세지 표현식

# resources/messages.properties
message.value=hello world

<p th:text="#{ message.value }"></p>

 

링크 표현식

<a th:href="@{/}">메인으로</a>

 

선택 변수 표현식

// ModelAndView의 Model에 추가 (name, age, gender, address)
mv.addObject("member", new MemberDTO("홍길동", 20, '남', "서울시 서초구"));
<p th:text="${ member.name }"></p>
<p th:object="${ member }" th:text="*{ age }"></p>
<p th:object="${ member }" th:text="*{ gender }"></p>
<div th:object="${ member }" >
	<p th:text="*{ address }"></p>
</div>

 

인라인 표현식

// ModelAndView의 Model에 추가
mv.addObject("hello", "hello!<h3>Thymeleaf</h3>");

<p th:inline="none">
	변수 표현식의 값을 html에 직접 표시하기 위해서 th:text와 같은 [[...]]를 사용하고 
	th:utext와 같은 [(...)]를 사용할 수 있다.
	대괄호로 묶어 이와 같이 변수 표현식의 값을 가져오는 것을 인라인 모드라고 하며 
	인라인 모드는 text모드와 자바스크립트 모드가 있다.
	우리는 변수 표현식의 값을 자바스크립트에서 쓰는 것이 아닌 html에서 사용하려는 것이므로
	th:inline="text"를 태그에 속성값으로 주고 써야 하지만 
	th:inline="text"를 쓰지 않아도 적용된다.

	반면 인라인 모드를 적용하지 않으려면 th:inline="none"을 속성값으로 주면 변수 표현식의 값이
	인라인모드로 사용하지 않아([[]] 또는 [()]를 인식하지 않음) 단순 문자열로 처리할 수 있다.

	자바스크립트에서 사용하려면 th:inline="javascript"를 태그에 속성값으로 주고 써야 하지만
	역시나 th:inline="javascript"를 쓰지 않아도 적용된다.
<p>
<ul>
	<li th:inline="text">[[${ hello }]]</li>
	<li>[(${ hello })]</li>
	<li th:inline="none">[[${ hello }]]</li>
	<li th:inline="none">[(${ hello })]</li>
</ul>
<script th:inline="javascript">

	window.onload = function(){

		/* 정적 페이지에서는 자바스크립트 문법 오류가 난다. (리터럴 형태가 아니기 때문)
		 * 하지만 동적 페이지에서는 정상 동작한다. ""로 감싸서 던진다.
	   * */
	  // let hello = [[${hello}]];

	  /* 정적 페이지에서는 자바스크립트 문법 오류가 발생하지 않는다. (문자열 리터럴 형태이기 때문)
	   * 하지만 동적 페이지에서는 에러 발생한다. ""로 감싸기 때문에 ""[[${ hello }]]""가 된다.
	   * */
	  // let hello = "[[${ hello }]]";

	  /* 정적 페이지와 동적 페이지 모두 문제가 생기지는 않는다. */
	  let hello = '[[${ hello }]]';

	  alert(hello);
  }
</script>
728x90
반응형
728x90
반응형

[Spring Boot] Spring Boot에서 다중 파일 업로드(Multi File Upload) 구현 가이드

 

[Spring Boot] Spring Boot에서 다중 파일 업로드(Multi File Upload) 구현 가이드

[Spring Boot] Spring Boot File Upload 예제 | 이미지 및 파일 업로드 구현 가이드 [Spring Boot] Spring Boot File Upload 예제 | 이미지 및 파일 업로드 구현 가이드[Spring Boot] Spring Boot에서 Interceptor 사용하기 | 설정

crushed-taro.tistory.com

1. 타임리프(Thymeleaf) 개요

1. 타임리프란?

  • 타임리프는 스프링 부트에서 공식적으로 지원하는 View 템플릿이다.

Spring Boot 사진 1

Thymeleaf는 웹 및 독립 실행형 환경 모두를 위한 최신 서버 측 Java 템플릿 엔진입니다.
Thymeleaf의 주요 목표는 귀하의 개발 워크플로에 우아하고 자연스러운 템플릿을 가져오는 것입니다.
HTML은 브라우저에 올바르게 표시될 수 있고 정적 프로토타입으로도 작동하여 개발 팀에서 보다 강력한 협업을 가능하게 합니다.
Spring Framework용 모듈, 선호하는 도구와의 통합 호스트 및 고유한 기능을 플러그인할 수 있는 기능을 갖춘 Thymeleaf는 할 수 있는 것이 훨씬 더 많지만 현대 HTML5 JVM 웹 개발에 이상적입니다.
출처 : https://www.thymeleaf.org/
 

Thymeleaf

Integrations galore Eclipse, IntelliJ IDEA, Spring, Play, even the up-and-coming Model-View-Controller API for Java EE 8. Write Thymeleaf in your favourite tools, using your favourite web-development framework. Check out our Ecosystem to see more integrati

www.thymeleaf.org

 

2. 타임리프의 특징

  • JSP와 달리 Thymeleaf 문서는 html 확장자를 갖고 있어 JSP처럼 Servlet이 문서를 표현하는 방식이 아니기 때문에 서버 없이도 동작 가능하다.
  • SSR 템플릿으로 백엔드에서 HTML을 동적으로 생성한다.
  • 타임리프는 스프링 부트에서 사용할 것을 권장하고 있고 스프링부트에서 JSP는 별도의 설정이 필요한 반면 타임리프는 바로 적용이 될만큼 스프링과 자연스럽게 통합할 수 있다.
  • [템플릿 엔진]: 데이터와 이 데이터들을 표현 할 템플릿을 결합해주는 도구로 스프링 부트가 지원하는 템플릿 엔진은 Thymeleaf, Freemarker, Mustache, Groovy가 있다.

출처 : https://en.wikipedia.org/wiki/Web_template_system

 

Web template system - Wikipedia

From Wikipedia, the free encyclopedia System in web publishing The basic process for a server-side web templating system: content (from a database), and "presentation specifications" (in a web template), are combined (through the template engine) to mass-p

en.wikipedia.org

Spring Boot 사진 2

728x90
반응형
728x90
반응형

[Spring Boot] Spring Boot File Upload 예제 | 이미지 및 파일 업로드 구현 가이드

 

[Spring Boot] Spring Boot File Upload 예제 | 이미지 및 파일 업로드 구현 가이드

[Spring Boot] Spring Boot에서 Interceptor 사용하기 | 설정부터 실전 예제까지 [Spring Boot] Spring Boot에서 Interceptor 사용하기 | 설정부터 실전 예제까지[Spring Boot] Spring Boot 에러 처리: @ControllerAdvice로 쉽게 관

crushed-taro.tistory.com

1. MultiFile Upload

1. 파일 업로드 뷰 페이지 작성

업로드 폼을 보여줄 뷰 페이지를 작성한다.

<form action="multi-file" method="post" encType="multipart/form-data">
  파일 : <input type="file" name="multiFiles" multiple><br>
  파일 설명 : <input type="text" name="multiFileDescription"><br>
  <input type="submit">
</form>

 

위 코드에서 input 태그의 multiple은 파일 여러 개를 한 번에 업로드 하기 위한 속성이다.

 

2. 파일 업로드를 처리할 컨트롤러 생성

업로드 파일이 같은 name 속성으로 여러 개 전달 되므로 MultipartFile클래스는 List타입으로 선언해야 한다.

@PostMapping("multi-file")
	public String multiFileUpload(@RequestParam List<MultipartFile> multiFiles, 
			String multiFileDescription, Model model) {

		System.out.println("multiFiles : " + multiFiles);
		System.out.println("multiFileDescription : " + multiFileDescription);
		
		/* 파일을 저장할 경로 설정 */
		String root = "src/main/resources/static";
		
		System.out.println("root : " + root);
		
		String filePath = root + "/uploadFiles";
		
		File dir = new File(filePath);
		if(!dir.exists()) {
			dir.mkdirs();
		}

		List<FileDTO> files = new ArrayList<>();
		
		try {
			for(MultipartFile file : multiFiles) {
				/* 파일명 변경 처리 */
				String originFileName = file.getOriginalFilename();
				String ext = originFileName.substring(originFileName.lastIndexOf("."));
				String savedName = UUID.randomUUID().toString().replace("-", "") + ext;

				/* 파일에 관한 정보 추출 후 보관 */
				files.add(new FileDTO(originFileName, savedName, filePath, 
					multiFileDescription));
				/* 파일을 저장 */
				file.transferTo(new File(filePath + "/" + savedName));
			}
			model.addAttribute("message", "파일 업로드 성공!");
		} catch (Exception e) {
			e.printStackTrace();
			
			/* 실패 시 이전에 저장 된 파일 삭제 */
			for(FileDTO file : files) {
				new File(filePath + "/" + file.getSavedName()).delete();
			}

			model.addAttribute("message", "파일 업로드 실패!!");
		} 
		
		return "result";
	}

 

@RequestParam어노테이션을 이용하여 요청 파라미터 중 multiFiles라는 이름으로 전송된 파일을 List<MultipartFile>객체로 받아온다. 이후 해당 파일을 처리하는데 필요한 정보를 DTO 타입을 선언해서 다룰 수 있다. 추후 DB에 저장하는 등의 작업이 필요하다. 실패 시 이전에 저장 된 파일은 삭제하며 최종적으로 result라는 뷰 페이지를 반환한다.

 

3. FileDTO

파일을 처리하는데 필요한 정보를 DTO 타입을 선언해서 다룰 수 있다. 추후 DB에 저장하는 등의 작업 시 활용 된다.

 

public class FileDTO {

    private String originFileName;
    private String savedName;
    private String filePath;
    private String fileDescription;

    public FileDTO() {
    }

    public FileDTO(String originFileName, String savedName, String filePath,
				  String fileDescription) {
        this.originFileName = originFileName;
        this.savedName = savedName;
        this.filePath = filePath;
        this.fileDescription = fileDescription;
    }

    public String getOriginFileName() {
        return originFileName;
    }

    public void setOriginFileName(String originFileName) {
        this.originFileName = originFileName;
    }

    public String getSavedName() {
        return savedName;
    }

    public void setSavedName(String savedName) {
        this.savedName = savedName;
    }

    public String getFilePath() {
        return filePath;
    }

    public void setFilePath(String filePath) {
        this.filePath = filePath;
    }

    public String getFileDescription() {
        return fileDescription;
    }

    public void setFileDescription(String fileDescription) {
        this.fileDescription = fileDescription;
    }

    @Override
    public String toString() {
        return "FileDTO{" +
                "originFileName='" + originFileName + '\'' +
                ", savedName='" + savedName + '\'' +
                ", filePath='" + filePath + '\'' +
                ", fileDescription='" + fileDescription + '\'' +
                '}';
    }

}

 

4. 반환 뷰 페이지

이전 테스트와 동일한 반환 뷰 페이지를 사용한다.

 

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>result</title>
</head>
<body>
    <h1 th:text="${ message }"></h1>
</body>
</html>

 

지정된 경로에 파일이 업로드 되는 것을 확인할 수 있다.

728x90
반응형
728x90
반응형

[Spring Boot] Spring Boot에서 Interceptor 사용하기 | 설정부터 실전 예제까지

 

[Spring Boot] Spring Boot에서 Interceptor 사용하기 | 설정부터 실전 예제까지

[Spring Boot] Spring Boot 에러 처리: @ControllerAdvice로 쉽게 관리하기 [Spring Boot] Spring Boot 에러 처리: @ControllerAdvice로 쉽게 관리하기[Spring Boot] Spring Boot 에러 처리: @ExceptionHandler로 쉽게 관리하기 [Spring Boo

crushed-taro.tistory.com

1. Single File Upload

1. application.properties

spring-boot-starter-web에는 file upload를 위한 multipartResolver 가 기본 빈으로 등록 되어 있기 때문에 추가적으로 등록할 필요는 없다. 파일 저장 경로, 용량 등에 대한 설정을 application.properties를 이용해서 할 수 있다.

 

파일의 크기가 fize-size-threshold 값 이하라면 임시파일을 생성하지 않고 메모리에서 즉시 파일을 읽어서 생성할 수 있다. 속도는 빠르지만 쓰레드가 작업을 수행하는 동안 부담이 될 수 있다.

 

파일의 크기가 fize-size-threshold 값을 초과한다면 파일은 spring.servlet.multipart.location 경로에 저장되어 해당 파일을 읽어서 작업을 하도록 되어있다. 이 예제에서는 프로젝트 내부 경로에 업로드 파일을 저장하려고 하니 프로젝트 절대 경로를 파일 저장 경로로 설정한다.

 

# 파일 저장 경로
spring.servlet.multipart.location=프로젝트절대경로

# 최대 업로드 파일 크기
spring.servlet.multipart.max-file-size=10MB

# 최대 요청 파일 크기
spring.servlet.multipart.max-request-size=10MB

 

2. 파일 업로드 뷰 페이지 작성

업로드 폼을 보여줄 뷰 페이지를 작성한다. 

 

<form action="single-file" method="post" encType="multipart/form-data">
	파일 : <input type="file" name="singleFile"><br>
  파일 설명 : <input type="text" name="singleFileDescription"><br>
  <input type="submit">
</form>

 

위 코드에서 enctype="multipart/form-data"는 파일 업로드를 위한 인코딩 타입을 지정하는 속성이다.

 

3. 파일 업로드를 처리할 컨트롤러 생성

 

Spring Framework에서는 MultipartFile클래스를 이용하여 파일 업로드를 처리한다. 이를 처리할 컨트롤러를 생성한다.

 

@PostMapping("single-file")
public String singleFileUpload(@RequestParam MultipartFile singleFile, 
		String singleFileDescription, Model model) {
		
		System.out.println("singleFile : " + singleFile);
		System.out.println("singleFileDescription : " + singleFileDescription);
		
		/* 파일을 저장할 경로 설정 */
		String root = "src/main/resources/static";
		String filePath = root + "/uploadFiles";
		
		File dir = new File(filePath);
		System.out.println(dir.getAbsolutePath());

		if(!dir.exists()) {
			dir.mkdirs();
		}
		
		/* 파일명 변경 처리 */
		String originFileName = singleFile.getOriginalFilename();
		String ext = originFileName.substring(originFileName.lastIndexOf("."));
		String savedName = UUID.randomUUID().toString().replace("-", "") + ext;
		
		/* 파일을 저장 */
		try {
			singleFile.transferTo(new File(filePath + "/" + savedName));
			model.addAttribute("message", "파일 업로드 성공!");
		} catch (Exception e) {
			e.printStackTrace();
			model.addAttribute("message", "파일 업로드 실패!!");
		} 
		
		return "result";
	}

 

@RequestParam어노테이션을 이용하여 요청 파라미터 중 singleFile이라는 이름으로 전송된 파일을 MultipartFile객체로 받아온다. 이후 해당 파일을 리네임 처리하여 저장하고, result라는 뷰 페이지를 반환한다.

 

4. 반환 뷰 페이지 작성

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>result</title>
</head>
<body>
    <h1 th:text="${ message }"></h1>
</body>
</html>

 

지정된 경로에 파일이 업로드 되는 것을 확인할 수 있다.

728x90
반응형

+ Recent posts