개발/Spring(Hodol)

Spring - API 인증 (파라미터, Header, Interceptor)

잇(IT) 2023. 10. 4. 13:09
728x90

- API 인증은 요청이 들어올 때 인증된 요청인가 허용된 사용자인가 확인 후 수행하기 위해서 사용한다.

 

- Get Parameter 방식
@PostMapping("/posts")
    public void post(@RequestBody @Valid PostCreate request, @RequestParam String authorization) {
        if(authorization.equals("bis")){
            request.validate();
            postService.write(request);
        }
    }

1. RequestParam 방식을 이용하여 쿼리 파라미터로 넘어오는 값을 비교하는 방법이 있다.

 

- test 코드

//when
        mockMvc.perform(MockMvcRequestBuilders.post("/posts?authorization=bis")
                                .contentType(APPLICATION_JSON)
//                                .content("{\"title\": \"제목입니다.\", \"content\": \"내용입니다.\"}")
                                .content(json)
                )
                .andExpect(status().isOk())
                .andDo(print());

1. 테스트 코드를 통해 쿼리 파라미터로 지정된 사용자명이 넘어왔을 때 허용이 된다.


- Header 방식
@PostMapping("/posts")
    public void post(@RequestBody @Valid PostCreate request, @RequestHeader String authorization) {
        if(authorization.equals("bis")){
            request.validate();
            postService.write(request);
        }
    }

1. 쿼리 파라미터가 아닌 요청 속성 중 Header 속성을 사용하는 방법이 있다. @RequestHeader를 이용하는 방식이다.

2. Header의 authorization 속성을 가져와 해당 속성의 값과 비교하여 요청을 인증하는 방식이다.

 

- test 코드

//when
        mockMvc.perform(MockMvcRequestBuilders.post("/posts")
                        .header("authorization", "bis")
                                .contentType(APPLICATION_JSON)
//                                .content("{\"title\": \"제목입니다.\", \"content\": \"내용입니다.\"}")
                                .content(json)
                )
                .andExpect(status().isOk())
                .andDo(print());

- 테스트 코드에 header 속성 값을 지정하여 테스트를 하게되면 정상적으로 통과하는 것을 볼 수 있다.


- Interceptor

- Spring Interceptor는 웹 애플리케이션에서 HTTP 요청의 처리 과정 중에 특정 작업을 수행하도록 설계된 컴포넌트이다.

 1. 인증 및 권한 검사

 2. 로깅 및 감사

 3. 캐싱

 4. 요청 및 응답 변형

 5. 예외 처리

 

- Spring Interceptor는 HandlerInterceptor 인터페이스를 구현하여 작성되며, preHandle, postHandle, afterCompletion과 같은 메서드를 제공하여 요청 처리 전후 및 완료 후에 작업을 수행할 수 있다.

 1. preHandle은 핸들러(Controller) 가기전에 무조건 실행된다. 

 2. postHandle은 핸들러(Controller) 가고 나서 실행된다.

 3. afterCompletion은 View로 보낸 이후에 실행된다.

 

- AuthInterceptor.java

@Slf4j
public class AuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info(">> preHandle");
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info(">> postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info(">> afterCompletion");
    }
}

- Interceptor만 작성하게 되면 웹 애플리케이션에 적용되지 않기 때문에 추가적으로 Configuration 코드를 작성해주어야 한다.

 

- WebMvcConfig.java

package com.islog.api.config;


import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuthInterceptor())
                // 여기까지만 사용하면 AuthInterceptor 사용 가능


    }
}

1. @Configuration을 이용하여 설정 클래스로 지정하고, WebMvcCOnfigurer를 상속 받는다.

2. addInterceptors 메서드를 이용하여 기존에 생성한 AuthInterceptor 객체를 추가한다.

3. 위 과정을 거쳐야 웹 애플리케이션에 기존에 작성한 AuthInterceptor가 적용된다.

 

- 디버깅

- 각 log.info를 관찰하기 각 log.info에 포인트를 걸어놓고 디버깅을 해보면

1. url로 요청을 보내게 되면, 아직 응답이 오기전 즉, controller에 도달하기 전 preHandle이 실행된다.

2. 다음 Controller에 요청이 전달되고, 응답이 오고 난 후 postHandle이 실행된다.

3. View까지 전달된 이후 afterCompletion이 실행되는 것을 확인 할 수 있다.

 

- preHandle의 경우 위에서 확인해본 것과 같이 핸들러에 도달하기 전 실행되는 부분이기 때문에 이전 파라미터, header를 통해 인증을 했던 방식처럼 인증을 사용하게 되면, 모든 controller에 인증이 요청된다.

 

* preHandle의 반환 타입은 boolean인데, false가 반환되면 핸들러에 정상적으로 도달하지 않는다.

 

- AuthInterceptor.java

@Slf4j
public class AuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info(">> preHandle");

        String accessToken = request.getParameter("accessToken");

        if(accessToken != null && accessToken.equals("bis")){
            return true;

        }
        throw new Unauthorized();
    }

1. 요청 파라미터 속성 중 accessToken의 값을 가져와 비교한다.

2. request의 원하는 속성 값이 맞을 경우 return true를 실행하고, 일치하지 않은 경우 예외처리를 한다.

 

* 추가로 accessToken의 경우 null을 받을 수 있기 때문에 사전에 null에 대한 처리를 해주어야 한다.

- 정상적으로 인증이 완료되었을 때 반환을 하고, 

 

- 인증을 실패하게 되면 예외를 발생시킨다.


- Interceptor 적용 예외

- 모든 요청에 Interceptor를 적용시키지 않고 싶다면 예외를 통해 처리 할 수 있다.

 

- WebMvcConfig.java

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuthInterceptor())
                // 여기까지만 사용하면 그냥 AuthInterceptor 사용 가능
                .excludePathPatterns("/test2");
        // 인터셉터를 적용 안할 경로를 위와 같이 지정 할 수 있다.

    }
}

- .excludePathPatterns를 통해 Interceptor를 예외시키고 싶은 요청 경로를 지정하게 되면 해당 경로는 Interceptor를 적용 받지 않는다.

 

 

728x90