You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring如何实现仅在Session Cookie有效时允许访问,避免重复校验

问题描述

我希望给API的所有功能添加认证保护,目前已经实现了Session Cookie的发送,但不想在每个接口函数里重复校验Cookie的有效性,有没有更优的实现方案?

注:以下代码仅用于测试,暂未处理明文密码的问题。

登录代码

@RestController
@EnableSpringHttpSession
public class OnlineMapping {
    @Autowired
    private UserRepository userRepository;
    @PostMapping(path = "/online")
    public ResponseEntity<?> onlineRequest(@RequestBody OnlineRequest onlineRequest, HttpSession session) {
        User user;
        user = userRepository.findUserByUsernameAndPassword(onlineRequest.username, onlineRequest.password);
        if (user!=null){
            user.setLatestTimeStamp(System.currentTimeMillis());

            return new ResponseEntity<>("You are now online, Enjoy!", HttpStatus.OK);
        } else {
            session.setAttribute("valid", false);
            session.invalidate();
            return new ResponseEntity<>("Invalid login", HttpStatus.valueOf(401));
        }
    }
}

示例接口函数

public ResponseEntity<?> createMessage(@RequestBody MessageCreateRequest messageCreateRequest, HttpSession session) {
    if (session.getAttribute("valid").equals(true)){ // 不想写这段重复代码
        Message m = new Message();
        m.setContent(messageCreateRequest.content);
        m.setSenderID(messageCreateRequest.senderID);
        m.setChannelID(messageCreateRequest.channelID);
        m.setTimeStamp(System.currentTimeMillis());
        messageRepository.save(m);
        return new ResponseEntity<>("Message created!", HttpStatus.OK);
    }//
    return new ResponseEntity<>("Invalid Session", HttpStatus.UNAUTHORIZED);
}
解决方案

在Spring生态里,有三种主流方式可以避免重复编写Session校验逻辑:

1. 用Spring Security实现自动Session认证

Spring Security原生支持基于Session的认证,配置后会自动拦截需要保护的接口,无需手动写校验代码:

配置步骤:

  • 引入Spring Security依赖(若未添加)
  • 编写Security配置类:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            // 配置免认证路径(比如登录接口)
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/online").permitAll()
                .anyRequest().authenticated()
            )
            // 开启Session管理,配置失效处理
            .sessionManagement(session -> session
                .invalidSessionStrategy((req, res) -> {
                    res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                    res.getWriter().write("Invalid Session");
                })
            )
            // 前后端分离API可禁用CSRF(根据实际场景调整)
            .csrf(csrf -> csrf.disable());
        return http.build();
    }
}
  • 改造登录接口,复用Spring Security的认证逻辑:
@RestController
public class OnlineMapping {
    @Autowired
    private AuthenticationManager authenticationManager;

    @PostMapping("/online")
    public ResponseEntity<?> login(@RequestBody OnlineRequest request) {
        try {
            // 触发Spring Security认证流程
            Authentication auth = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(request.username, request.password)
            );
            SecurityContextHolder.getContext().setAuthentication(auth);
            // 认证成功后Spring自动创建并维护Session
            return new ResponseEntity<>("You are now online, Enjoy!", HttpStatus.OK);
        } catch (AuthenticationException e) {
            return new ResponseEntity<>("Invalid login", HttpStatus.UNAUTHORIZED);
        }
    }
}

配置完成后,除了/online之外的所有接口都会自动校验Session有效性,无需在业务代码中重复判断。

2. 自定义拦截器统一校验Session

如果不想引入Spring Security,可通过自定义拦截器实现全局Session校验:

实现步骤:

  • 编写拦截器类:
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;

public class SessionAuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 排除登录接口
        if ("/online".equals(request.getRequestURI())) {
            return true;
        }
        // 校验Session有效性
        Boolean valid = (Boolean) request.getSession().getAttribute("valid");
        if (valid == null || !valid) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("Invalid Session");
            return false;
        }
        return true;
    }
}
  • 注册拦截器到Spring容器:
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 WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SessionAuthInterceptor())
            .addPathPatterns("/**") // 拦截所有路径
            .excludePathPatterns("/online"); // 排除登录接口
    }
}

这样所有接口请求都会被拦截器统一校验Session,业务接口无需再写重复代码。

3. 自定义注解+AOP实现灵活认证控制

如果需要更细粒度的认证控制(比如部分接口需要认证,部分不需要),可以用自定义注解配合AOP实现:

实现步骤:

  • 定义认证注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequireAuth {
}
  • 编写AOP切面处理校验逻辑:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import jakarta.servlet.http.HttpSession;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Aspect
@Component
public class AuthAspect {

    @Around("@annotation(RequireAuth)")
    public Object checkAuth(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取当前请求的Session
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpSession session = attributes.getRequest().getSession();
        Boolean valid = (Boolean) session.getAttribute("valid");
        if (valid == null || !valid) {
            return new ResponseEntity<>("Invalid Session", HttpStatus.UNAUTHORIZED);
        }
        // 校验通过,执行原业务方法
        return joinPoint.proceed();
    }
}
  • 在需要认证的接口上添加注解:
@RequireAuth
public ResponseEntity<?> createMessage(@RequestBody MessageCreateRequest messageCreateRequest) {
    // 直接编写业务逻辑,无需处理Session校验
    Message m = new Message();
    m.setContent(messageCreateRequest.content);
    m.setSenderID(messageCreateRequest.senderID);
    m.setChannelID(messageCreateRequest.channelID);
    m.setTimeStamp(System.currentTimeMillis());
    messageRepository.save(m);
    return new ResponseEntity<>("Message created!", HttpStatus.OK);
}

这种方式可以精准控制哪些接口需要认证,灵活性更高。

内容的提问来源于stack exchange,提问作者Alex_X1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 22:30:54