shopify hmac optimise

master
Yixian 3 years ago
parent c084cbd9cb
commit 1c249b1e3c

@ -0,0 +1,22 @@
package au.com.royalpay.payment.manage;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CacheRequestFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (request instanceof ContentCachingRequestWrapper) {
filterChain.doFilter(request, response);
} else {
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
filterChain.doFilter(wrappedRequest, response);
}
}
}

@ -17,6 +17,11 @@ public class WebConfiguration implements WebMvcConfigurer {
@Resource @Resource
private ManagerUserInterceptor managerUserInterceptor; private ManagerUserInterceptor managerUserInterceptor;
@Bean
public CacheRequestFilter cacheRequestFilter() {
return new CacheRequestFilter();
}
@Bean @Bean
public ShopifyRequestInfoInterceptor shopifyRequestInfoInterceptor() { public ShopifyRequestInfoInterceptor shopifyRequestInfoInterceptor() {
return new ShopifyRequestInfoInterceptor(); return new ShopifyRequestInfoInterceptor();

@ -40,7 +40,7 @@ public class ShopifyAuthTemplateController {
*/ */
@GetMapping("/auth") @GetMapping("/auth")
@ShopifyEndpoint @ShopifyEndpoint
public String shopifyStorePermission(@RequestParam("shop") String shop, public String shopifyStorePermission(@RequestParam(value = "shop", required = false) String shop,
@RequestParam("hmac") String hmac, HttpServletRequest request) { @RequestParam("hmac") String hmac, HttpServletRequest request) {
if (!Pattern.matches("^[a-zA-Z0-9][a-zA-Z0-9\\-]*\\.myshopify\\.com", shop)) { if (!Pattern.matches("^[a-zA-Z0-9][a-zA-Z0-9\\-]*\\.myshopify\\.com", shop)) {
throw new BadRequestException("Parameter shop is invalid."); throw new BadRequestException("Parameter shop is invalid.");

@ -1,5 +1,8 @@
package au.com.royalpay.payment.manage.shopify.support; package au.com.royalpay.payment.manage.shopify.support;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.HmacAlgorithms; import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils; import org.apache.commons.codec.digest.HmacUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -14,6 +17,7 @@ import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.annotation.adapters.HexBinaryAdapter; import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.Security; import java.security.Security;
import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
public class HmacVerificationUtil { public class HmacVerificationUtil {
@ -25,10 +29,10 @@ public class HmacVerificationUtil {
public static boolean checkParameters(String message, String secret, String hmac) { public static boolean checkParameters(String message, String secret, String hmac) {
try { try {
Security.addProvider(new BouncyCastleProvider()); Security.addProvider(new BouncyCastleProvider());
SecretKey secretKey = new SecretKeySpec(secret.getBytes("UTF8"), "HmacSHA256"); SecretKey secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
Mac mac = Mac.getInstance(secretKey.getAlgorithm()); Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey); mac.init(secretKey);
byte[] digest = mac.doFinal(message.getBytes("UTF-8")); byte[] digest = mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
String marshal = new HexBinaryAdapter().marshal(digest).toLowerCase(Locale.ROOT); String marshal = new HexBinaryAdapter().marshal(digest).toLowerCase(Locale.ROOT);
return StringUtils.equals(marshal, hmac); return StringUtils.equals(marshal, hmac);
} catch (Exception e) { } catch (Exception e) {
@ -37,28 +41,34 @@ public class HmacVerificationUtil {
} }
public static boolean hmacSHA256(String input, String key, String hmac) { public static boolean hmacSHA256(String input, String key, String hmac) {
String encode = encode(input, key, HmacAlgorithms.HMAC_SHA_256); if (isHex(hmac)) {
logger.debug("input={}; key={}; encoded={}; request-hmac: {}", input, key, encode, hmac); try {
return StringUtils.equals(encode, hmac); byte[] requestHmac = Hex.decodeHex(hmac);
byte[] hmacRes = hmac(input, key, HmacAlgorithms.HMAC_SHA_256);
String hmacHex = Hex.encodeHexString(hmacRes);
logger.debug("hex-mode: input={}; key={}; encoded={}; request-hmac: {}", input, key, hmacHex, hmac);
return Arrays.equals(requestHmac, hmacRes);
} catch (DecoderException ignore) {
return false;
}
} else {
//base64
byte[] hmacRes = hmac(input, key, HmacAlgorithms.HMAC_SHA_256);
String hmacB64 = Base64.encodeBase64String(hmacRes);
logger.debug("b64-mode: input={}; key={}; encoded={}; request-hmac: {}", input, key, hmacB64, hmac);
byte[] requestHmac = Base64.decodeBase64(hmac);
return Arrays.equals(requestHmac, hmacRes);
}
} }
private static String encode(String input, String key, HmacAlgorithms algorithm) { private static boolean isHex(String str) {
Mac mac = HmacUtils.getInitializedMac(algorithm, key.getBytes(StandardCharsets.UTF_8)); return str != null && str.toUpperCase(Locale.ROOT).matches("^[0-9A-F]$");
byte[] content = input.getBytes(StandardCharsets.UTF_8);
byte[] signResult = mac.doFinal(content);
return bytesToHex(signResult);
} }
private static String bytesToHex(byte[] hash) { private static byte[] hmac(String input, String key, HmacAlgorithms algorithm) {
StringBuilder hexString = new StringBuilder(); Mac mac = HmacUtils.getInitializedMac(algorithm, key.getBytes(StandardCharsets.UTF_8));
for (byte b : hash) { byte[] content = input.getBytes(StandardCharsets.UTF_8);
String hex = Integer.toHexString(0xff & b); return mac.doFinal(content);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} }

@ -1,12 +1,18 @@
package au.com.royalpay.payment.manage.shopify.support; package au.com.royalpay.payment.manage.shopify.support;
import org.springframework.web.util.ContentCachingRequestWrapper;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class ShopifyHttpUtils { public class ShopifyHttpUtils {
public static String getRequestBody(HttpServletRequest request) { public static String getRequestBody(HttpServletRequest request) {
if (request instanceof ContentCachingRequestWrapper) {
return new String(((ContentCachingRequestWrapper) request).getContentAsByteArray(), StandardCharsets.UTF_8);
}
BufferedReader br = null; BufferedReader br = null;
StringBuilder sb = new StringBuilder(""); StringBuilder sb = new StringBuilder("");
try { try {

@ -31,6 +31,7 @@ public class ShopifyRequestInfoInterceptor extends HandlerInterceptorAdapter {
if (HttpMethod.POST.matches(request.getMethod())) { if (HttpMethod.POST.matches(request.getMethod())) {
if (AnnotatedElementUtils.isAnnotated(method, ShopifyEndpoint.class)) { if (AnnotatedElementUtils.isAnnotated(method, ShopifyEndpoint.class)) {
String requestBody = ShopifyHttpUtils.getRequestBody(request); String requestBody = ShopifyHttpUtils.getRequestBody(request);
JSONObject body = JSONObject.parseObject(requestBody); JSONObject body = JSONObject.parseObject(requestBody);
String shop = body.getString("shop_domain"); String shop = body.getString("shop_domain");

Loading…
Cancel
Save