Master Unified Login, Exception, and Response Handling in Spring Boot with AOP and Interceptors

This article walks through building a Spring Boot application that centralizes user login verification, exception handling, and response formatting by leveraging AOP, custom HandlerInterceptors, @ControllerAdvice, and ResponseBodyAdvice to create clean, maintainable backend code.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
Master Unified Login, Exception, and Response Handling in Spring Boot with AOP and Interceptors

Introduction

The following sections implement three unified functions for a Spring Boot project: user login permission verification, unified data format return, and unified exception handling.

Unified user login permission verification

Unified data format return

Unified exception handling

1. User Login Permission Verification

Initially each controller method performed its own login check, leading to duplicated code, higher maintenance cost, and no clear separation from business logic.

1.1 Initial Login Verification

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

Problems of this approach:

Each method must repeat the same login check logic.

Adding more controllers increases modification and maintenance cost.

The login check is unrelated to business logic but must be written in every method.

A common AOP method is therefore required.

1.2 Spring AOP Login Verification Issues

Using a Spring AOP @Before or @Around advice seems natural, but two issues arise:

HttpSession cannot be obtained inside the advice.

Excluding specific endpoints (e.g., login and registration) from interception is cumbersome.

1.3 Spring Interceptor

Spring provides HandlerInterceptor to solve the above problems. Implementation steps:

Create a custom interceptor that implements HandlerInterceptor and overrides preHandle for login checks.

Register the interceptor in a class that implements WebMvcConfigurer via addInterceptors, specifying path patterns to include and exclude.

1.3.1 Preparation

package com.example.demo.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
    @RequestMapping("/login")
    public boolean login(HttpServletRequest request, String username, String password) {
        if (StringUtils.hasLength(username) && StringUtils.hasLength(password)) {
            if ("admin".equals(username) && "admin".equals(password)) {
                HttpSession session = request.getSession();
                session.setAttribute("userinfo", "admin");
                return true;
            } else {
                return false;
            }
        }
        return false;
    }
    @RequestMapping("/getinfo")
    public String getInfo() {
        log.debug("executed getinfo method");
        return "executed getinfo method";
    }
    @RequestMapping("/reg")
    public String reg() {
        log.debug("executed reg method");
        return "executed reg method";
    }
}

1.3.2 Custom Interceptor

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;
        }
        log.error("Current user has no access permission");
        response.setStatus(401);
        return false;
    }
}

1.3.3 Register Interceptor

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("/**")
                .excludePathPatterns("/user/login", "/user/reg");
    }
}

The flow before a controller is invoked now includes the interceptor's preHandle method.

Interceptor flow diagram
Interceptor flow diagram

2. Unified Exception Handling

Use @ControllerAdvice together with @ExceptionHandler to capture exceptions globally and return a consistent 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<String, Object> handleException(Exception e) {
        HashMap<String, Object> result = new HashMap<>();
        result.put("code", "-1");
        result.put("msg", e.getMessage());
        return result;
    }

    @ExceptionHandler(ArithmeticException.class)
    @ResponseBody
    public HashMap<String, Object> handleArithmetic(ArithmeticException e) {
        HashMap<String, Object> result = new HashMap<>();
        result.put("code", "-2");
        result.put("msg", e.getMessage());
        return result;
    }
}

When a controller throws an exception, the appropriate method in the advice class returns a predefined error map.

3. Unified Data Return Format

Wrap all controller responses into a standard JSON object using @ControllerAdvice + ResponseBodyAdvice.

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<Object> {
    @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 String return type
            return "{\"state\":1,\"msg\":\"\",\"data\":" + body + "}";
        }
        HashMap<String, Object> result = new HashMap<>();
        result.put("state", 1);
        result.put("msg", "");
        result.put("data", body);
        return result;
    }
}

For convenience, a utility class AjaxResult can be used to build the standard map manually.

package com.example.demo.common;

import java.util.HashMap;

public class AjaxResult {
    public static HashMap<String, Object> success(Object data) {
        HashMap<String, Object> result = new HashMap<>();
        result.put("code", 200);
        result.put("msg", "");
        result.put("data", data);
        return result;
    }
    public static HashMap<String, Object> fail(int code, String msg) {
        HashMap<String, Object> result = new HashMap<>();
        result.put("code", code);
        result.put("msg", msg);
        result.put("data", "");
        return result;
    }
    public static HashMap<String, Object> fail(int code, String msg, Object data) {
        HashMap<String, Object> result = new HashMap<>();
        result.put("code", code);
        result.put("msg", msg);
        result.put("data", data);
        return result;
    }
}

These components together provide a clean, maintainable way to enforce consistent login checks, error handling, and response structures across the entire Spring Boot application.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

aopException HandlingSpring BootInterceptorUnified response
Java High-Performance Architecture
Written by

Java High-Performance Architecture

Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.

0 followers
Reader feedback

How this landed with the community

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.