본문으로 바로가기


(글을 시작하기 전, interceptor에 대한 간략한 정리가 필요하다면 http://www.leafcats.com/39 를 참조하시기 바란다.)


spring에서 interceptor를 사용하기 위해서는 java코드로 configuration 설정을 해 주던가, xml에서 interceptor에 대한 설정을 해 주면 될 것이다.


그렇다면 spring을 누구나 '쉽게' 사용 가능한 spring boot에서의 interceptor 사용은 어떨까?

설정 자체는 일반 spring과 크게 다를게 없다.


1. 사용자는 로그인을 한다. 

2. 시스템은 로그인한 사용자 정보를 세션에 저장한다. 

3. 사용자는 기능을 요청한다.

4. 시스템은 사용자가 요청한 기능을 수행하기 전에 요청한 사용자의 세션을 체크한다. 

5. 시스템은 올바른 세션일 경우 기능 승인, 세션이 없거나 올바르지 않을 경우 사용자를 로그인 페이지로 이동시킨다.


위 로직에서 4번과 5번의 사용자 세션 체크 기능을 interceptor로 구현해 보겠다.



우선 해당 로직을 수행할 인터셉터를 만들자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Component
public class CertificationInterceptor implements HandlerInterceptor{
 
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        HttpSession session = request.getSession();
        UserVO loginVO = (UserVO) session.getAttribute("loginUser");
 
        if(ObjectUtils.isEmpty(loginVO)){
            response.sendRedirect("/moveLogin.go");
            return false;
        }else{
            session.setMaxInactiveInterval(30*60);
            return true;
        }
        
    }
 
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        // TODO Auto-generated method stub
        
    }
 
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // TODO Auto-generated method stub
        
    }
 
}
cs


HandlerInterceptor 인터페이스를 상속받으면, 아래 3개의 메소드를 오버라이드한다.


- preHandle : 클라이언트의 요청을 컨트롤러에 전달하기 전에 호출된다. 여기서 false를 리턴하면 다음 내용(Controller)을 실행하지 않는다.

- postHandle : 클라이언트의 요청을 처리한 뒤에 호출된다. 컨트롤러에서 예외가 발생되면 실행되지 않는다.

- afterCompletion : 클라이언트 요청을 마치고 클라이언트에서 뷰를 통해 응답을 전송한뒤 실행이 된다. 뷰를 생성할 때에 예외가 발생할 경우에도 실행이 된다.


이 중, 로그인 세션 체크를 위한 로직은 preHandle에서 위 코드처럼 처리했다. 아마 대다수의 로직은 이곳에서 작성될 것이다. 여기서는 세션이 있는지만을 체크 했는데, 좀 더 확실하게 하려면 세션에 있는 사용자 정보로 DB에 한번 더 다녀와서 정확하게 검증 하는 것이 좋다.




인터셉터를 만들었으니 이제 사용하기 위한 설정을 해 보자.


인터셉터에 대한 설정은 spring boot의 main 메소드가 있는 클래스에 해도 작동한다.

하지만 나는 @configuration 어노테이션을 사용한 configuration전용 클래스에 각종 자바로 해야하는 spring 설정들을 하는 편이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 
@Configuration
@MapperScan(value={"com.**.mapper"})
public class WebConfig extends WebMvcConfigurerAdapter{
    
    /*
     * mybatis mapper 설정
     * */
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception{
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        Resource[] arrResource = new PathMatchingResourcePatternResolver()
                .getResources("classpath:mapper/**/*Mapper.xml");
        sqlSessionFactoryBean.setMapperLocations(arrResource);
        sqlSessionFactoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
        return sqlSessionFactoryBean.getObject();
    }
    
    /*
     * 로그인 인증 Interceptor 설정
     * */
    @Autowired
    CertificationInterceptor certificationInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(certificationInterceptor)
                .addPathPatterns("/**/*.do");
    }
 
}
 
cs


위에 mybatis mapper 설정 부분은 볼 필요 없고, 아래쪽에 로그인 인증 Interceptor 설정 부분을 보면 된다.

@MapperScan(value={"com.**.mapper"}) 은 mybatis mapper scan을 위한 어노테이션 이므로 생략한다.


addPathPatterns에는 controller로 오는 요청들에 대한 패턴을 지정하여, 이 패턴들에 해당하는 요청들에 대해 지정한 interceptor를 태우라는 설정이다.

설정한 패턴들 중, 일부 패턴을 제외하고 싶다면 excludePathPatterns 를 붙여 제외하고 싶은 패턴을 string 값으로 주면 된다.


나는 뷰에서 컨트롤러로 들어오는 모든 요청들을 ".do"로 지정했다. 이렇게 되면 위에 지정한 CertificationInterceptor에서 ".do"에 대한 모든 요청들을 가로채 세션 체크를 할 것이다.


다만, login 하기 전에 수행할 수 있는 기능들(login, 회원가입, 비밀번호찾기)의 경우는 저 인터셉터를 거치면 안되기 때문에 excludePathPatterns 에 선언하면 될 것이다.

나같은 경우에는 해당 기능들은 ".do"가 아닌 ".go"를 사용해 피해가는 방법을 택했다.





 Other Contents 

댓글을 달아 주세요

  1. tcp 2020.07.08 15:17

    큰 도움이 되었습니다. 감사합니다