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

Shiro自定义过滤器报错“No SecurityManager accessible”求助排查

问题排查与解决

问题分析

初始错误原因

将/login/**的过滤器从anon改为自定义customLoginFilter后抛出UnavailableSecurityManagerException,核心原因是:

  • 自定义过滤器CustomLoginFilter错误继承了BasicHttpAuthenticationFilter,该过滤器专门用于HTTP Basic认证,完全不匹配普通登录接口的认证逻辑;
  • 直接通过new CustomLoginFilter()创建实例,未将过滤器纳入Spring容器管理,也未注入SecurityManager,导致过滤器执行时无法获取Shiro安全管理器实例。

手动绑定后的401原因

手动绑定SecurityManager后返回401,是因为BasicHttpAuthenticationFilter会自动尝试从请求头的Authorization字段解析Basic认证信息,而你的登录接口是常规表单/JSON参数提交,没有符合要求的认证头,导致认证失败。

解决步骤

1. 更换过滤器基类

将CustomLoginFilter的父类改为AuthenticatingFilter,该类是Shiro专门为自定义用户名密码登录场景设计的基类:

import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.web.util.WebUtils;
import org.apache.commons.io.IOUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import java.nio.charset.StandardCharsets;
import java.io.IOException;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class CustomLoginFilter extends AuthenticatingFilter {

    // 从请求中解析用户名密码,生成认证Token
    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
        // 示例:解析JSON格式请求体,可根据实际改为表单参数解析
        String requestBody = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
        JSONObject params = JSON.parseObject(requestBody);
        String username = params.getString("username");
        String password = params.getString("password");
        return new UsernamePasswordToken(username, password);
    }

    // 处理登录请求逻辑
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        if (isLoginRequest(request, response)) {
            if (isLoginSubmission(request, response)) {
                // 执行登录认证
                return executeLogin(request, response);
            }
            return true;
        } else {
            // 非登录请求直接返回401
            WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }
    }

    // 登录成功后处理:记录日志+返回响应
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
        log.info("用户[{}]登录成功", token.getPrincipal());
        HttpServletResponse httpResponse = WebUtils.toHttp(response);
        httpResponse.setStatus(HttpServletResponse.SC_OK);
        httpResponse.setContentType("application/json;charset=utf-8");
        httpResponse.getWriter().write(JSON.toJSONString("登录成功"));
        return false; // 终止过滤器链,不再执行后续逻辑
    }

    // 登录失败后处理:记录日志+返回错误
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, Exception e, ServletRequest request, ServletResponse response) {
        log.error("用户[{}]登录失败", token.getPrincipal(), e);
        HttpServletResponse httpResponse = WebUtils.toHttp(response);
        httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        httpResponse.setContentType("application/json;charset=utf-8");
        try {
            httpResponse.getWriter().write(JSON.toJSONString("登录失败:" + e.getMessage()));
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return false;
    }
}

2. 修改Shiro配置,注入SecurityManager

将自定义过滤器配置为Spring Bean,并注入SecurityManager,确保过滤器能获取到Shiro安全管理器实例:

// 在ShiroConfig类中添加过滤器Bean定义
@Bean
public CustomLoginFilter customLoginFilter(DefaultWebSecurityManager securityManager) {
    CustomLoginFilter loginFilter = new CustomLoginFilter();
    loginFilter.setSecurityManager(securityManager);
    loginFilter.setLoginUrl("/login/local"); // 指定你的登录接口路径
    return loginFilter;
}

// 修改getShiroFilterBean中的过滤器配置
@Bean
public ShiroFilterFactoryBean getShiroFilterBean(DefaultWebSecurityManager defaultWebSecurityManager, CustomLoginFilter customLoginFilter){
    ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
    bean.setSecurityManager(defaultWebSecurityManager);

    LinkedHashMap<String, Filter> filterMap = new LinkedHashMap<>();
    filterMap.put("customLoginFilter", customLoginFilter); // 使用Spring容器管理的过滤器实例
    bean.setFilters(filterMap);

    Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
    filterChainDefinitionMap.put("/login/**", "customLoginFilter");
    bean.setFilterChainDefinitionMap(filterChainDefinitionMap);

    bean.setLoginUrl("/login/to-login");
    return bean;
}

3. 验证依赖配置

你的Maven依赖已经适配Spring Boot 3的Jakarta EE规范,无需修改。如果需要简化依赖,可以直接使用Shiro的Jakarta版本starter(版本需匹配):

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-web-starter</artifactId>
    <version>1.13.0</version>
    <classifier>jakarta</classifier>
</dependency>

关键说明

  • AuthenticatingFilter是Shiro专门用于自定义登录认证的过滤器基类,比BasicHttpAuthenticationFilter更适配常规登录场景;
  • 过滤器必须由Spring管理并注入SecurityManager,Shiro会自动处理ThreadContext的绑定,无需手动操作;
  • 重写createToken、onAccessDenied等方法时,需根据实际请求格式(表单/JSON)调整参数解析逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 02:44:56