added comments and clean up

This commit is contained in:
heyethereum
2024-07-17 00:32:22 +08:00
parent a02eaab0b1
commit f65cee37f9
6 changed files with 116 additions and 98 deletions

View File

@@ -13,5 +13,5 @@ import lombok.experimental.SuperBuilder;
@NoArgsConstructor @NoArgsConstructor
public class BaseScanResponse { public class BaseScanResponse {
private QRCode scannedQRCode; private QRCode scannedQRCode;
private QRCodeType qrCodeType; private QRCodeType qrCode;
} }

View File

@@ -10,5 +10,5 @@ import lombok.experimental.SuperBuilder;
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
public final class URLResponse extends BaseScanResponse{ public final class URLResponse extends BaseScanResponse{
private QRCodeURL qrCodeURL; private QRCodeURL details;
} }

View File

@@ -7,7 +7,7 @@ import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.UuidGenerator;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.UUID; import java.util.UUID;
@@ -21,15 +21,16 @@ import java.util.UUID;
public class QRCode { public class QRCode {
@Id @Id
@JsonIgnore
@GeneratedValue(generator = "UUID") @GeneratedValue(generator = "UUID")
@GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator") @UuidGenerator
@Column(updatable = false, nullable = false) @Column(updatable = false, nullable = false)
private UUID id; private UUID id;
@JsonIgnore @JsonIgnore
@Column(name = "qr_code_type_id", nullable = false) @Column(name = "qr_code_type_id", nullable = false)
private Long qrCodeTypeId; private Long qrCodeTypeId;
@JsonIgnore
private String userId; private String userId;
private String contents; private String contents;

View File

@@ -7,10 +7,9 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.UuidGenerator;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
@Entity @Entity
@@ -20,11 +19,10 @@ import java.util.UUID;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class QRCodeURL { public class QRCodeURL {
@Id @Id
@JsonIgnore @JsonIgnore
@GeneratedValue(generator = "UUID") @GeneratedValue(generator = "UUID")
@GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator") @UuidGenerator
@Column(updatable = false, nullable = false) @Column(updatable = false, nullable = false)
private UUID id; private UUID id;

View File

@@ -23,6 +23,7 @@ import reactor.core.publisher.Mono;
import java.io.IOException; import java.io.IOException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
@@ -44,6 +45,9 @@ public class QRCodeTypeService {
@Autowired @Autowired
private URLRepository urlRepository; private URLRepository urlRepository;
@Autowired
private URLVerificationService urlVerificationService;
@Autowired @Autowired
private SafeBrowsingService safeBrowsingService; private SafeBrowsingService safeBrowsingService;
@@ -98,109 +102,29 @@ public class QRCodeTypeService {
.findFirst() .findFirst()
.orElse(defaultQRCodeType); .orElse(defaultQRCodeType);
} }
private BaseScanResponse insertIntoRespectiveTable(QRCode qrCode, QRCodeType qrCodeType) { private BaseScanResponse insertIntoRespectiveTable(QRCode qrCode, QRCodeType qrCodeType) {
String contents = qrCode.getContents();
try { try {
String url = qrCode.getContents(); QRCodeURL urlObj = urlVerificationService.breakdownURL(contents);
QRCodeURL urlObj = breakdownURL(url); List<String> redirectChain = urlVerificationService.countAndTrackRedirects(contents);
urlObj.setQrCodeId(qrCode.getId()); urlObj.setQrCodeId(qrCode.getId());
List<String> redirectChain = countAndTrackRedirects(url);
logger.info("Redirect chain: {}", redirectChain);
for (int i = 0; i < redirectChain.size(); i++) {
logger.info((i == 0 ? "Initial URL: " : "Redirect #" + i + ": ") + redirectChain.get(i));
}
urlObj.setRedirect(redirectChain.size() - 1); urlObj.setRedirect(redirectChain.size() - 1);
urlObj.setRedirectChain(redirectChain); urlObj.setRedirectChain(redirectChain);
// Insert into URL table
urlRepository.save(urlObj); urlRepository.save(urlObj);
return URLResponse.builder() return URLResponse.builder().scannedQRCode(qrCode).qrCode(qrCodeType).details(urlObj).build();
.scannedQRCode(qrCode) } catch (IOException | URISyntaxException e) {
.qrCodeType(qrCodeType)
.qrCodeURL(urlObj)
.build();
} catch (IOException e) {
logger.error("Error: ", e); logger.error("Error: ", e);
} }
return BaseScanResponse.builder() return BaseScanResponse.builder()
.scannedQRCode(qrCode) .scannedQRCode(qrCode)
.qrCodeType(qrCodeType) .qrCode(qrCodeType)
.build(); .build();
} }
// Function to breakdown URL into subdomain, domain, topLevelDomain, query params, fragment
public QRCodeURL breakdownURL(String urlString) throws MalformedURLException {
URL url = new URL(urlString);
QRCodeURL urlObj = new QRCodeURL();
String host = url.getHost();
String[] hostParts = host.split("\\.");
String subdomain = "";
if (hostParts.length >= 2) {
urlObj.setTopLevelDomain(hostParts[hostParts.length - 1]);
urlObj.setDomain(hostParts[hostParts.length - 2]);
if (hostParts.length > 2) {
subdomain = String.join(".", java.util.Arrays.copyOfRange(hostParts, 0, hostParts.length - 2));
}
}
urlObj.setSubdomain(subdomain);
String path = url.getPath();
urlObj.setPath(path.isEmpty() ? "/" : path);
String query = url.getQuery();
Map<String, String> queryParams = new HashMap<>();
if (query != null) {
for (String param : query.split("&")) {
String[] pair = param.split("=");
queryParams.put(pair[0], pair.length > 1 ? pair[1] : "");
}
logger.info("queryParams: {}", queryParams);
}
urlObj.setQuery(queryParams.toString());
String fragment = url.getRef();
urlObj.setFragment(fragment);
return urlObj;
}
List<String> countAndTrackRedirects(String urlString) throws IOException {
URL url = new URL(urlString);
List<String> redirectChain = new ArrayList<>();
redirectChain.add(urlString); // Add the initial URL to the chain
boolean redirected;
int redirectCount = 0;
do {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setInstanceFollowRedirects(false);
int responseCode = connection.getResponseCode();
redirected = (responseCode >= 300 && responseCode < 400);
if (redirected) {
String newUrl = connection.getHeaderField("Location");
if (newUrl == null) {
break;
}
// Handle relative URLs
if (!newUrl.startsWith("http://") && !newUrl.startsWith("https://")) {
newUrl = new URL(url, newUrl).toString();
}
url = new URL(newUrl);
redirectChain.add(newUrl);
redirectCount++;
logger.info("Redirect #{}: {}",redirectCount, newUrl);
}
connection.disconnect();
} while (redirected && redirectCount < CommonConstants.MAX_REDIRECT_COUNT);
return redirectChain;
}
public Mono<String> detectType(QRCodePayload payload) { public Mono<String> detectType(QRCodePayload payload) {
String data = payload.getData(); String data = payload.getData();

View File

@@ -1,12 +1,107 @@
package com.safeqr.app.qrcode.service; package com.safeqr.app.qrcode.service;
import com.safeqr.app.constants.CommonConstants;
import com.safeqr.app.qrcode.dto.QRCodePayload; import com.safeqr.app.qrcode.dto.QRCodePayload;
import com.safeqr.app.qrcode.dto.URLVerificationResponse; import com.safeqr.app.qrcode.dto.URLVerificationResponse;
import com.safeqr.app.qrcode.entity.QRCodeURL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.IOException;
import java.net.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service @Service
public class URLVerificationService { public class URLVerificationService {
private static final Logger logger = LoggerFactory.getLogger(URLVerificationService.class);
// Function to breakdown URL into subdomain, domain, topLevelDomain, query params, fragment
public QRCodeURL breakdownURL(String urlString) throws MalformedURLException, URISyntaxException {
URI uri = new URI(urlString);
URL url = uri.toURL();
QRCodeURL urlObj = new QRCodeURL();
String host = url.getHost();
// split host into subdomain, domain, topLevelDomain
String[] hostParts = host.split("\\.");
String subdomain = "";
if (hostParts.length >= 2) {
// set topLevelDomain to the last part of the host
urlObj.setTopLevelDomain(hostParts[hostParts.length - 1]);
// set domain to the second last part of the host
urlObj.setDomain(hostParts[hostParts.length - 2]);
// set subdomain to the first part of the host
if (hostParts.length > 2) {
subdomain = String.join(".", java.util.Arrays.copyOfRange(hostParts, 0, hostParts.length - 2));
}
}
// set subdomain to URL host
urlObj.setSubdomain(subdomain);
String path = url.getPath();
//set path to URL path if it's not empty, otherwise set it to root path
urlObj.setPath(path.isEmpty() ? "/" : path);
String query = url.getQuery();
Map<String, String> queryParams = new HashMap<>();
if (query != null) {
// split query params into key value pairs
for (String param : query.split("&")) {
String[] pair = param.split("=");
queryParams.put(pair[0], pair.length > 1 ? pair[1] : "");
}
logger.info("queryParams: {}", queryParams);
}
// set query params to URL query
urlObj.setQuery(queryParams.toString());
// set fragment to URL ref
urlObj.setFragment(url.getRef());
return urlObj;
}
List<String> countAndTrackRedirects(String urlString) throws IOException, URISyntaxException {
URI uri = new URI(urlString);
URL url = uri.toURL();
List<String> redirectChain = new ArrayList<>();
// Add the initial URL to the chain
redirectChain.add(urlString);
boolean redirected;
int redirectCount = 0;
do {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setInstanceFollowRedirects(false);
int responseCode = connection.getResponseCode();
redirected = (responseCode >= 300 && responseCode < 400);
// Handle redirects
if (redirected) {
// Location header contains the URL to redirect to
String newUrl = connection.getHeaderField("Location");
if (newUrl == null) {
break;
}
// Handle relative URLs
uri = uri.resolve(newUrl);
url = uri.toURL();
redirectChain.add(url.toString());
redirectCount++;
logger.info("Redirect #{}: {}",redirectCount, newUrl);
}
connection.disconnect();
} while (redirected && redirectCount < CommonConstants.MAX_REDIRECT_COUNT);
return redirectChain;
}
public URLVerificationResponse verifyURL(QRCodePayload payload) { public URLVerificationResponse verifyURL(QRCodePayload payload) {
URLVerificationResponse response = new URLVerificationResponse(); URLVerificationResponse response = new URLVerificationResponse();
try { try {