全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-18 8 分钟 ✍️ juanwangdev

Spring MVC 拦截器实现登录验证

登录验证是Web应用的核心安全功能,拦截器提供了统一的入口点实现全局登录检查。

Session登录验证

拦截器实现

Java
@Component
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("user") == null) {
            // 未登录,重定向到登录页
            response.sendRedirect("/login");
            return false;
        }

        // 已登录,将用户信息存入请求上下文
        User user = (User) session.getAttribute("user");
        request.setAttribute("currentUser", user);

        return true;
    }
}

配置拦截器

Java
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
            .addPathPatterns("/**")
            .excludePathPatterns(
                "/login",
                "/register",
                "/logout",
                "/static/**",
                "/public/**",
                "/error"
            );
    }
}

Token登录验证

拦截器实现

Java
@Component
public class TokenInterceptor implements HandlerInterceptor {

    @Autowired
    private TokenService tokenService;

    @Override
    public boolean preHandle(HttpServletRequest request,
                            HttpServletResponse response,
                            Object handler) throws Exception {
        // 从请求头获取Token
        String token = request.getHeader("Authorization");

        if (token == null || !token.startsWith("Bearer ")) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("{\"code\":401,\"message\":\"未登录\"}");
            return false;
        }

        // 验证Token
        token = token.substring(7);    // 移除"Bearer "前缀
        User user = tokenService.validateToken(token);

        if (user == null) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("{\"code\":401,\"message\":\"Token无效或已过期\"}");
            return false;
        }

        // 存入用户上下文
        UserContext.setUser(user);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler,
                                Exception ex) {
        UserContext.clear();    // 清理ThreadLocal
    }
}

Token服务实现

Java
@Service
public class TokenService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public String generateToken(User user) {
        String token = UUID.randomUUID().toString().replace("-", "");
        String key = "token:" + token;

        // Token有效期24小时
        redisTemplate.opsForValue().set(key, user, 24, TimeUnit.HOURS);

        return token;
    }

    public User validateToken(String token) {
        String key = "token:" + token;
        return (User) redisTemplate.opsForValue().get(key);
    }

    public void invalidateToken(String token) {
        String key = "token:" + token;
        redisTemplate.delete(key);
    }
}

用户上下文工具类

Java
public class UserContext {

    private static final ThreadLocal<User> userHolder = new ThreadLocal<>();

    public static void setUser(User user) {
        userHolder.set(user);
    }

    public static User getUser() {
        return userHolder.get();
    }

    public static Long getUserId() {
        User user = getUser();
        return user != null ? user.getId() : null;
    }

    public static void clear() {
        userHolder.remove();
    }
}

JWT登录验证

拦截器实现

Java
@Component
public class JwtInterceptor implements HandlerInterceptor {

    @Autowired
    private JwtUtil jwtUtil;

    @Override
    public boolean preHandle(HttpServletRequest request,
                            HttpServletResponse response,
                            Object handler) throws Exception {
        String token = request.getHeader("Authorization");

        if (token == null || !token.startsWith("Bearer ")) {
            throw new UnauthorizedException("未授权访问");
        }

        token = token.substring(7);

        try {
            Claims claims = jwtUtil.parseToken(token);
            Long userId = claims.get("userId", Long.class);
            String username = claims.get("username", String.class);

            // 验证用户是否存在
            User user = userService.findById(userId);
            if (user == null) {
                throw new UnauthorizedException("用户不存在");
            }

            UserContext.setUser(user);
            return true;

        } catch (ExpiredJwtException e) {
            throw new UnauthorizedException("Token已过期");
        } catch (Exception e) {
            throw new UnauthorizedException("Token无效");
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler,
                                Exception ex) {
        UserContext.clear();
    }
}

JWT工具类

Java
@Component
public class JwtUtil {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration:86400000}")
    private Long expiration;    // 默认24小时

    public String generateToken(User user) {
        return Jwts.builder()
            .setSubject(user.getUsername())
            .claim("userId", user.getId())
            .claim("username", user.getUsername())
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + expiration))
            .signWith(SignatureAlgorithm.HS256, secret)
            .compact();
    }

    public Claims parseToken(String token) {
        return Jwts.parser()
            .setSigningKey(secret)
            .parseClaimsJws(token)
            .getBody();
    }

    public boolean isTokenExpired(String token) {
        try {
            Claims claims = parseToken(token);
            return claims.getExpiration().before(new Date());
        } catch (ExpiredJwtException e) {
            return true;
        }
    }
}

注解式登录验证

定义注解

Java
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequireLogin {
    boolean required() default true;
}

拦截器实现

Java
@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                            HttpServletResponse response,
                            Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;

            // 检查方法上的注解
            RequireLogin methodAnnotation =
                handlerMethod.getMethodAnnotation(RequireLogin.class);

            // 检查类上的注解
            RequireLogin classAnnotation =
                handlerMethod.getBeanType().getAnnotation(RequireLogin.class);

            if (methodAnnotation != null && methodAnnotation.required()
                || classAnnotation != null && classAnnotation.required()) {
                // 验证登录状态
                return checkLogin(request, response);
            }
        }
        return true;
    }

    private boolean checkLogin(HttpServletRequest request,
                              HttpServletResponse response) throws Exception {
        String token = request.getHeader("Authorization");
        if (token == null) {
            response.setStatus(401);
            response.getWriter().write("{\"code\":401,\"message\":\"请先登录\"}");
            return false;
        }
        // 验证Token...
        return true;
    }
}

Controller中使用

Java
@RestController
@RequestMapping("/api")
public class UserController {

    // 需要登录
    @RequireLogin
    @GetMapping("/user/profile")
    public User getProfile() {
        return UserContext.getUser();
    }

    // 不需要登录
    @GetMapping("/public/info")
    public Map<String, Object> getPublicInfo() {
        return Collections.singletonMap("version", "1.0.0");
    }
}

白名单配置

Java
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor())
            .addPathPatterns("/**")
            .excludePathPatterns(
                "/login",
                "/register",
                "/logout",
                "/api/auth/**",
                "/api/public/**",
                "/swagger-ui/**",
                "/v3/api-docs/**",
                "/static/**",
                "/favicon.ico",
                "/error"
            );
    }
}

生产环境建议使用JWT + Redis实现登录验证,支持Token刷新和注销。

要点总结

  • Session验证通过检查session中的用户信息
  • Token验证从请求头获取并校验Token有效性
  • JWT验证无需服务端存储,适合分布式系统
  • 注解式验证灵活控制哪些方法需要登录
  • 必须配置excludePathPatterns排除公开接口
  • afterCompletion中清理ThreadLocal避免内存泄漏

📝 发现内容有误?点击此处直接编辑

← 上一篇 Spring MVC 拦截器实现权限控制
下一篇 → Spring MVC 拦截器概念与作用
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库