Subversion Repositories SmartDukaan

Rev

Rev 35272 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 35272 Rev 36510
Line 1... Line 1...
1
package com.spice.profitmandi.web.interceptor;
1
package com.spice.profitmandi.web.interceptor;
2
 
2
 
3
import javax.servlet.http.HttpServletRequest;
-
 
4
 
-
 
5
import javax.servlet.http.HttpServletResponse;
3
import com.spice.profitmandi.web.filter.RequestCachingFilter;
6
 
-
 
7
import org.apache.logging.log4j.Logger;
-
 
8
import org.apache.logging.log4j.LogManager;
4
import org.apache.logging.log4j.LogManager;
-
 
5
import org.apache.logging.log4j.Logger;
9
import org.springframework.beans.factory.annotation.Autowired;
6
import org.springframework.beans.factory.annotation.Autowired;
10
import org.springframework.cache.Cache;
-
 
11
import org.springframework.cache.CacheManager;
7
import org.springframework.data.redis.core.RedisTemplate;
12
import org.springframework.stereotype.Component;
8
import org.springframework.stereotype.Component;
13
import org.springframework.web.servlet.HandlerInterceptor;
9
import org.springframework.web.servlet.HandlerInterceptor;
14
import org.springframework.web.servlet.ModelAndView;
10
import org.springframework.web.servlet.ModelAndView;
15
 
11
 
16
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
12
import javax.servlet.http.HttpServletRequest;
17
import com.spice.profitmandi.common.model.UserInfo;
13
import javax.servlet.http.HttpServletResponse;
18
import com.spice.profitmandi.service.authentication.JWTUtil;
14
import java.security.MessageDigest;
19
import com.spice.profitmandi.common.web.util.ResponseSender;
15
import java.util.concurrent.TimeUnit;
20
 
16
 
21
@Component
17
@Component
22
public class PostInterceptor implements HandlerInterceptor {
18
public class PostInterceptor implements HandlerInterceptor {
23
 
19
 
24
    private static final Logger LOGGER = LogManager.getLogger(PostInterceptor.class);
20
    private static final Logger LOGGER = LogManager.getLogger(PostInterceptor.class);
-
 
21
    private static final String IDEM_PREFIX = "idem:";
-
 
22
    private static final long IDEM_TTL_SECONDS = 300;
-
 
23
    private static final String REQUEST_ATTR_IDEM_KEY = "postInterceptor.idemKey";
25
 
24
 
26
    @Autowired
25
    @Autowired
27
    private ResponseSender<?> responseSender;
26
    private RedisTemplate<String, Object> redisTemplate;
28
 
27
 
29
    @Autowired
28
    @Override
-
 
29
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
30
    private CacheManager timeoutCacheManager;
30
        String idempotencyHeader = request.getHeader("IdempotencyKey");
-
 
31
        boolean isPost = "POST".equalsIgnoreCase(request.getMethod());
31
 
32
 
-
 
33
        if (!isPost && (idempotencyHeader == null || idempotencyHeader.isEmpty())) {
32
    @Autowired
34
            return true;
33
    JWTUtil jwtUtil;
35
        }
34
 
36
 
-
 
37
        String idemKey = buildIdempotencyKey(request, idempotencyHeader);
-
 
38
        if (idemKey == null) {
-
 
39
            return true;
35
    @Override
40
        }
-
 
41
 
-
 
42
        String redisKey = IDEM_PREFIX + idemKey;
-
 
43
        Boolean claimed = redisTemplate.opsForValue()
36
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object arg2, Exception arg3)
44
                .setIfAbsent(redisKey, "pending", IDEM_TTL_SECONDS, TimeUnit.SECONDS);
-
 
45
 
-
 
46
        if (Boolean.TRUE.equals(claimed)) {
-
 
47
            request.setAttribute(REQUEST_ATTR_IDEM_KEY, idemKey);
37
            throws Exception {
48
            return true;
-
 
49
        }
-
 
50
 
-
 
51
        LOGGER.info("Duplicate request detected: idemKey={}, uri={}", idemKey, request.getRequestURI());
-
 
52
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
-
 
53
        response.getWriter().write("Duplicate request.");
-
 
54
        return false;
38
    }
55
    }
39
 
56
 
40
    @Override
57
    @Override
41
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object arg2, ModelAndView arg3)
58
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
42
            throws Exception {
59
            throws Exception {
43
        LOGGER.info("request is received after : "+request.getRequestURL().toString());
-
 
44
    }
60
    }
45
 
61
 
46
    @Override
62
    @Override
47
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
63
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
-
 
64
            throws Exception {
48
        LOGGER.info("request is received in post interceptor before [{}] with method [{}]", request.getRequestURL().toString(), request.getMethod());
65
        String idemKey = (String) request.getAttribute(REQUEST_ATTR_IDEM_KEY);
49
        if ("POST".equalsIgnoreCase(request.getMethod())) {
66
        if (idemKey == null) {
-
 
67
            return;
-
 
68
        }
50
            String idempotencyKey = request.getHeader("IdempotencyKey");
69
        String redisKey = IDEM_PREFIX + idemKey;
51
            if (idempotencyKey == null || idempotencyKey.isEmpty()) {
70
        if (ex == null && response.getStatus() >= 200 && response.getStatus() < 300) {
52
//                response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing idempotency token in header");
71
            redisTemplate.opsForValue().set(redisKey, "done", IDEM_TTL_SECONDS, TimeUnit.SECONDS);
-
 
72
        } else {
53
                return true;
73
            redisTemplate.delete(redisKey);
54
            }
74
        }
-
 
75
    }
55
 
76
 
56
            Cache cache = timeoutCacheManager.getCache("IdempotencyKey");
77
    private String buildIdempotencyKey(HttpServletRequest request, String header) {
57
            if (cache != null) {
78
        if (header != null && !header.isEmpty()) {
58
                if (cache.get(idempotencyKey) != null) {
79
            String uri = request.getRequestURI();
59
                    response.setStatus(HttpServletResponse.SC_OK);
80
            String query = request.getQueryString();
60
                    response.getWriter().write("Duplicate request. Idempotency Key already processed.");
81
            String scope = header + "|" + uri + (query != null ? "?" + query : "");
61
                    return false;
82
            return sha256(scope.getBytes(java.nio.charset.StandardCharsets.UTF_8));
62
                } else {
83
        }
-
 
84
        if ("POST".equalsIgnoreCase(request.getMethod())
63
                    cache.put(idempotencyKey, "PROCESSED");
85
                && request instanceof RequestCachingFilter.CachedBodyRequest) {
-
 
86
            byte[] body = ((RequestCachingFilter.CachedBodyRequest) request).getCachedBody();
64
                    return true;
87
            if (body.length > 0) {
65
                }
88
                return sha256(body);
66
            }
89
            }
67
        }
90
        }
68
        return true;
91
        return null;
69
    }
92
    }
70
 
93
 
71
    private UserInfo getUserInfo(String token) throws ProfitMandiBusinessException{
94
    private static String sha256(byte[] data) {
-
 
95
        try {
-
 
96
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
-
 
97
            byte[] hash = digest.digest(data);
-
 
98
            StringBuilder hex = new StringBuilder(64);
-
 
99
            for (byte b : hash) {
-
 
100
                hex.append(String.format("%02x", b));
-
 
101
            }
72
        return jwtUtil.getUserInfo(token);
102
            return hex.toString();
-
 
103
        } catch (Exception e) {
-
 
104
            throw new RuntimeException("SHA-256 not available", e);
-
 
105
        }
73
    }
106
    }
74
}
107
}