[Mark1] Spring Security 설정

iniestar·2021년 12월 30일
0

Mark1

목록 보기
3/4

스프링 시큐리티(Spring Security)는 로그인, 로그아웃, 세션, 접근 권한 등의 중요한 보안 설정을 하는 모듈이다. 소스에서는 "WebSecurityConfigurerAdapter" 클래스를 상속하여 하위 함수들을 통해 설정한다.

스프링 시큐리티에서의 설정은 configure 함수를 오버라이딩하여 사용하는데, 설정을 3가지의 큰 꼭지로 나뉠 수 있다.

① 사용자 인증정보 설정

웹페이지에 로그인 할 경우 사용자의 아이디와 패스워드, 그리고 로그인 레벨이 무엇인지를 식별하여 로그인을 허용한다.

② Ignore URL 설정

서버에 요청되는 URL 중 인증이 필요하지 않은 URL에 대하여 스프링 시큐리티 룰을 무시하는 패턴을 설정한다.
(ex. "/css/**", "/img/**")

③ 접근 권한 및 세션 설정

로그인 , 로그아웃, 접근 제한 시 처리하게 될 프로세스를 설정하며 요청하는 세션에 대해서 설정한다.

위 설정들을 실제 코드 상에서 표현하면 아래와 같다.

1. Custom Security 클래스 생성

별도의 Spring Security를 상속하기 위한 하위 클래스를 생성한다. 이후 부팅 시 설정을 위해 호출되는 @Configuration 어노테이션과 Spring Security 클래스를 명시하는 @EnableWebSecurity 어노테이션를 선언한 다음 "WebSecurityConfigurerAdapter" 클래스를 상속받는다.

@Configuration
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
 
(생략..)

2. WebSecurityConfigurerAdapter 클래스 오버라이딩

1) 사용자 인증정보 설정

로그인 시 어떤 아이디, 패스워드로 로그인할지 설정하며 일반적으로 Dao 객체를 통해 DB에 등록된 사용자를 연결한다.

추후에 자세히 다룰것이며 현재는 고정으로 아이디와 패스워드를 설정한다. 설정 방법은 다음과 같다.

@Override
 protected void configure(AuthenticationManagerBuilder auth) throws Exception {
 	auth.inMemoryAuthentication()
   	.withUser("admin")
   	// In spring security 5 must determine the algorithm that
   	// your passwords are stored in and prefix ass passwords with {id}
   	.password("{noop}admin123") // To migrate plain text password by prefixing them with {noop}
   	.roles("ADMIN");
 }

2) Ignore URL 설정

css, image, js 등의 공통으로 사용하는 파일들에 대해서 URL 접근 시 스프링 시큐리티 룰을 무시하도록 설정한다.

설정 방법은 다음과 같다.

@Override
public void configure(org.springframework.security.config.annotation.web.builders.WebSecurity web) throws Exception {
	web.ignoring().antMatchers("/css/**", "/img/**", "/scss/**", "/js/**");
}

3) 접근 권한 및 세션 설정

로그인, 로그아웃, 접근 제한 시 페이지 처리 부분과 세션에 대한 설정을 다룬다. 설정 방법은 다음과 같다.

@Override
protected void configure(HttpSecurity http) throws Exception {
  http.csrf().disable()// 보안설정을 비활성화
  .authorizeRequests()
  .antMatchers("/", "/login", "resources").permitAll()
  .anyRequest().authenticated() // 그외 리소스들은 인증을해야 접근 가능
 
  .and()
  .formLogin()
  .loginPage("/login") // 별도 로그인 페이지 사용
  .successHandler(SuccessHandler()) // 로그인 성공 시 별도 핸들러 사용
  .failureHandler(FailureHandler()) // 로그인 실패 시 별도 핸들러 사용
 
  .and()
  .logout()
  .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
  .logoutSuccessUrl("/login") // 로그아웃 성공 시 URL 리턴
 
  .and()
  .exceptionHandling()
  .accessDeniedHandler(AccessDeniedHandler()) // 별도 Access Denied 핸들러 사용
 
  .and()
  .sessionManagement()
  .maximumSessions(1) // 같은 아이디로 1명만 로그인할 수 있음
  .maxSessionsPreventsLogin(true) // 신규 로그인 사용자의 로그인 허용되고, 기존 사용자는 로그아웃
  ;
}

3. 핸들러 생성

"WebSecurityConfigurerAdapter" 클래스를 상속받아 생성한 Configure 함수에서 로그인 성공, 실패, 접근제한 등의 상황에 대하여 사용자가 컨트롤 할 수 있도록 지원하고 있다. 별도의 커스텀 핸들러 클래스를 생성하고 핸들러 인터페이스를 상속하여 사용이 가능하다. 로그인 성공, 실패, 접근 제한에 대한 핸들러 인터페이스는 다음과 같다.

※ Spring Security Handler Interface

   구분					  인터페이스
로그인 성공			AuthenticationSuccessHandler
로그인 실패			AuthenticationFailureHandler
접근 제한			AccessDeniedHandler

위 인터페이스를 상속하여 아래와 같이 설정이 가능하다.

1) 로그인 성공 핸들러 예제

public class CustomLoginSuccessHandler implements AuthenticationSuccessHandler {
    private static final Logger logger = LoggerFactory.getLogger(CustomLoginSuccessHandler.class);
 
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
 
        HttpSession session = request.getSession();
        logger.info("[Login success...]");
 
        if(null != session) {
            logger.info("Session ID [" + session.getId() + "]");
            logger.info("User ID [" + authentication.getName() + "]");
            request.setAttribute("userName", authentication.getName());
            request.getRequestDispatcher("/index").forward(request, response);
        }
    }
}

로그인 세션이 생성되었는지 확인하고 정상적으로 생성이 되었다면 프레임워크로 전달된 파라미터를 통해 값을 출력한다.

2) 로그인 실패 핸들러 예제

public class CustomLoginFailureHandler implements AuthenticationFailureHandler {
    private static final Logger logger = LoggerFactory.getLogger(CustomLoginFailureHandler.class);
 
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        logger.info("[Login Fail...]");
 
        request.getRequestDispatcher("/login").forward(request, response);
    }
}

로그인 실패 시 로그인 페이지로 리다이렉트를 하며, 추후 별도의 오류 문구를 삽입할 예정.

3) 접근 제한 핸들러 예제
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
    private static final Logger logger = LoggerFactory.getLogger(CustomAccessDeniedHandler.class);
 
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        logger.info("Access Denied >>[" + request.getRequestURI() + "]");
 
        request.getRequestDispatcher("/error").forward(request, response);
    }
}

본 프로젝트에서는 특정 페이지 접근에 실패했을 경우 통합 에러 페이지를 호출 한다. 에러 내용 을 별로 페이지로 나누지 않은 이유는 에러 정보 또한 공격자에게 중요한 힌트가 될 수 있기 때문.

profile
while(true) { 손가락 관절염++ };

0개의 댓글