개요
대부분의 서비스는 인증이 된 사용자만 이용이 가능합니다. 이번 포스팅에서는 스프링을 활용하여 기본적인 인증 방법을 소개하고자 합니다.
1.인증을 받는 방법
이번 포스팅에서는 다음과 같은 방법으로 인증 절차를 구현해보고자 합니다.
1.
GET Parameter
2.
Header
먼저 단순한 형태의 Get Parameter 입니다.
@PostMapping("/posts")
public void post(@RequestBody @Valid PostCreateDto request,
@RequestParam String authorization) {
if(authorization.equals("auth"){
request.isValidate();
postService.write(request);
}
}
Java
복사
이렇게 코드를 구현하게 되면 http://localhost:8080/posts?authorization = auth 형식으로 호출 해야만 글을 작성할 수 있습니다.
하지만 주소는 리소스를 식별하기 위한 방법인데 불필요하게 인증에 필요한 코드가 들어가면 좋지 않습니다.
Header를 이용한 인증 방법입니다.
@PostMapping("/posts")
public void post(@RequestBody @Valid PostCreateDto request,
@RequestHeader String authorization) {
if(authorization.equals("auth"){
request.isValidate();
postService.write(request);
}
}
Java
복사
헤더에 값이 제대로 가는지 어떻게 확인할까요? MockMvc를 통해 구현 가능합니다.
PostControllerTest.java
@Test
@DisplayName("글 작성시 헤더로 인증 값을 전달")
public write() throws Exception {
//given
PostCreateDto request = PostCreateDto.builder()
.title("블로그 제목")
.content("블로그 내용")
.build();
String json = objectMapper.writeValueAsString(request);
// when
mockMvc.perform(post("/posts")
.header("authorization", "auth")
.contentType(APPLICATION_JSON)
.content(json))
.andExpect(status().isOk())
.andDo(print());
Java
복사
2. 인증이 필요한 메서드와 필요없는 메서드
블로그를 만든다고 가정 해봅시다. 블로그의 기능 중에서 인증이 필요한 기능은 무엇이 있을까요?
글 작성, 수정과 같이 블로그에 대부분을 차지하는 내용들은 블로그 주인만 작성할 수 있기에 인증이 필요합니다. 하지만 글 조회 같은 경우는 인증이 필요 없습니다.
그러면 글 작성, 수정, 그리고 삭제에 대한 메서드에 인증 절차를 추가한다고 가정해보겠습니다.
@PostMapping("/posts")
public void post(@RequestBody @Valid PostCreateDto request,
@RequestHeader String authorization) {
if(authorization.equals("auth"){
request.isValidate();
postService.write(request);
}
}
@PatchMapping("/posts/{postId}")
public PostResponse edit(@PathVariable Long postId,
@RequestBody @Valid PostEditor request,
@RequestHeader String authorization) {
return postService.edit(postId, request);
}
@DeleteMapping("/posts/{postId}")
public Post delete(@PathVariable Long postId,
@RequestHeader String authorization) {
return postService.delete(postId);
}
Java
복사
하지만 매번 인증이 필요한 메서드에 @RequestHeader를 추가하는 것은 여간 귀찮은 일이 아닙니다.
인터셉터로 반복 최소화 하기
이를 서술하기에 앞서 인터셉터가 무엇인지 짚고 넘어가야 될 것 같습니다.
3. 인터셉터란
인터셉터는 “낚아채다” 라는 의미를 가지고 있는 영단어입니다. 그 의미와 걸맞게 인터셉터는 클라이언트에 의해 호출된 특정 URL이 컨트롤러에 도달하기 전 낚아채는 역할을 합니다. 따라서 개발자는 기존 컨트롤러를 수정하지 않고 원하는 추가 작업을 사전 혹은 사후에 하여 컨트롤러를 제어할 수 있도록 도와주는 것이 인터셉터 입니다.
어떻게 구현하나요?
HandlerInterceptor 혹은 HandlerInterceptorAdapter를 오버라이딩 함으로써 구현합니다.
HandlerInterceptor는 총 세 개의 메서드를 가지고 있습니다.
preHandle()
•
컨트롤러의 동작 이전에 수행할 동작을 제어합니다.
•
컨트롤러 동작 이전에 처리해야 할 작업이 있는 경우 혹은 요청 정보를 가공하거나 추가하는 경우 사용합니다.
•
실행되어야 할 핸들러에 대한 정보를 매개변수로 받기 때문에 세밀한 로직을 구성할 수 있습니다.
•
리턴 값이 boolean이며 리턴이 true일 경우 preHandle() 실행 후 핸들러에 접근합니다. false일 경우 작업을 중단하기 때문에 컨트롤러와 남은 인터셉터가 실행되지 않습니다.
postHandle()
•
핸들러가 이미 실행이 된 상태이지만 View가 생성되기 이전에 호출됩니다.
•
ModelAndView 타입의 정보를 매개변수로 받기 떄문에 컨트롤러에서 View 정보를 전달하기 위해 작업한 Model 객체 정보를 참조 혹은 조작할 수 있습니다.
•
preHandler()가 false인 경우 실행되지 않습니다.
•
적용 중인 인터셉터가 여러 개인 경우 preHandler()는 역순으로 호출됩니다.
•
비동기적 요청 처리 시에는 처리되지 않습니다.
afterComplement()
•
모든 View에서 최종 결과를 생성하는 일을 포함한 모든 작업이 완료된 후에 실행됩니다.
그렇다면 인터셉터를 만들어보도록 하겠습니다.
AuthInterceptor.java
@Slf4j
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
String accessToken = request.getParameter("accessToken");
if (accessToken != null && accessToken.equals("auth")) {
return true;
}
throw new UnauthorizedException();
}
}
Java
복사
위 코드는 요청에 포함된 accessToken을 확인하여 인증이 되었는지 판단하는 메서드입니다. preHandle() 메서드에 해당 코드를 구현했기 때문에 컨트롤러 동작 이전에 인증 처리를 합니다.
만약 인증이 되지 않았다면UnauthorizedException 를 발생 시킵니다. 해당 예외는 401 상태코드를 반환합니다.
추가적으로 Interceptor를 등록해주기 위해서 WebMvcConfigurer를 구현한 설정 클래스에 등록해주어야 합니다.
WebMvcConfig.java
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor());
// .excludePathPatterns()
// .addPathPatterns();
}
}
Java
복사
+) 주석 처리된 두 메서드를 통해 인증에서 제외하거나 추가할 경로를 설정할 수 있습니다.