diff --git a/src/main/java/com/eactive/testmaster/config/AjaxAuthenticationFailureHandler.java b/src/main/java/com/eactive/testmaster/config/AjaxAuthenticationFailureHandler.java new file mode 100644 index 0000000..2789e3c --- /dev/null +++ b/src/main/java/com/eactive/testmaster/config/AjaxAuthenticationFailureHandler.java @@ -0,0 +1,42 @@ +package com.eactive.testmaster.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.stereotype.Component; + +@Component +public class AjaxAuthenticationFailureHandler implements AuthenticationFailureHandler { + + private final PortalAuthenticationFailureHandler delegate; + private final ObjectMapper objectMapper; + + public AjaxAuthenticationFailureHandler(PortalAuthenticationFailureHandler delegate, ObjectMapper objectMapper) { + this.delegate = delegate; + this.objectMapper = objectMapper; + } + + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, + AuthenticationException exception) throws IOException, ServletException { + if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) { + Map result = new HashMap<>(); + result.put("status", "ERROR"); + result.put("message", exception.getLocalizedMessage()); + + response.setStatus(HttpStatus.UNAUTHORIZED.value()); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + objectMapper.writeValue(response.getWriter(), result); + } else { + delegate.onAuthenticationFailure(request, response, exception); + } + } +} diff --git a/src/main/java/com/eactive/testmaster/config/AjaxAuthenticationSuccessHandler.java b/src/main/java/com/eactive/testmaster/config/AjaxAuthenticationSuccessHandler.java new file mode 100644 index 0000000..56d353d --- /dev/null +++ b/src/main/java/com/eactive/testmaster/config/AjaxAuthenticationSuccessHandler.java @@ -0,0 +1,54 @@ +package com.eactive.testmaster.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import org.springframework.http.MediaType; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.stereotype.Component; + +@Component +public class AjaxAuthenticationSuccessHandler implements AuthenticationSuccessHandler { + + private final PortalAuthenticationSuccessHandler delegate; + private final ObjectMapper objectMapper; + + public AjaxAuthenticationSuccessHandler(PortalAuthenticationSuccessHandler delegate, ObjectMapper objectMapper) { + this.delegate = delegate; + this.objectMapper = objectMapper; + } + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) throws IOException, ServletException { + if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) { + Map result = new HashMap<>(); + HttpSession session = request.getSession(); + + // Check for special conditions (reusing logic from original handler) + if (session.getAttribute("dormantAccount") != null) { + result.put("status", "DORMANT"); + result.put("message", session.getAttribute("success")); + result.put("redirectUrl", session.getAttribute("redirectUrl")); + } else if (session.getAttribute("passwordExpired") != null) { + result.put("status", "PASSWORD_EXPIRED"); + result.put("message", session.getAttribute("success")); + result.put("redirectUrl", session.getAttribute("redirectUrl")); + } else { + result.put("status", "SUCCESS"); + result.put("redirectUrl", request.getContextPath() + "/"); + } + + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + objectMapper.writeValue(response.getWriter(), result); + } else { + delegate.onAuthenticationSuccess(request, response, authentication); + } + } +} diff --git a/src/main/java/com/eactive/testmaster/config/PortalConfigSecurity.java b/src/main/java/com/eactive/testmaster/config/PortalConfigSecurity.java index 14c1cae..91710d4 100644 --- a/src/main/java/com/eactive/testmaster/config/PortalConfigSecurity.java +++ b/src/main/java/com/eactive/testmaster/config/PortalConfigSecurity.java @@ -25,15 +25,18 @@ public class PortalConfigSecurity { private final PortalAuthenticationFailureHandler authenticationFailureHandler; private final PortalAuthenticationSuccessHandler authenticationSuccessHandler; + private final AjaxAuthenticationSuccessHandler ajaxAuthenticationSuccessHandler; + private final AjaxAuthenticationFailureHandler ajaxAuthenticationFailureHandler; private final PortalAuthenticationManager portalAuthenticationManager; - private final CsrfExclusionService csrfExclusionService; - public PortalConfigSecurity(PortalAuthenticationFailureHandler authenticationFailureHandler, PortalAuthenticationSuccessHandler authenticationSuccessHandler, PortalAuthenticationManager portalAuthenticationManager, - CsrfExclusionService csrfExclusionService) { + public PortalConfigSecurity(PortalAuthenticationFailureHandler authenticationFailureHandler, PortalAuthenticationSuccessHandler authenticationSuccessHandler, AjaxAuthenticationSuccessHandler ajaxAuthenticationSuccessHandler, + AjaxAuthenticationFailureHandler ajaxAuthenticationFailureHandler, PortalAuthenticationManager portalAuthenticationManager, CsrfExclusionService csrfExclusionService) { this.authenticationFailureHandler = authenticationFailureHandler; this.authenticationSuccessHandler = authenticationSuccessHandler; + this.ajaxAuthenticationSuccessHandler = ajaxAuthenticationSuccessHandler; + this.ajaxAuthenticationFailureHandler = ajaxAuthenticationFailureHandler; this.portalAuthenticationManager = portalAuthenticationManager; this.csrfExclusionService = csrfExclusionService; } @@ -79,16 +82,29 @@ public class PortalConfigSecurity { .usernameParameter("id") .passwordParameter("password") .loginProcessingUrl("/actionLogin.do") - .failureHandler(authenticationFailureHandler) - .successHandler(authenticationSuccessHandler)) + .failureHandler((request, response, exception) -> { + if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) { + ajaxAuthenticationFailureHandler.onAuthenticationFailure(request, response, exception); + } else { + authenticationFailureHandler.onAuthenticationFailure(request, response, exception); + } + }) + .successHandler((request, response, authentication) -> { + if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) { + ajaxAuthenticationSuccessHandler.onAuthenticationSuccess(request, response, authentication); + } else { + authenticationSuccessHandler.onAuthenticationSuccess(request, response, authentication); + } + })) .logout(logout -> logout .logoutRequestMatcher(new AntPathRequestMatcher("/actionLogout.do", "GET")) .logoutSuccessUrl("/")) .csrf(csrf -> csrf - .csrfTokenRepository(csrfTokenRepository(csrfExclusionService)) - .ignoringRequestMatchers(request -> csrfExclusionService.shouldExclude(request.getServletPath())) - .ignoringAntMatchers("/h2-console/**") - ).headers(headers -> headers.frameOptions().sameOrigin()) + .csrfTokenRepository(csrfTokenRepository(csrfExclusionService)) + .ignoringRequestMatchers(request -> csrfExclusionService.shouldExclude(request.getServletPath())) + .ignoringAntMatchers("/h2-console/**") + ) + .headers(headers -> headers.frameOptions().sameOrigin()) .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .sessionFixation()