Spring Security自定义访问控制:拒绝访问时强制登录后继续请求
嘿,我来帮你搞定这个Spring Security自定义访问控制的需求!结合你给出的AccessManager,咱们一步步实现「满足条件就放行,不满足就跳登录、登录后回到原操作」的逻辑:
实现思路
核心逻辑是:在用户已认证的前提下,用自定义过滤器拦截请求,调用你的AccessManager检查条件;如果不满足,就保存当前请求的URL,重定向到登录页;用户登录成功后,Spring Security会自动跳回之前保存的请求路径,继续执行原操作。
步骤1:完善你的AccessManager
首先给AccessManager的方法添加HttpServletRequest参数,这样你可以获取当前请求的路径、参数,或者结合当前登录用户信息做判断:
@Component("accessManager") public class AccessManager { public boolean hasAccess(HttpServletRequest request) { // 这里替换成你的实际判断逻辑 // 示例:检查当前用户的会话状态、请求路径是否符合规则等 Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null && auth.isAuthenticated()) { // 比如判断用户是否有特定属性,或者请求是否是特定接口 return false; } return false; } }
步骤2:编写自定义访问控制过滤器
创建一个OncePerRequestFilter的实现,集成AccessManager来处理访问控制逻辑,同时负责保存原请求、重定向登录:
@Component public class CustomAccessControlFilter extends OncePerRequestFilter { @Autowired private AccessManager accessManager; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 排除登录页、静态资源等无需检查的路径,避免循环重定向 String path = request.getServletPath(); if (path.startsWith("/login") || path.startsWith("/css/") || path.startsWith("/js/")) { filterChain.doFilter(request, response); return; } // 先检查用户是否已登录,未登录直接交给Spring Security处理跳转登录 Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth == null || !auth.isAuthenticated() || "anonymousUser".equals(auth.getPrincipal())) { response.sendRedirect(request.getContextPath() + "/login"); return; } // 调用自定义AccessManager检查条件 if (!accessManager.hasAccess(request)) { // 保存当前请求到Session,Spring Security登录成功后会自动跳回这个请求 request.getSession().setAttribute("SPRING_SECURITY_SAVED_REQUEST", new DefaultSavedRequest(request)); // 重定向到登录页 response.sendRedirect(request.getContextPath() + "/login"); return; } // 满足条件,继续执行后续请求链 filterChain.doFilter(request, response); } }
步骤3:配置Spring Security过滤链
把自定义过滤器加到Spring Security的过滤链中,同时配置登录页、登录成功跳转逻辑:
@Configuration @EnableWebSecurity public class SecurityConfig { @Autowired private CustomAccessControlFilter customAccessControlFilter; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth // 允许登录页和静态资源匿名访问 .requestMatchers("/login", "/css/**", "/js/**").permitAll() // 其他所有请求需要先认证 .anyRequest().authenticated() ) .formLogin(form -> form .loginPage("/login") // 配置你的自定义登录页路径 .permitAll() // 登录成功后,Spring Security会优先跳回之前保存的请求,没有的话跳默认页 .defaultSuccessUrl("/") ) .logout(logout -> logout .permitAll() ) // 把自定义过滤器加到UsernamePasswordAuthenticationFilter之前,确保登录后先检查我们的规则 .addFilterBefore(customAccessControlFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } }
可选:方法级安全集成
如果你想在Controller方法上更灵活地使用这个规则,可以配置方法级安全,用@PreAuthorize调用你的AccessManager:
// 在SecurityConfig里添加这个Bean @Bean public MethodSecurityExpressionHandler methodSecurityExpressionHandler() { DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler(); // 把AccessManager注册到表达式处理器中 handler.setPermissionEvaluator(new CustomPermissionEvaluator(accessManager)); return handler; } // 自定义PermissionEvaluator,用于衔接方法级注解和AccessManager static class CustomPermissionEvaluator implements PermissionEvaluator { private final AccessManager accessManager; public CustomPermissionEvaluator(AccessManager accessManager) { this.accessManager = accessManager; } @Override public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) { // 获取当前请求 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); return accessManager.hasAccess(request); } @Override public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) { // 按需实现基于资源ID的权限判断,不需要可以返回false或抛出异常 return false; } }
然后在Controller方法上使用:
@PreAuthorize("hasPermission(null, null)") @GetMapping("/your-protected-path") public String protectedPage() { return "protected"; }
关键细节提醒
- 保存原请求:我们用
SPRING_SECURITY_SAVED_REQUEST这个Session属性,Spring Security的默认登录成功处理器会自动读取它,跳回用户之前想要访问的页面,不用自己额外处理跳转逻辑。 - 过滤器顺序:把自定义过滤器放在
UsernamePasswordAuthenticationFilter之前,确保用户登录后,先经过我们的自定义规则检查,再执行后续的授权逻辑。 - 排除路径:一定要排除登录页和静态资源,否则会出现「检查不满足→跳登录→再检查→再跳登录」的循环。
内容的提问来源于stack exchange,提问作者Sviatlana
相关产品推荐
相关产品推荐

