Backend Development 20 min read

Implementing Unified User Login Validation, Exception Handling, and Data Return Format in Spring Boot

This article demonstrates how to consolidate user login verification, exception handling, and response formatting in a Spring Boot application by using AOP, HandlerInterceptor, @ControllerAdvice, and ResponseBodyAdvice, providing complete code examples and explaining the underlying request processing flow.

Top Architect
Top Architect
Top Architect
Implementing Unified User Login Validation, Exception Handling, and Data Return Format in Spring Boot

In this tutorial a senior architect explains how to achieve three goals in a Spring Boot project: unified user login permission verification, unified data format return, and unified exception handling.

1. Unified User Login Validation

The initial implementation places login checks inside each controller method, which leads to duplicated code, higher maintenance cost, and no clear separation from business logic.

@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
        }
    }
    @RequestMapping("/m2")
    public Object method2(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute("userinfo") != null) {
            return true;
        } else {
            return false;
        }
    }
    // other methods ...
}

Because each method repeats the same logic, a common AOP solution is proposed.

1.2 Spring AOP Approach

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) {
        Object obj = null;
        System.out.println("Around method start");
        try {
            obj = joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("Around method end");
        return obj;
    }
}

The AOP solution cannot obtain the HttpSession object and cannot easily exclude login or registration endpoints, making it unsuitable for this scenario.

1.3 Spring Interceptor Solution

Spring provides HandlerInterceptor which can intercept requests before they reach the controller.

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
    }
}

The interceptor is registered 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
    }
}

During request processing Spring's DispatcherServlet calls applyPreHandle which executes the preHandle method of all registered interceptors, effectively performing the login check before the controller method runs.

2. Unified Exception Handling

Exceptions are centrally handled using @ControllerAdvice combined with @ExceptionHandler :

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;
    }
}

Specific exception handlers can return different error codes and messages, and the advice applies to the whole project.

3. Unified Data Return Format

Two approaches are shown. The first uses @ControllerAdvice + ResponseBodyAdvice to wrap every response into a standard JSON structure:

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;

/** Unified response wrapper */
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice
{
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true; // apply to all responses
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                               Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        HashMap
result = new HashMap<>();
        result.put("state", 1);
        result.put("msg", "");
        result.put("data", body);
        return result;
    }
}

If the controller returns a String , the advice converts the wrapper to a JSON string to avoid type mismatches.

A softer alternative is to define a utility class AjaxResult and let developers return AjaxResult.success(...) or AjaxResult.fail(...) explicitly:

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;
    }
    // overloads with custom message or data omitted for brevity
}

Using this class keeps the response format consistent while giving developers control over when to apply it.

Finally, the article includes several diagrams (embedded as <img src="..."> ) illustrating the request flow with and without interceptors, and encourages readers to join a discussion group for further exchange.

Backend DevelopmentException HandlingSpring BootInterceptorUnified Response
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.