Implementing Unified User Login, Exception Handling, and Data Response in Spring Boot with AOP and Interceptors
This article demonstrates how to use Spring Boot AOP and HandlerInterceptor to create a unified user login permission check, standardized exception handling with @ControllerAdvice, and consistent JSON response formatting, providing step‑by‑step code examples and configuration details for building robust backend services.
The article introduces a Spring Boot module that unifies three core functions: user login permission verification, uniform data format return, and centralized exception handling, using AOP and interceptor techniques.
1. User login permission validation
Initially, each controller method contains repetitive login checks, which leads to maintenance overhead. The original implementation looks like this:
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/m1")
public Object method(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null && session.getAttribute("userinfo") != null) {
return true; // logged in
} else {
return false; // not logged in
}
}
// other methods ...
}Because every method repeats this logic, a common AOP solution is considered.
1.2 Spring AOP approach
Using a @Before or @Around advice to perform the check, the skeleton code is:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class UserAspect {
@Pointcut("execution(* com.example.demo.controller..*.*(..))")
public void pointcut() {}
@Before("pointcut()")
public void doBefore() {}
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Around method start");
Object obj = joinPoint.proceed();
System.out.println("Around method end");
return obj;
}
}Two major problems appear: the HttpSession object cannot be accessed inside the advice, and it is difficult to exclude specific endpoints such as login and registration from interception.
1.3 Spring Interceptor solution
Spring provides the HandlerInterceptor interface, which allows pre‑handling of requests before the controller is invoked.
Custom interceptor implementation:
package com.example.demo.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/** Login interceptor */
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession(false);
if (session != null && session.getAttribute("userinfo") != null) {
return true; // allow
}
log.error("Current user has no access permission");
response.setStatus(401);
return false; // block
}
}Registering the interceptor in a WebMvcConfigurer implementation:
package com.example.demo.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**") // intercept all
.excludePathPatterns("/user/login", "/user/reg"); // exclude login & registration
}
}The addPathPatterns method defines which URLs are intercepted, while excludePathPatterns lists the URLs that bypass the interceptor.
1.4 Interceptor execution principle
During request processing, DispatcherServlet calls applyPreHandle , which iterates over all registered HandlerInterceptor instances and invokes their preHandle methods. If any interceptor returns false , the request processing stops.
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
if (!mappedHandler.applyPreHandle(request, response)) {
return; // interceptor blocked the request
}
// controller execution
// ...
}The source of applyPreHandle shows the loop over the interceptor list and the call to preHandle :
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = (HandlerInterceptor) this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
}
return true;
}2. Unified exception handling
Using @ControllerAdvice together with @ExceptionHandler , the application can catch all exceptions and return a uniform JSON structure:
package com.example.demo.config;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
@ControllerAdvice
public class ErrorAdvice {
@ExceptionHandler(Exception.class)
@ResponseBody
public HashMap
exceptionAdvice(Exception e) {
HashMap
result = new HashMap<>();
result.put("code", "-1");
result.put("msg", e.getMessage());
return result;
}
@ExceptionHandler(ArithmeticException.class)
@ResponseBody
public HashMap
arithmeticAdvice(ArithmeticException e) {
HashMap
result = new HashMap<>();
result.put("code", "-2");
result.put("msg", e.getMessage());
return result;
}
}Additional examples show handling of NullPointerException and custom messages, illustrating how the most specific handler is chosen.
3. Unified data response format
To ensure every API returns a consistent JSON payload, a ResponseBodyAdvice implementation wraps the original body into a standard structure:
package com.example.demo.common;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.util.HashMap;
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice
{
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof HashMap) {
return body; // already wrapped
}
if (body instanceof String) {
// special handling for plain strings
return new com.fasterxml.jackson.databind.ObjectMapper()
.writeValueAsString(AjaxResult.success(body));
}
return AjaxResult.success(body);
}
}The utility class AjaxResult provides static methods success and fail to build the standard map with fields code , msg , and data :
package com.example.demo.common;
import java.util.HashMap;
public class AjaxResult {
public static HashMap
success(Object data) {
HashMap
result = new HashMap<>();
result.put("code", 200);
result.put("msg", "");
result.put("data", data);
return result;
}
public static HashMap
fail(int code, String msg) {
HashMap
result = new HashMap<>();
result.put("code", code);
result.put("msg", msg);
result.put("data", "");
return result;
}
}With these components, the application achieves a clean separation of concerns: authentication logic lives in the interceptor, error handling is centralized via @ControllerAdvice , and every response conforms to a predictable JSON schema, simplifying front‑end integration and future maintenance.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.