Spring Boot Login Authentication: Filters, Interceptors, AOP, ThreadLocal, and Parameter Resolver
This article presents a comprehensive Spring Boot tutorial on implementing non‑intrusive login authentication using three techniques—Filter, Interceptor, and AOP with custom annotations—plus extensions such as ThreadLocal storage and a Spring MVC argument resolver, complete with project setup, Maven dependencies, and test screenshots.
1. Login Authentication Overview
In modern front‑back end projects, authentication is often performed by issuing a token after a successful login, which the client sends via cookie or header on each request. The backend validates the token before executing business logic, keeping authentication logic separate from business code.
2. Non‑Intrusive Implementation Methods
Spring provides three ways to achieve non‑intrusive login and permission checks:
Java Web Filter
Spring MVC Interceptor
Spring AOP combined with a custom annotation
3. Project Structure and Prerequisites
The sample project contains a UserController with three endpoints: login, query users, and a test endpoint. After a successful login, a UUID token is generated and stored in Redis with a 30‑minute expiration.
public class LoginFilter implements Filter {
private final RedisTemplate
redisTemplate;
private final LoginProperties loginProperties;
// ... init, doFilter, returnNoLogin, destroy methods ...
}4. Implementation Details
4.1 Filter Implementation
The LoginFilter checks the request URI against an exclusion list, retrieves the token from the request header, validates it against Redis, renews the token expiration, and returns a 401 JSON response if validation fails.
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Resource
private LoginProperties loginProperties;
@Bean
public FilterRegistrationBean
loginFilterRegistration() {
FilterRegistrationBean
registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new LoginFilter(redisTemplate, loginProperties));
registrationBean.setName("loginFilter");
registrationBean.addUrlPatterns(loginProperties.getFilterIncludeUrl().toArray(new String[0]));
registrationBean.setOrder(-1);
return registrationBean;
}
}4.2 Interceptor Implementation
The LoginInterception interceptor performs the same token validation logic in its preHandle method and returns a 401 response when necessary.
@Component
public class LoginInterception implements HandlerInterceptor {
@Resource
private RedisTemplate
redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader(Constant.TOKEN_HEADER_NAME);
// token validation logic ...
return true;
}
// ... returnNoLogin method ...
}4.3 AOP + Custom Annotation
A custom annotation @LoginValidator marks methods or classes that require login validation. The LoginAspect defines a pointcut for this annotation and performs token validation before proceeding.
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LoginValidator {
boolean validated() default true;
}
@Aspect
@Component
public class LoginAspect {
@Pointcut("@annotation(xyz.hlh.annotition.LoginValidator) || @within(xyz.hlh.annotition.LoginValidator)")
public void pointCut() {}
@Around("pointCut()")
public Object before(ProceedingJoinPoint joinpoint) throws Throwable {
// token validation logic ...
return joinpoint.proceed();
}
}5. Extensions
5.1 ThreadLocal Storage
A LoginUserThread class holds the authenticated User object in a ThreadLocal , allowing downstream code to access the user without passing it explicitly.
public class LoginUserThread {
private static final ThreadLocal
LOGIN_USER = new ThreadLocal<>();
public static User get() { return LOGIN_USER.get(); }
public void put(User user) { LOGIN_USER.set(user); }
public void remove() { LOGIN_USER.remove(); }
}5.2 Spring MVC Parameter Resolver
A custom annotation @LoginUser and a corresponding LoginUserResolver allow controller methods to receive the logged‑in User directly as a method argument.
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LoginUser {}
@Component
public class LoginUserResolver implements HandlerMethodArgumentResolver {
@Resource
private RedisTemplate
redisTemplate;
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(LoginUser.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String token = request.getHeader(Constant.TOKEN_HEADER_NAME);
return token != null ? redisTemplate.opsForValue().get(Constant.REDIS_USER_PREFIX + token) : null;
}
}6. Execution Order
If all three mechanisms (Filter, Interceptor, AOP) are present, the processing order is: Filter → Interceptor → AOP.
7. Maven Dependencies (pom.xml excerpt)
org.springframework.boot
spring-boot-dependencies
2.5.2
pom
import
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-aop
org.springframework.boot
spring-boot-starter-data-redis
org.projectlombok
lombok8. Testing
Postman is used to test the login endpoint, user list retrieval, and access control. Screenshots demonstrate 401 responses for unauthenticated requests and successful responses after login.
Overall, the article provides a step‑by‑step guide to building a reusable, non‑intrusive authentication layer in Spring Boot, covering configuration, code implementation, extensions, and verification.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
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.