Spring MVC+Security环境下验证用户登录状态并管控AJAX接口权限
嘿,这个场景我太熟了!基于Spring MVC + Spring Security的项目里,防护AJAX接口其实就是把Spring Security的拦截能力和AJAX的响应特性结合起来,我给你梳理一套落地的方案:
核心防护思路
核心是要区分AJAX请求和普通请求的响应逻辑:普通请求可以重定向到登录页/403页面,但AJAX请求需要返回JSON格式的状态码和提示,避免前端出现跳转异常。下面分步骤实现:
1. 配置Spring Security的基础拦截规则
首先在你的SecurityConfig里,明确指定哪些AJAX接口需要认证/特定角色权限。假设你的AJAX接口都放在/api/**路径下,示例代码如下:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() // 所有AJAX接口必须先登录 .antMatchers("/api/**").authenticated() // 特定接口仅允许ADMIN角色访问 .antMatchers("/api/admin/**").hasRole("ADMIN") // 其他静态资源/页面允许匿名访问 .anyRequest().permitAll() .and() // 保留你原有的登录/登出配置 .formLogin() .loginPage("/login") .permitAll() .and() .logout() .permitAll(); } }
2. 自定义未认证请求的处理逻辑(AuthenticationEntryPoint)
Spring Security默认会把未认证请求重定向到登录页,但这对AJAX请求不友好。我们需要写一个自定义处理器,检测到AJAX请求时返回401状态码+JSON提示:
@Component public class AjaxAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { // 通过请求头判断是否为AJAX请求 if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) { response.setContentType("application/json;charset=UTF-8"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.getWriter().write("{\"code\":401,\"message\":\"请先登录系统\"}"); } else { // 非AJAX请求还是走默认的登录页重定向 response.sendRedirect(request.getContextPath() + "/login"); } } }
然后把这个处理器配置到SecurityConfig里:
@Autowired private AjaxAuthenticationEntryPoint ajaxAuthenticationEntryPoint; @Override protected void configure(HttpSecurity http) throws Exception { http .exceptionHandling() .authenticationEntryPoint(ajaxAuthenticationEntryPoint) .and() // 其他原有配置... }
3. 处理权限不足的情况(AccessDeniedHandler)
如果用户已经登录,但没有对应角色的权限,默认会返回403页面。同样,我们需要针对AJAX请求返回JSON提示:
@Component public class AjaxAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException { if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) { response.setContentType("application/json;charset=UTF-8"); response.setStatus(HttpServletResponse.SC_FORBIDDEN); response.getWriter().write("{\"code\":403,\"message\":\"您没有权限访问该接口\"}"); } else { // 非AJAX请求跳转到自定义403页面 response.sendRedirect(request.getContextPath() + "/403"); } } }
同样把这个处理器配置到SecurityConfig:
@Autowired private AjaxAccessDeniedHandler ajaxAccessDeniedHandler; @Override protected void configure(HttpSecurity http) throws Exception { http .exceptionHandling() .authenticationEntryPoint(ajaxAuthenticationEntryPoint) .accessDeniedHandler(ajaxAccessDeniedHandler) .and() // 其他原有配置... }
4. 前端AJAX请求的统一响应处理
前端需要配合后端的状态码,做统一的提示或跳转。比如用jQuery的ajaxSetup全局处理:
$.ajaxSetup({ complete: function(xhr) { if (xhr.status === 401) { alert("登录已过期,请重新登录"); window.location.href = "/login"; } else if (xhr.status === 403) { alert("您没有权限执行此操作"); } } });
额外安全提醒
- 不要依赖前端权限控制:前端隐藏按钮/菜单只是优化体验,真正的权限校验必须靠后端的Spring Security拦截规则,避免用户直接构造请求访问接口。
- 别忘了CSRF防护:Spring Security默认开启CSRF,前端AJAX请求需要携带CSRF令牌。可以在页面meta标签里配置,然后全局添加到请求头:
<meta name="_csrf" content="${_csrf.token}"/> <meta name="_csrf_header" content="${_csrf.headerName}"/>
var token = $("meta[name='_csrf']").attr("content"); var header = $("meta[name='_csrf_header']").attr("content"); $(document).ajaxSend(function(e, xhr, options) { xhr.setRequestHeader(header, token); });
内容的提问来源于stack exchange,提问作者mandy
相关产品推荐
相关产品推荐

