This commit is contained in:
Wowieee4
2026-04-23 11:38:29 +07:00
parent ff76abbbf6
commit f8ca77eeb9
12 changed files with 1240 additions and 457 deletions
@@ -9,6 +9,8 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import com.walkguide.security.JwtAuthFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@@ -20,27 +22,28 @@ public class SecurityConfig {
}
// Aturan jalan masuk ke API kita:
// Jangan lupa inject filter-nya di atas (tambahin parameter JwtAuthFilter jwtAuthFilter)
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
public SecurityFilterChain filterChain(HttpSecurity http, JwtAuthFilter jwtAuthFilter) throws Exception {
http
.cors(cors -> cors.configurationSource(request -> {
var corsConfiguration = new org.springframework.web.cors.CorsConfiguration();
// Izinkan semua origin localhost dan semua port (biar Chrome gak ngeblok)
corsConfiguration.setAllowedOriginPatterns(java.util.List.of(
"http://localhost:*",
"http://127.0.0.1:*"
));
corsConfiguration.setAllowedMethods(java.util.List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
corsConfiguration.setAllowedHeaders(java.util.List.of("*"));
corsConfiguration.setAllowCredentials(true);
return corsConfiguration;
var corsConfig = new org.springframework.web.cors.CorsConfiguration();
corsConfig.setAllowedOriginPatterns(java.util.List.of("http://localhost:*", "http://127.0.0.1:*"));
corsConfig.setAllowedMethods(java.util.List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
corsConfig.setAllowedHeaders(java.util.List.of("*"));
corsConfig.setAllowCredentials(true);
return corsConfig;
}))
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/auth/**", "/swagger-ui/**", "/v3/api-docs/**").permitAll() // Login & Swagger bebas
.requestMatchers("/api/guardian/**").hasRole("GUARDIAN") // Khusus Guardian
.requestMatchers("/api/user/**").hasRole("USER") // Khusus Tunanetra
.anyRequest().authenticated()
);
)
// TARUH SATPAM DI SINI
.addFilterBefore(jwtAuthFilter, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@@ -0,0 +1,43 @@
package com.walkguide.controller;
import java.util.Map;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.walkguide.dto.ApiResponse;
import com.walkguide.service.MockDataService;
import lombok.RequiredArgsConstructor;
@RestController
@RequestMapping("/api/guardian")
@RequiredArgsConstructor
public class GuardianController {
private final MockDataService mockDataService;
// 4. Ambil Status User
@GetMapping("/user-status")
public ResponseEntity<ApiResponse<Map<String, Object>>> getUserStatus() {
return ResponseEntity.ok(new ApiResponse<>(true, mockDataService.getUserStatus(), "Data status user berhasil diambil"));
}
// 5. Setting Hardware Shortcut
@PutMapping("/settings/shortcuts")
public ResponseEntity<ApiResponse<Map<String, Object>>> updateShortcuts(@RequestBody Map<String, Object> request) {
Map<String, Object> updated = mockDataService.updateShortcuts(request);
return ResponseEntity.ok(new ApiResponse<>(true, updated, "Shortcut berhasil diperbarui"));
}
// 6. Setting Sensitivitas AI
@PutMapping("/settings/ai")
public ResponseEntity<ApiResponse<Map<String, Object>>> updateAiSettings(@RequestBody Map<String, Object> request) {
Map<String, Object> updated = mockDataService.updateAiSettings(request);
return ResponseEntity.ok(new ApiResponse<>(true, updated, "Setting AI berhasil diperbarui"));
}
}
@@ -0,0 +1,24 @@
package com.walkguide.controller;
import java.util.Map;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.walkguide.dto.ApiResponse;
@RestController
@RequestMapping("/api/user")
public class UserController {
// 7. Sinyal Darurat (Voice Command)
@PostMapping("/emergency")
public ResponseEntity<ApiResponse<String>> triggerEmergency(@RequestBody Map<String, Object> request) {
// Simulasi mengirim notif ke Guardian
String triggerType = (String) request.get("triggerType");
return ResponseEntity.ok(new ApiResponse<>(true, "Darurat Terkirim", "Guardian telah diberi peringatan via: " + triggerType));
}
}
@@ -0,0 +1,57 @@
package com.walkguide.security;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.util.Collections;
@Component
@RequiredArgsConstructor
public class JwtAuthFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
final String authHeader = request.getHeader("Authorization");
// Kalau gak ada token, lewatin aja (biar dicegat sama SecurityConfig)
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
// Potong tulisan "Bearer "
final String jwt = authHeader.substring(7);
try {
// Ambil email & role dari token lu
String email = jwtUtil.extractUsername(jwt);
String role = jwtUtil.extractRole(jwt); // Pastiin JwtUtil lu punya fungsi extractRole!
// Daftarin user ini ke sistem keamanan Spring
if (email != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
email, null, Collections.singletonList(new SimpleGrantedAuthority(role))
);
SecurityContextHolder.getContext().setAuthentication(authToken);
}
} catch (Exception e) {
// Token kadaluarsa / rusak
System.out.println("JWT Error: " + e.getMessage());
}
filterChain.doFilter(request, response);
}
}
@@ -2,33 +2,67 @@ package com.walkguide.security;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
@Component
public class JwtUtil {
@Value("${jwt.secret}")
private String secret;
// Kunci rahasia buat enkripsi & dekripsi token (Minimal 256-bit)
private static final String SECRET_KEY = "404E635266556A586E3272357538782F413F4428472B4B6250645367566B5970";
@Value("${jwt.expiration}")
private Long expiration;
private Key getSigningKey() {
return Keys.hmacShaKeyFor(secret.getBytes());
// Fungsi tambahan buat ngebongkar Email (Username)
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
// Fungsi tambahan buat ngebongkar Role
public String extractRole(String token) {
Claims claims = extractAllClaims(token);
return claims.get("role", String.class);
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
private Claims extractAllClaims(String token) {
return Jwts.parserBuilder()
.setSigningKey(getSignInKey())
.build()
.parseClaimsJws(token)
.getBody();
}
private Key getSignInKey() {
byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY);
return Keys.hmacShaKeyFor(keyBytes);
}
// Fungsi lama lu buat bikin token
public String generateToken(String email, String role) {
Map<String, Object> claims = new HashMap<>();
claims.put("role", role);
return createToken(claims, email);
}
private String createToken(Map<String, Object> claims, String subject) {
return Jwts.builder()
.setSubject(email)
.claim("role", role)
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(getSigningKey())
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // Aktif 10 jam
.signWith(getSignInKey(), SignatureAlgorithm.HS256)
.compact();
}
}
@@ -0,0 +1,43 @@
package com.walkguide.service;
import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Service;
@Service
public class MockDataService {
// Tambahin 'final' di sini sesuai saran VS Code
private final Map<String, Object> userStatus = new HashMap<>();
private final Map<String, Object> hardwareShortcuts = new HashMap<>();
private final Map<String, Object> aiSettings = new HashMap<>();
public MockDataService() {
// Data default awal saat Spring Boot nyala
userStatus.put("status", "Sedang Berjalan");
userStatus.put("location", "Jl. Kenangan, SBY");
userStatus.put("batteryLevel", 85);
userStatus.put("lastSeen", "2026-04-23T08:40:00Z");
hardwareShortcuts.put("volumeUpAction", "accept_call");
hardwareShortcuts.put("volumeDownAction", "emergency_ping");
aiSettings.put("alertDistanceMeters", 2.5);
aiSettings.put("hapticFeedback", true);
}
public Map<String, Object> getUserStatus() {
return userStatus;
}
public Map<String, Object> updateShortcuts(Map<String, Object> newShortcuts) {
hardwareShortcuts.putAll(newShortcuts);
return hardwareShortcuts;
}
public Map<String, Object> updateAiSettings(Map<String, Object> newSettings) {
aiSettings.putAll(newSettings);
return aiSettings;
}
}