---
# System prepended metadata

title: AuthenticationFilter

---

# AuthenticationFilter
### 前言
&nbsp;&nbsp;身份驗證器主要用於提升系統安全性，確保每一次 API 存取都已通過授權與驗證。本範例示範如何透過 **IP 白名單** 與 **API Key 驗證** 來進行基本的存取控制。

### 實作
&nbsp;&nbsp;假設今天是要先檢查`IP`或是`API KEY`是否在白名單內，才能進行訪問，實作如下。

加入依賴
```xml=
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
```

appliction.propertiex
```propertiex=
# authentication
demo.api.allowlist=123.45.678.910
demo.api.key[0].value=DEMO_1234-5678-1234-5678
demo.api.key[0].expiry=1798646400000
demo.api.key[1].value=DEMO_9876-5432-9876-5432
demo.api.key[1].expiry=0000000000000
```

DemoAuthenticationConfigurationProperties.java
```java=
@Configuration
@ConfigurationProperties(prefix = "demo.api")
@Data
public class DemoAuthenticationConfigurationProperties {
    private List<String> allowlist;
    private List<Key> key;
    
    @Data
    public static class Key {

        private String value;

        private Long expiry;
    }
}
```

AuthenticationFilter.java
```java=
@Component
@Slf4j
public class AuthenticationFilter extends OncePerRequestFilter {
    private final List<DemoAuthenticationConfigurationProperties.Key> gatewebApiKey;
    private final List<String> gatewebIpAddressAllowlist;
    
    private static final String API_KEY_HEADER = "DEMO-API-KEY";
    
    public AuthenticationFilter(DemoAuthenticationConfigurationProperties demoAuthenticationConfigurationProperties) {
        log.info("DemoAuthenticationConfigurationProperties: {}", demoAuthenticationConfigurationProperties);
        this.demoApiKey = demoAuthenticationConfigurationProperties.getKey();
        this.demoIpAddressAllowlist = demoAuthenticationConfigurationProperties.getAllowlist();
    }
    
    @Override
    protected void doFilterInternal(
            @NonNull HttpServletRequest request,
            @NonNull HttpServletResponse response,
            @NonNull FilterChain filterChain
    ) throws ServletException, IOException {        
        final String requestURI = request.getRequestURI();
        final boolean shouldCheckPermission = requestURI.startsWith("/demo");
        final boolean isAuthorized = isAuthorized(request.getHeader("Authorization"));
        final String clientIp = getClientIp(request);
        final boolean isAllowedIp = isAllowedIp(clientIp);
        
        if (shouldCheckPermission && !isAuthorized && !isAllowedIp) {
                log.warn("Request [{}] and Ip [{}] with [{}] Authorization is unauthorized", request.getRequestURI(), clientIp, request.getHeader("Authorization"));
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                return;
            }
        
            log.debug("Request [{}] and Ip [{}] with [{}] Authorization is authorized", request.getRequestURI(), clientIp, isAuthorized ? "correct" : "incorrect");
        }
        
        filterChain.doFilter(request, response);
    }
    
     public boolean isAuthorized(String authorization) {
        if (authorization == null || authorization.isEmpty()) {
            return false;
        }

        return demoApiKey.stream()
                .filter(key -> key.getValue().equals(authorization.substring(API_KEY_HEADER.length() + 1).trim()))
                .anyMatch(key -> isValidExpiry(key.getExpiryDate()));
    }

    private boolean isAllowedIp(String clientIp) {
        if (demoIpAddressAllowlist == null || demoIpAddressAllowlist.isEmpty()) {
            return false;
        }

        return demoIpAddressAllowlist.contains(clientIp);
    }

    private boolean isValidExpiry(LocalDate expiry) {
        return !LocalDate.now().isAfter(expiry);
    }
    
}
```

:::info
注入設定檔的值後，就是一些if else檢查判斷，就可以達成過濾啦!
:::

### 總結
&nbsp;&nbsp;本範例透過 Spring Security 的 `OncePerRequestFilter` 實作了一個簡易的身份驗證機制，包含：

1. IP 白名單機制
   * 限制只有特定 IP 能存取 API
2. API Key 驗證
   * 驗證請求是否攜帶有效 Key
   * 並檢查 Key 是否過期
3. 綜合驗證
   * 當IP或是API Key任一個是有效的就能存取 API

此設計適合用於：
* 內部 API 保護
* Gateway 前置驗證
* 微服務初步安全控管

### 參考網址
https://chikuwacode.github.io/articles/spring-boot-security-implement-authentication-filter-with-jwt/
https://www.baeldung.com/configuration-properties-in-spring-boot
https://hackmd.io/@Ernie1999/SkyU6JRp-l