Introduction
Identity-Aware Proxy (IAP) is a security layer that provides centralized authentication and authorization for applications. In Java, IAP enables secure access control without modifying application code by leveraging context-aware security policies.
Architecture Overview
1. IAP Integration Architecture
User Request → IAP → Authentication → Authorization → Java Application ↓ ↓ ↓ ↓ ↓ Browser Google IAP OAuth/SSO Context Policies Business Logic ↓ ↓ ↓ ↓ ↓ HTTPS JWT Token User Identity Access Control @Secure
Setup and Dependencies
1. Maven Dependencies
<properties> <spring-boot.version>3.1.0</spring-boot.version> <google-cloud.version>2.28.0</google-cloud.version> <nimbus-jose.version>9.31</nimbus-jose.version> </properties> <dependencies> <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${spring-boot.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <version>${spring-boot.version}</version> </dependency> <!-- Google Cloud IAP --> <dependency> <groupId>com.google.cloud</groupId> <artifactId>google-cloud-iap</artifactId> <version>2.28.0</version> </dependency> <!-- JWT Libraries --> <dependency> <groupId>com.nimbusds</groupId> <artifactId>nimbus-jose-jwt</artifactId> <version>${nimbus-jose.version}</version> </dependency> <!-- OAuth2 Client --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-client</artifactId> <version>${spring-boot.version}</version> </dependency> <!-- Spring Security OAuth2 Resource Server --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-resource-server</artifactId> <version>${spring-boot.version}</version> </dependency> <!-- Servlet API for request wrappers --> <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <version>6.0.0</version> <scope>provided</scope> </dependency> </dependencies> Core IAP Implementation
1. IAP Configuration Properties
@Configuration @ConfigurationProperties(prefix = "iap") @Data @Validated public class IapProperties { @NotNull private GoogleCloudConfig googleCloud = new GoogleCloudConfig(); @NotNull private SecurityConfig security = new SecurityConfig(); @NotNull private JwtConfig jwt = new JwtConfig(); @Data public static class GoogleCloudConfig { private String projectNumber; private String projectId; private String backendServiceId; private String audience; } @Data public static class SecurityConfig { private boolean enabled = true; private boolean requireIap = true; private List<String> allowedDomains = new ArrayList<>(); private List<String> excludedPaths = List.of("/health", "/info", "/public/**"); private String redirectUrl = "/login"; } @Data public static class JwtConfig { private String issuer = "https://cloud.google.com/iap"; private String headerName = "x-goog-iap-jwt-assertion"; private String publicKeyUrl = "https://www.gstatic.com/iap/verify/public_key"; private long clockSkewSeconds = 300; } } 2. IAP JWT Token Verifier
@Service @Slf4j public class IapTokenVerifier { private final IapProperties iapProperties; private final JwtDecoder jwtDecoder; private final ObjectMapper objectMapper; public IapTokenVerifier(IapProperties iapProperties, ObjectMapper objectMapper) { this.iapProperties = iapProperties; this.objectMapper = objectMapper; this.jwtDecoder = createJwtDecoder(); } /** * Verify IAP JWT token and extract user identity */ public IapUserIdentity verifyToken(String jwtToken) { try { Jwt jwt = jwtDecoder.decode(jwtToken); // Validate token claims validateTokenClaims(jwt); // Extract user identity return extractUserIdentity(jwt); } catch (Exception e) { log.error("IAP token verification failed", e); throw new IapAuthenticationException("Token verification failed", e); } } /** * Verify token from HTTP request headers */ public IapUserIdentity verifyRequest(HttpServletRequest request) { String jwtToken = extractTokenFromRequest(request); if (jwtToken == null) { throw new IapAuthenticationException("IAP JWT token not found in request"); } return verifyToken(jwtToken); } /** * Validate token without throwing exception */ public boolean isValidToken(String jwtToken) { try { verifyToken(jwtToken); return true; } catch (IapAuthenticationException e) { return false; } } private JwtDecoder createJwtDecoder() { return NimbusJwtDecoder.withJwkSetUri(iapProperties.getJwt().getPublicKeyUrl()).build(); } private void validateTokenClaims(Jwt jwt) { String issuer = jwt.getIssuer().toString(); String audience = getAudienceClaim(jwt); String expectedAudience = iapProperties.getGoogleCloud().getAudience(); // Validate issuer if (!iapProperties.getJwt().getIssuer().equals(issuer)) { throw new IapAuthenticationException("Invalid token issuer: " + issuer); } // Validate audience if (expectedAudience != null && !expectedAudience.equals(audience)) { throw new IapAuthenticationException("Invalid token audience: " + audience); } // Validate expiration Instant expiration = jwt.getExpiresAt(); if (expiration != null && expiration.isBefore(Instant.now())) { throw new IapAuthenticationException("Token has expired"); } log.debug("IAP token validation successful for user: {}", getEmailClaim(jwt)); } private IapUserIdentity extractUserIdentity(Jwt jwt) { return IapUserIdentity.builder() .id(getSubjectClaim(jwt)) .email(getEmailClaim(jwt)) .name(getClaimAsString(jwt, "name")) .picture(getClaimAsString(jwt, "picture")) .hd(getClaimAsString(jwt, "hd")) // hosted domain .issuer(jwt.getIssuer().toString()) .audience(getAudienceClaim(jwt)) .issuedAt(jwt.getIssuedAt()) .expiresAt(jwt.getExpiresAt()) .claims(jwt.getClaims()) .build(); } private String extractTokenFromRequest(HttpServletRequest request) { // Check IAP-specific header first String token = request.getHeader(iapProperties.getJwt().getHeaderName()); // Fallback to Authorization header if (token == null) { String authHeader = request.getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Bearer ")) { token = authHeader.substring(7); } } return token; } // Helper methods to extract claims private String getSubjectClaim(Jwt jwt) { return jwt.getSubject(); } private String getEmailClaim(Jwt jwt) { return getClaimAsString(jwt, "email"); } private String getAudienceClaim(Jwt jwt) { Object audience = jwt.getClaims().get("aud"); if (audience instanceof String) { return (String) audience; } else if (audience instanceof List) { @SuppressWarnings("unchecked") List<String> audiences = (List<String>) audience; return audiences.get(0); // Return first audience } return null; } private String getClaimAsString(Jwt jwt, String claimName) { Object claimValue = jwt.getClaims().get(claimName); return claimValue != null ? claimValue.toString() : null; } } @Data @Builder @NoArgsConstructor @AllArgsConstructor public class IapUserIdentity { private String id; private String email; private String name; private String picture; private String hd; // hosted domain private String issuer; private String audience; private Instant issuedAt; private Instant expiresAt; private Map<String, Object> claims; public String getDomain() { if (email != null && email.contains("@")) { return email.substring(email.indexOf('@') + 1); } return null; } public boolean isFromDomain(String domain) { return domain != null && domain.equals(getDomain()); } } Spring Security Integration
1. IAP Security Configuration
@Configuration @EnableWebSecurity @EnableMethodSecurity(prePostEnabled = true) @Slf4j public class IapSecurityConfig { private final IapProperties iapProperties; private final IapTokenVerifier iapTokenVerifier; public IapSecurityConfig(IapProperties iapProperties, IapTokenVerifier iapTokenVerifier) { this.iapProperties = iapProperties; this.iapTokenVerifier = iapTokenVerifier; } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(authz -> authz .requestMatchers(getExcludedPaths()).permitAll() .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt.jwtAuthenticationConverter(iapJwtAuthenticationConverter())) ) .addFilterBefore(iapAuthenticationFilter(), BasicAuthenticationFilter.class) .exceptionHandling(exceptions -> exceptions .authenticationEntryPoint(iapAuthenticationEntryPoint()) ); return http.build(); } @Bean public IapAuthenticationFilter iapAuthenticationFilter() { return new IapAuthenticationFilter(iapTokenVerifier, iapProperties); } @Bean public IapAuthenticationEntryPoint iapAuthenticationEntryPoint() { return new IapAuthenticationEntryPoint(); } @Bean public Converter<Jwt, AbstractAuthenticationToken> iapJwtAuthenticationConverter() { return new IapJwtAuthenticationConverter(); } @Bean public IapSecurityContextRepository iapSecurityContextRepository() { return new IapSecurityContextRepository(iapTokenVerifier); } private String[] getExcludedPaths() { List<String> excludedPaths = iapProperties.getSecurity().getExcludedPaths(); return excludedPaths.toArray(new String[0]); } } @Component @Slf4j public class IapAuthenticationFilter extends OncePerRequestFilter { private final IapTokenVerifier iapTokenVerifier; private final IapProperties iapProperties; public IapAuthenticationFilter(IapTokenVerifier iapTokenVerifier, IapProperties iapProperties) { this.iapTokenVerifier = iapTokenVerifier; this.iapProperties = iapProperties; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // Skip IAP authentication for excluded paths if (isExcludedPath(request)) { filterChain.doFilter(request, response); return; } try { // Verify IAP token and extract user identity IapUserIdentity userIdentity = iapTokenVerifier.verifyRequest(request); // Create authentication token IapAuthenticationToken authentication = new IapAuthenticationToken(userIdentity); // Set security context SecurityContextHolder.getContext().setAuthentication(authentication); log.debug("IAP authentication successful for user: {}", userIdentity.getEmail()); } catch (IapAuthenticationException e) { log.warn("IAP authentication failed: {}", e.getMessage()); if (iapProperties.getSecurity().isRequireIap()) { SecurityContextHolder.clearContext(); throw e; } // If IAP is not required, continue without authentication } filterChain.doFilter(request, response); } private boolean isExcludedPath(HttpServletRequest request) { String path = request.getServletPath(); return iapProperties.getSecurity().getExcludedPaths().stream() .anyMatch(excludedPath -> { if (excludedPath.endsWith("/**")) { String basePath = excludedPath.substring(0, excludedPath.length() - 3); return path.startsWith(basePath); } return path.equals(excludedPath); }); } } @Component public class IapJwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> { @Override public AbstractAuthenticationToken convert(Jwt jwt) { IapUserIdentity userIdentity = extractUserIdentity(jwt); return new IapAuthenticationToken(userIdentity); } private IapUserIdentity extractUserIdentity(Jwt jwt) { return IapUserIdentity.builder() .id(jwt.getSubject()) .email(jwt.getClaimAsString("email")) .name(jwt.getClaimAsString("name")) .picture(jwt.getClaimAsString("picture")) .hd(jwt.getClaimAsString("hd")) .issuer(jwt.getIssuer().toString()) .audience(jwt.getClaimAsString("aud")) .issuedAt(jwt.getIssuedAt()) .expiresAt(jwt.getExpiresAt()) .claims(jwt.getClaims()) .build(); } } @Component @Slf4j public class IapAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { log.warn("Authentication required for: {} - {}", request.getRequestURI(), authException.getMessage()); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.setContentType("application/json"); Map<String, Object> errorResponse = new HashMap<>(); errorResponse.put("timestamp", Instant.now().toString()); errorResponse.put("status", HttpServletResponse.SC_UNAUTHORIZED); errorResponse.put("error", "Unauthorized"); errorResponse.put("message", "IAP authentication required"); errorResponse.put("path", request.getRequestURI()); if (authException instanceof IapAuthenticationException iapException) { errorResponse.put("details", iapException.getMessage()); } ObjectMapper objectMapper = new ObjectMapper(); response.getWriter().write(objectMapper.writeValueAsString(errorResponse)); } } public class IapAuthenticationToken extends AbstractAuthenticationToken { private final IapUserIdentity userIdentity; public IapAuthenticationToken(IapUserIdentity userIdentity) { super(Collections.emptyList()); this.userIdentity = userIdentity; setAuthenticated(true); } @Override public Object getCredentials() { return null; // Credentials are handled by IAP } @Override public Object getPrincipal() { return userIdentity; } public IapUserIdentity getUserIdentity() { return userIdentity; } } @Component @Slf4j public class IapSecurityContextRepository implements SecurityContextRepository { private final IapTokenVerifier iapTokenVerifier; public IapSecurityContextRepository(IapTokenVerifier iapTokenVerifier) { this.iapTokenVerifier = iapTokenVerifier; } @Override public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) { return SecurityContextHolder.createEmptyContext(); } @Override public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) { // Stateless - no context saving needed } @Override public boolean containsContext(HttpServletRequest request) { try { iapTokenVerifier.verifyRequest(request); return true; } catch (IapAuthenticationException e) { return false; } } } Authorization and Access Control
1. Context-Aware Authorization Service
@Service @Slf4j public class IapAuthorizationService { private final IapProperties iapProperties; private final AccessPolicyRepository accessPolicyRepository; public IapAuthorizationService(IapProperties iapProperties, AccessPolicyRepository accessPolicyRepository) { this.iapProperties = iapProperties; this.accessPolicyRepository = accessPolicyRepository; } /** * Check if user has access to the resource */ public boolean hasAccess(IapUserIdentity user, HttpServletRequest request) { String path = request.getRequestURI(); String method = request.getMethod(); // Check domain restrictions if (!isDomainAllowed(user)) { log.warn("Domain not allowed for user: {} from domain: {}", user.getEmail(), user.getDomain()); return false; } // Check access policies AccessPolicy policy = accessPolicyRepository.findPolicyForPath(path); if (policy != null) { return evaluateAccessPolicy(user, policy, method); } // Default: allow access if authenticated return true; } /** * Check if user has specific role */ public boolean hasRole(IapUserIdentity user, String role) { // Extract roles from claims or external source List<String> userRoles = extractUserRoles(user); return userRoles.contains(role); } /** * Check if user has permission for specific action */ public boolean hasPermission(IapUserIdentity user, String resource, String action) { Permission permission = Permission.builder() .resource(resource) .action(action) .build(); return evaluatePermission(user, permission); } /** * Get user's accessible resources */ public List<String> getAccessibleResources(IapUserIdentity user) { return accessPolicyRepository.findAccessibleResources(user); } /** * Context-aware access check with additional context */ public boolean hasAccessWithContext(IapUserIdentity user, AccessContext context) { // Implement context-aware access logic // Consider time, location, device, etc. // Time-based access if (context.getAccessTime() != null && !isWithinAccessHours(context.getAccessTime())) { return false; } // Location-based access if (context.getUserLocation() != null && !isAllowedLocation(context.getUserLocation())) { return false; } // Device-based access if (context.getUserDevice() != null && !isAllowedDevice(context.getUserDevice())) { return false; } return true; } private boolean isDomainAllowed(IapUserIdentity user) { List<String> allowedDomains = iapProperties.getSecurity().getAllowedDomains(); if (allowedDomains.isEmpty()) { return true; // No domain restrictions } String userDomain = user.getDomain(); return userDomain != null && allowedDomains.contains(userDomain); } private boolean evaluateAccessPolicy(IapUserIdentity user, AccessPolicy policy, String method) { // Check if method is allowed if (!policy.getAllowedMethods().contains(method)) { return false; } // Check role requirements if (!policy.getRequiredRoles().isEmpty()) { List<String> userRoles = extractUserRoles(user); if (policy.getRequiredRoles().stream().noneMatch(userRoles::contains)) { return false; } } // Check domain restrictions if (!policy.getAllowedDomains().isEmpty()) { String userDomain = user.getDomain(); if (userDomain == null || !policy.getAllowedDomains().contains(userDomain)) { return false; } } return true; } private List<String> extractUserRoles(IapUserIdentity user) { // Extract roles from JWT claims or external service @SuppressWarnings("unchecked") List<String> roles = (List<String>) user.getClaims().get("roles"); return roles != null ? roles : List.of("USER"); } private boolean evaluatePermission(IapUserIdentity user, Permission permission) { // Implement permission evaluation logic // This could integrate with external policy engines return accessPolicyRepository.hasPermission(user, permission); } private boolean isWithinAccessHours(Instant accessTime) { // Implement time-based access logic LocalTime time = LocalTime.ofInstant(accessTime, ZoneId.systemDefault()); return !time.isBefore(LocalTime.of(8, 0)) && !time.isAfter(LocalTime.of(18, 0)); } private boolean isAllowedLocation(String location) { // Implement location-based access logic List<String> allowedLocations = List.of("US", "CA", "EU"); return allowedLocations.contains(location.toUpperCase()); } private boolean isAllowedDevice(String device) { // Implement device-based access logic return !"mobile".equalsIgnoreCase(device); // Example: block mobile devices } } @Service @Slf4j public class AccessPolicyRepository { private final Map<String, AccessPolicy> pathPolicies = new ConcurrentHashMap<>(); private final Map<String, List<Permission>> userPermissions = new ConcurrentHashMap<>(); public AccessPolicyRepository() { initializeDefaultPolicies(); } public AccessPolicy findPolicyForPath(String path) { return pathPolicies.entrySet().stream() .filter(entry -> path.matches(entry.getKey())) .map(Map.Entry::getValue) .findFirst() .orElse(null); } public List<String> findAccessibleResources(IapUserIdentity user) { return pathPolicies.entrySet().stream() .filter(entry -> { AccessPolicy policy = entry.getValue(); return policy.getRequiredRoles().stream() .anyMatch(role -> hasRole(user, role)); }) .map(Map.Entry::getKey) .collect(Collectors.toList()); } public boolean hasPermission(IapUserIdentity user, Permission permission) { List<Permission> permissions = userPermissions.get(user.getEmail()); return permissions != null && permissions.contains(permission); } public void addPolicy(String pathPattern, AccessPolicy policy) { pathPolicies.put(pathPattern, policy); } public void grantPermission(String userEmail, Permission permission) { userPermissions.computeIfAbsent(userEmail, k -> new ArrayList<>()) .add(permission); } private void initializeDefaultPolicies() { // Admin API policies addPolicy("/api/admin/.*", AccessPolicy.builder() .allowedMethods(List.of("GET", "POST", "PUT", "DELETE")) .requiredRoles(List.of("ADMIN")) .allowedDomains(List.of("company.com")) .build()); // User API policies addPolicy("/api/users/.*", AccessPolicy.builder() .allowedMethods(List.of("GET", "POST", "PUT")) .requiredRoles(List.of("USER", "ADMIN")) .allowedDomains(List.of("company.com")) .build()); // Public API policies addPolicy("/api/public/.*", AccessPolicy.builder() .allowedMethods(List.of("GET")) .requiredRoles(Collections.emptyList()) .allowedDomains(Collections.emptyList()) .build()); } private boolean hasRole(IapUserIdentity user, String role) { @SuppressWarnings("unchecked") List<String> roles = (List<String>) user.getClaims().get("roles"); return roles != null && roles.contains(role); } } @Data @Builder @NoArgsConstructor @AllArgsConstructor public class AccessPolicy { private List<String> allowedMethods; private List<String> requiredRoles; private List<String> allowedDomains; private List<String> allowedIps; private Map<String, Object> conditions; } @Data @Builder @NoArgsConstructor @AllArgsConstructor public class Permission { private String resource; private String action; private Map<String, Object> conditions; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Permission that = (Permission) o; return Objects.equals(resource, that.resource) && Objects.equals(action, that.action); } @Override public int hashCode() { return Objects.hash(resource, action); } } @Data @Builder @NoArgsConstructor @AllArgsConstructor public class AccessContext { private Instant accessTime; private String userLocation; private String userDevice; private String clientIp; private Map<String, Object> additionalContext; } Custom Annotations and Aspect-Oriented Security
1. Custom Security Annotations
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface IapSecured { String[] roles() default {}; String[] domains() default {}; String condition() default ""; } @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface IapPreAuthorize { String value(); } @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface IapPostAuthorize { String value(); } @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface IapContextAware { String[] requiredContext() default {}; } @Component @Aspect @Slf4j public class IapSecurityAspect { private final IapAuthorizationService authorizationService; public IapSecurityAspect(IapAuthorizationService authorizationService) { this.authorizationService = authorizationService; } @Around("@annotation(iapSecured)") public Object checkIapSecurity(ProceedingJoinPoint joinPoint, IapSecured iapSecured) throws Throwable { IapUserIdentity user = getCurrentUser(); if (user == null) { throw new IapAuthenticationException("User not authenticated"); } // Check roles if (iapSecured.roles().length > 0) { boolean hasRequiredRole = Arrays.stream(iapSecured.roles()) .anyMatch(role -> authorizationService.hasRole(user, role)); if (!hasRequiredRole) { throw new IapAuthorizationException("Insufficient roles"); } } // Check domains if (iapSecured.domains().length > 0) { boolean hasRequiredDomain = Arrays.stream(iapSecured.domains()) .anyMatch(domain -> user.isFromDomain(domain)); if (!hasRequiredDomain) { throw new IapAuthorizationException("Domain not allowed"); } } return joinPoint.proceed(); } @Around("@annotation(iapContextAware)") public Object checkContextAwareSecurity(ProceedingJoinPoint joinPoint, IapContextAware iapContextAware) throws Throwable { IapUserIdentity user = getCurrentUser(); if (user == null) { throw new IapAuthenticationException("User not authenticated"); } // Build access context from method parameters AccessContext accessContext = buildAccessContext(joinPoint); if (!authorizationService.hasAccessWithContext(user, accessContext)) { throw new IapAuthorizationException("Context-based access denied"); } return joinPoint.proceed(); } private IapUserIdentity getCurrentUser() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null && authentication.getPrincipal() instanceof IapUserIdentity) { return (IapUserIdentity) authentication.getPrincipal(); } return null; } private AccessContext buildAccessContext(ProceedingJoinPoint joinPoint) { // Extract context information from method parameters and request HttpServletRequest request = getCurrentRequest(); return AccessContext.builder() .accessTime(Instant.now()) .clientIp(getClientIp(request)) .userDevice(request.getHeader("User-Agent")) .additionalContext(extractMethodContext(joinPoint)) .build(); } private HttpServletRequest getCurrentRequest() { RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); if (requestAttributes instanceof ServletRequestAttributes servletRequestAttributes) { return servletRequestAttributes.getRequest(); } return null; } private String getClientIp(HttpServletRequest request) { if (request == null) return "unknown"; String xForwardedFor = request.getHeader("X-Forwarded-For"); if (xForwardedFor != null && !xForwardedFor.isEmpty()) { return xForwardedFor.split(",")[0].trim(); } return request.getRemoteAddr(); } private Map<String, Object> extractMethodContext(ProceedingJoinPoint joinPoint) { Map<String, Object> context = new HashMap<>(); // Extract method parameters for context Object[] args = joinPoint.getArgs(); String[] paramNames = getParameterNames(joinPoint); if (paramNames != null) { for (int i = 0; i < args.length; i++) { context.put(paramNames[i], args[i]); } } return context; } private String[] getParameterNames(ProceedingJoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); return signature.getParameterNames(); } } REST API Controllers with IAP Security
1. Secure REST Controllers
@RestController @RequestMapping("/api") @Slf4j public class SecureUserController { private final UserService userService; private final IapAuthorizationService authorizationService; public SecureUserController(UserService userService, IapAuthorizationService authorizationService) { this.userService = userService; this.authorizationService = authorizationService; } @GetMapping("/users") @IapSecured(roles = {"USER", "ADMIN"}) public ResponseEntity<List<User>> getUsers() { IapUserIdentity currentUser = getCurrentUser(); log.info("User {} accessed users list", currentUser.getEmail()); List<User> users = userService.getAllUsers(); return ResponseEntity.ok(users); } @GetMapping("/users/{id}") @IapSecured(roles = {"USER", "ADMIN"}) public ResponseEntity<User> getUser(@PathVariable String id) { IapUserIdentity currentUser = getCurrentUser(); // Context-aware authorization if (!currentUser.getEmail().equals(id) && !authorizationService.hasRole(currentUser, "ADMIN")) { throw new IapAuthorizationException("Cannot access other user's data"); } User user = userService.getUserById(id); return ResponseEntity.ok(user); } @PostMapping("/users") @IapSecured(roles = {"ADMIN"}, domains = {"company.com"}) public ResponseEntity<User> createUser(@RequestBody CreateUserRequest request) { IapUserIdentity currentUser = getCurrentUser(); log.info("Admin {} creating user: {}", currentUser.getEmail(), request.getEmail()); User user = userService.createUser(request); return ResponseEntity.status(HttpStatus.CREATED).body(user); } @PutMapping("/users/{id}") @IapSecured(roles = {"ADMIN"}) @IapContextAware(requiredContext = {"businessHours"}) public ResponseEntity<User> updateUser(@PathVariable String id, @RequestBody UpdateUserRequest request) { IapUserIdentity currentUser = getCurrentUser(); User user = userService.updateUser(id, request); return ResponseEntity.ok(user); } @GetMapping("/profile") public ResponseEntity<UserProfile> getProfile() { IapUserIdentity currentUser = getCurrentUser(); UserProfile profile = userService.getUserProfile(currentUser.getEmail()); return ResponseEntity.ok(profile); } @GetMapping("/admin/stats") @IapSecured(roles = {"ADMIN"}, domains = {"company.com"}) public ResponseEntity<AdminStats> getAdminStats() { IapUserIdentity currentUser = getCurrentUser(); log.info("Admin {} accessed statistics", currentUser.getEmail()); AdminStats stats = userService.getAdminStatistics(); return ResponseEntity.ok(stats); } @GetMapping("/context-test") @IapContextAware(requiredContext = {"location=US", "device=desktop"}) public ResponseEntity<String> contextAwareEndpoint() { return ResponseEntity.ok("Context-aware access granted"); } private IapUserIdentity getCurrentUser() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null && authentication.getPrincipal() instanceof IapUserIdentity) { return (IapUserIdentity) authentication.getPrincipal(); } throw new IapAuthenticationException("User not authenticated"); } } @RestController @RequestMapping("/api/public") @Slf4j public class PublicController { @GetMapping("/health") public ResponseEntity<Map<String, String>> health() { Map<String, String> status = new HashMap<>(); status.put("status", "UP"); status.put("timestamp", Instant.now().toString()); return ResponseEntity.ok(status); } @GetMapping("/info") public ResponseEntity<Map<String, String>> info() { Map<String, String> info = new HashMap<>(); info.put("name", "IAP Protected Application"); info.put("version", "1.0.0"); return ResponseEntity.ok(info); } } Monitoring and Auditing
1. IAP Audit Service
```java
@Service
@Slf4j
public class IapAuditService {
private final MeterRegistry meterRegistry; private final Counter authenticationCounter; private final Counter authorizationCounter; public IapAuditService(MeterRegistry meterRegistry) { this.meterRegistry = meterRegistry; this.authenticationCounter = Counter.builder("iap.authentication.attempts") .description("IAP authentication attempts") .register(meterRegistry); this.authorizationCounter = Counter.builder("iap.authorization.checks") .description("IAP authorization checks") .register(meterRegistry); } public void logAuthenticationSuccess(IapUserIdentity user, HttpServletRequest request) { authenticationCounter.increment(); log.info("IAP Authentication SUCCESS - User: {}, IP: {}, Path: {}", user.getEmail(), getClientIp(request), request.getRequestURI()); // Record detailed metrics meterRegistry.counter("iap.authentication.success", "user", user.getEmail(), "domain", user.getDomain()) .increment(); } public void logAuthenticationFailure(String reason, HttpServletRequest request) { authenticationCounter.increment(); log.warn("IAP Authentication FAILURE - Reason: {}, IP: {}, Path: {}", reason, getClientIp(request), request.getRequestURI()); meterRegistry.counter("iap.authentication.failure", "reason", reason) .increment(); } public void logAuthorizationSuccess(IapUserIdentity user, String resource, String action) { authorizationCounter.increment(); log.debug("IAP Authorization SUCCESS - User: {}, Resource: {}, Action: {}", user.getEmail(), resource, action); meterRegistry.counter("iap.authorization.success", "user", user.getEmail(), "resource", resource, "action", action) .increment(); } public void logAuthorizationFailure(IapUserIdentity user, String resource, String action, String reason) { authorizationCounter.increment(); log.warn("IAP Authorization FAILURE - User: {}, Resource: {}, Action: {}, Reason: {}", user.getEmail(), resource, action, reason); meterRegistry.counter("iap.authorization.failure", "user", user.getEmail(), "resource", resource, "action", action, "reason", reason) .increment Java Logistics, Shipping Integration & Enterprise Inventory Automation (Tracking, ERP, RFID & Billing Systems)
https://macronepal.com/blog/aftership-tracking-in-java-enterprise-package-visibility/
Explains how to integrate AfterShip tracking services into Java applications to provide real-time shipment visibility, delivery status updates, and centralized tracking across multiple courier services.
https://macronepal.com/blog/shipping-integration-using-fedex-api-with-java-for-logistics-automation/
Explains how to integrate the FedEx API into Java systems to automate shipping tasks such as creating shipments, calculating delivery costs, generating shipping labels, and tracking packages.
https://macronepal.com/blog/shipping-and-logistics-integrating-ups-apis-with-java-applications/
Explains UPS API integration in Java to enable automated shipping operations including rate calculation, shipment scheduling, tracking, and delivery confirmation management.
https://macronepal.com/blog/generating-and-reading-qr-codes-for-products-in-java/
Explains how Java applications generate and read QR codes for product identification, tracking, and authentication, supporting faster inventory handling and product verification processes.
https://macronepal.com/blog/designing-a-robust-pick-and-pack-workflow-in-java/
Explains how to design an efficient pick-and-pack workflow in Java warehouse systems, covering order processing, item selection, packaging steps, and logistics preparation to improve fulfillment efficiency.
https://macronepal.com/blog/rfid-inventory-management-system-in-java-a-complete-guide/
Explains how RFID technology integrates with Java applications to automate inventory tracking, reduce manual errors, and enable real-time stock monitoring in warehouses and retail environments.
https://macronepal.com/blog/erp-integration-with-odoo-in-java/
Explains how Java applications connect with Odoo ERP systems to synchronize inventory, orders, customer records, and financial data across enterprise systems.
https://macronepal.com/blog/automated-invoice-generation-creating-professional-excel-invoices-with-apache-poi-in-java/
Explains how to automatically generate professional Excel invoices in Java using Apache POI, enabling structured billing documents and automated financial record creation.
https://macronepal.com/blog/enterprise-financial-integration-using-quickbooks-api-in-java-applications/
Explains QuickBooks API integration in Java to automate financial workflows such as invoice management, payment tracking, accounting synchronization, and financial reporting.