티스토리 뷰

반응형

Spring 4 에서 새로 소개된 @Order 어노테이션은 같은 타입의 Bean이 Collection에 Autowired 될 때 순서를 지정하기 위해 사용합니다. 아무래도 인증이나 보안과 관련되어 가장 우선적으로 필터링할 클래스에 주로 사용합니다. 

먼저 사용하기 위한 샘플 소스를 작성해보도록 하겠습니다. 

Work.java

public interface Work {
   public void work();
}

Programmer.java

@Service
@Order(value=1)
public class Programmer implements Work {
   public void work() {
      System.out.println("Programmer Working...");
   }
}

Designer.java

@Service
@Order(value=2)
public class Designer implements Work {
   public void work() {
      System.out.println("Designer Working...");
   }
}

위와 같이 work 메소드를 먼저 정의하였을 경우 출력은 Order가 오름차순으로 Autowired 됩니다. 그래서 결과 또한 아래와 같이 출력될 것입니다.

## 결과
Programmer Working... (Order=1)
Designer Working... (Order=2)

 


이번에는 각 클래스의 Order 내 값만 변경해서 출력해보겠습니다.

Programmer.java

@Service
@Order(value=2)
public class Programmer implements Work {
   public void work() {
      System.out.println("Programmer Working...");
   }
}

Designer.java

@Service
@Order(value=1)
public class Designer implements Work {
   public void work() {
      System.out.println("Designer Working...");
   }
}
## 결과
Designer Working... (Order=1)
Programmer Working... (Order=2)

이런 Order를 적용해 인증과 관련된 클래스, 즉 Filter 클래스를 적용해보면 어떨까요?

JsonToUrlEncodedAuthenticationFilter.java

package com.example.auth.core.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.apache.catalina.connector.RequestFacade;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Objects;

import static org.springframework.util.StringUtils.hasText;

@Component
@Order(value = Integer.MIN_VALUE)
@RequiredArgsConstructor
public class JsonToUrlEncodedAuthenticationFilter implements Filter {
    private final ObjectMapper objectMapper;

    @Override
    public void init(final FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
        try {
            RequestFacade castRequest = (RequestFacade) request;
        } catch (ClassCastException e) {
            chain.doFilter(request, response);
            return;
        }

        if (Objects.equals(request.getContentType(), "application/json") && Objects.equals(((RequestFacade) request).getServletPath(), "/oauth/token")) {
            InputStream is = request.getInputStream();
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();

            int nRead;
            byte[] data = new byte[16384];

            while ((nRead = is.read(data, 0, data.length)) != -1) {
                buffer.write(data, 0, nRead);
            }
            buffer.flush();
            byte[] json = buffer.toByteArray();

            HashMap<String, String> result = objectMapper.readValue(json, HashMap.class);
            HashMap<String, String[]> r = new HashMap<>();
            for (String key : result.keySet()) {
                String[] val = new String[1];
                val[0] = result.get(key);
                r.put(key, val);
            }

            String[] val = new String[1];
            val[0] = ((RequestFacade) request).getMethod();
            r.put("_method", val);

            String authorization = ((RequestFacade) request).getHeader("Authorization");
            if (hasText(authorization) && authorization.toLowerCase().startsWith("basic")) {
                final String[] values = decodeHttpBasicAuthorization(authorization);
                String[] client_id = new String[1];
                client_id[0] = values[0];
                String[] client_secret = new String[1];
                client_secret[0] = values[1];
                r.put("client_id", client_id);
                r.put("client_secret", client_secret);
            }

            HttpServletRequest s = new MyServletRequestWrapper(((HttpServletRequest) request), r);
            chain.doFilter(s, response);
        } else {
            chain.doFilter(request, response);
        }
    }

    private String[] decodeHttpBasicAuthorization(final String authorization) {
        String base64Credentials = authorization.substring("Basic".length()).trim();
        byte[] credDecoded = Base64.getDecoder().decode(base64Credentials);
        String credentials = new String(credDecoded, StandardCharsets.UTF_8);
        final String[] values = credentials.split(":", 2);
        return values;
    }

    @Override
    public void destroy() {

    }
}

 

위에서는 @Order(value = Integer.MIN_VALUE) 로 적용하여 가장 우선적으로 필터링을 진행하기 위한 어노테이션을 적용했습니다. 

여기서 잘 보아야할 메소드는 doFilter 입니다. 가장 먼저 try-catch 문을 이용하여 ServletRequestRequestFacade로 강제형변환이 되는지 체크 후 하위 소스에서 RequestFacade에서 제공하는 getMethod() 와 getHeader("Authorization") 를 활용하여 인증 절차를 거치게 됩니다. 

반응형
댓글
공지사항