Password stats implemented
password now passed in body instead of parametr
This commit is contained in:
@@ -3,6 +3,7 @@ package iz._11a.passmetric;
|
||||
import iz._11a.passmetric.model.PasswordLeakResult;
|
||||
import iz._11a.passmetric.service.BruteForceService;
|
||||
import iz._11a.passmetric.service.PasswordLeakService;
|
||||
import iz._11a.passmetric.service.PasswordStatisticsService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@@ -24,7 +25,7 @@ public class PasswordController {
|
||||
|
||||
@PostMapping(path = "/api/password/strength")
|
||||
@ResponseBody
|
||||
public Map<String, Object> checkPasswordLive(@RequestParam("password") String password) {
|
||||
public Map<String, Object> checkPasswordLive(@RequestBody String password) {
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
|
||||
@@ -38,6 +39,7 @@ public class PasswordController {
|
||||
response.put("tips", tips);
|
||||
response.put("leaked", leakResult.isLeaked() ? "Hasło wyciekło " + leakResult.getCount() +" razy" : "Hasło nie występuje w wyciekach");
|
||||
response.put("timetohack", BruteForceService.estimateTimeToHackFormatted(password));
|
||||
response.put("stats", PasswordStatisticsService.getPasswordStatistics(password));
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package iz._11a.passmetric.service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class PasswordStatisticsService {
|
||||
|
||||
public static Map<String, Object> getPasswordStatistics(String password) {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
|
||||
if (password == null || password.isEmpty()) {
|
||||
stats.put("length", 0);
|
||||
stats.put("lowercaseCount", 0);
|
||||
stats.put("uppercaseCount", 0);
|
||||
stats.put("digitCount", 0);
|
||||
stats.put("specialCount", 0);
|
||||
return stats;
|
||||
}
|
||||
|
||||
int lowercaseCount = 0;
|
||||
int uppercaseCount = 0;
|
||||
int digitCount = 0;
|
||||
int specialCount = 0;
|
||||
|
||||
for (char c : password.toCharArray()) {
|
||||
if (Character.isLowerCase(c)) {
|
||||
lowercaseCount++;
|
||||
} else if (Character.isUpperCase(c)) {
|
||||
uppercaseCount++;
|
||||
} else if (Character.isDigit(c)) {
|
||||
digitCount++;
|
||||
} else {
|
||||
specialCount++;
|
||||
}
|
||||
}
|
||||
|
||||
stats.put("length", password.length());
|
||||
stats.put("lowercaseCount", lowercaseCount);
|
||||
stats.put("uppercaseCount", uppercaseCount);
|
||||
stats.put("digitCount", digitCount);
|
||||
stats.put("specialCount", specialCount);
|
||||
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
@@ -219,3 +219,68 @@ ul#tipsList li {
|
||||
margin: 0.3rem 0.5rem;
|
||||
}
|
||||
|
||||
/*stats*/
|
||||
|
||||
.stats-container {
|
||||
width: min(92vw, 540px);
|
||||
margin: 16px auto 0;
|
||||
padding: 16px;
|
||||
border-radius: 12px;
|
||||
border: 1.5px solid var(--border);
|
||||
background: var(--panel);
|
||||
backdrop-filter: blur(12px);
|
||||
display: none;
|
||||
}
|
||||
|
||||
.stats-title {
|
||||
margin: 0 0 12px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
background: rgba(148, 163, 184, 0.1);
|
||||
border-radius: 8px;
|
||||
transition: background 160ms ease;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.stat-item {
|
||||
background: rgba(51, 65, 85, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
.stat-item:hover {
|
||||
background: rgba(148, 163, 184, 0.15);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.stat-item:hover {
|
||||
background: rgba(51, 65, 85, 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 13px;
|
||||
color: var(--muted);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,33 @@
|
||||
<div id="strengthBarFill"></div>
|
||||
</div>
|
||||
|
||||
<div id="statsContainer" class="stats-container">
|
||||
<h3 class="stats-title">Statystyki hasła</h3>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Długość:</span>
|
||||
<span class="stat-value" id="statLength">-</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Małe litery:</span>
|
||||
<span class="stat-value" id="statLowercase">-</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Wielkie litery:</span>
|
||||
<span class="stat-value" id="statUppercase">-</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Cyfry:</span>
|
||||
<span class="stat-value" id="statDigits">-</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Znaki specjalne:</span>
|
||||
<span class="stat-value" id="statSpecial">-</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="message-wrapper">
|
||||
<p id="liveMessage" class="message" aria-live="polite"></p>
|
||||
<p id="leakMessage" class="message" aria-live="polite"></p>
|
||||
@@ -36,6 +63,7 @@
|
||||
const tipsList = document.getElementById('tipsList');
|
||||
const leakOut = document.getElementById('leakMessage');
|
||||
const timeToHackOut = document.getElementById('timeToHackMessage');
|
||||
const statsContainer = document.getElementById('statsContainer');
|
||||
|
||||
// ukryj przy pierwszym załadowaniu strony
|
||||
tipsList.style.display = 'none';
|
||||
@@ -56,6 +84,7 @@
|
||||
tipsList.innerHTML = "";
|
||||
tipsList.style.display = 'none';
|
||||
timeToHackOut.textContent = '';
|
||||
statsContainer.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -63,8 +92,8 @@
|
||||
try {
|
||||
const resp = await fetch('/api/password/strength', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
body: new URLSearchParams({password: val})
|
||||
headers: {'Content-Type': 'text/plain'},
|
||||
body: val,
|
||||
});
|
||||
if (!resp.ok) {
|
||||
out.textContent = 'Błąd sprawdzania';
|
||||
@@ -140,6 +169,23 @@
|
||||
timeToHackOut.classList.remove("ok");
|
||||
}
|
||||
|
||||
// stats
|
||||
if (data.stats) {
|
||||
document.getElementById('statLength').textContent = data.stats.length || 0;
|
||||
document.getElementById('statLowercase').textContent =
|
||||
data.stats.lowercaseCount || 0;
|
||||
document.getElementById('statUppercase').textContent =
|
||||
data.stats.uppercaseCount || 0;
|
||||
document.getElementById('statDigits').textContent =
|
||||
data.stats.digitCount || 0;
|
||||
document.getElementById('statSpecial').textContent =
|
||||
data.stats.specialCount || 0;
|
||||
|
||||
statsContainer.style.display = 'block';
|
||||
} else {
|
||||
statsContainer.style.display = 'none';
|
||||
}
|
||||
|
||||
} catch {
|
||||
out.textContent = 'Błąd sieci';
|
||||
leakOut.textContent = '';
|
||||
|
||||
Reference in New Issue
Block a user