当前位置: 首页 > 知识库问答 >
问题:

在Shiro中,未绑定的SecurityManager真的是无效的应用程序配置吗?

贺元明
2023-03-14

我正在将Apache Shiro添加到我的应用程序中,我想知道以下错误消息是否真正准确:

org.apache.shiro.UnavailableSecurityManagerException:调用代码无法访问 No SecurityManager,绑定到 org.apache.shiro.util.ThreadContext 或作为 vm 静态单例。这是无效的应用程序配置。

我浏览了一下源代码,得到的印象是,只要我不使用< code>SecurityUtils并且我愿意将< code>SecurityManager传递给需要它的组件,我实际上不需要将< code>SecurityManager分配给< code>SecurityUtils使用的静态单例。

我想避免的一件事是让Shiro将任何东西放入< code>ThreadLocal或让Shiro使用它的< code>ThreadContext支持类。我使用的是Apache Thrift,不想让自己致力于一个请求一个线程的网络设计。我对Shiro的要求非常低,所以我将在下面展示我正在做的事情。

我在我的应用程序中使用Guice,但我没有使用shiro-guice,因为Shiro AOP的东西取决于将主题ThreadContext相关联。相反,我从一个非常简单的Guice模块开始。

public class ShiroIniModule extends AbstractModule {
    @Override
    protected void configure() {}

    @Provides
    @Singleton
    public SecurityManager provideSecurityManager() {
        return new DefaultSecurityManager(new IniRealm("classpath:shiro.ini"));
    }
}

这不完全是一个生产质量领域/安全管理器设置,但对我来说已经足够好了。接下来,我创建了自己的管理器类,它的作用域非常有限,由我的应用程序的组件使用。我有两个这样的;一个< code > ThriftAuthenticationManager 和一个< code > ThriftAuthorizationManager 。以下是前者:

@Singleton
public class ThriftAuthenticationManager {
    private final Logger log = LoggerFactory.getLogger(ThriftAuthenticationManager.class);

    private final SecurityManager securityManager;

    @Inject
    public ThriftAuthenticationManager(SecurityManager securityManager) {
        this.securityManager = securityManager;
    }

    public String authenticate(String username, String password) throws TException {
        try {
            Subject currentUser = new Subject.Builder(securityManager).buildSubject();

            if (!currentUser.isAuthenticated()) {
                currentUser.login(new UsernamePasswordToken(username, password));
            }

            String authToken = currentUser.getSession().getId().toString();
            Preconditions.checkState(!Strings.isNullOrEmpty(authToken));
            return authToken;
        }
        catch (AuthenticationException e) {
            throw Exceptions.security(SecurityExceptions.AUTHENTICATION_EXCEPTION);
        }
        catch(Throwable t) {
            log.error("Unexpected error during authentication.", t);
            throw new TException("Unexpected error during authentication.", t);
        }

    }
}

而后者:

@Singleton
public class ThriftAuthorizationManager {
    private final Logger log = LoggerFactory.getLogger(ThriftAuthorizationManager.class);

    private final SecurityManager securityManager;

    @Inject
    public ThriftAuthorizationManager(SecurityManager securityManager) {
        this.securityManager = securityManager;
    }

    public void checkPermissions(final String authToken, final String permissions)
            throws TException {
        withThriftExceptions(new Callable<Void>() {
            @Override
            public Void call() throws Exception {
                securityManager.checkPermission(getPrincipals(authToken), permissions);
                return null;
            }
        });
    }

    public void checkPermission(final String authToken, final Permission permission)
            throws TException {
        withThriftExceptions(new Callable<Void>() {
            @Override
            public Void call() throws Exception {
                securityManager.checkPermission(getPrincipals(authToken), permission);
                return null;
            }
        });
    }

    private Subject getSubject(String authToken) {
        return new Subject.Builder(securityManager).sessionId(authToken).buildSubject();
    }

    private PrincipalCollection getPrincipals(String authToken) {
        return getSubject(authToken).getPrincipals();
    }

    private void withThriftExceptions(Callable<Void> callable) throws TException {
        try {
            callable.call();
        }
        catch(SessionException e) {
            throw Exceptions.security(SecurityExceptions.SESSION_EXCEPTION);
        }
        catch(UnauthenticatedException e) {
            throw Exceptions.security(SecurityExceptions.UNAUTHENTICATED_EXCEPTION);
        }
        catch(AuthorizationException e) {
            throw Exceptions.security(SecurityExceptions.AUTHORIZATION_EXCEPTION);
        }
        catch(ShiroException e) {
            throw Exceptions.security(SecurityExceptions.SECURITY_EXCEPTION);
        }
        catch(Throwable t) {
            log.error("An unexpected error occurred during authorization.", t);
            throw new TException("Unexpected error during authorization.", t);
        }
    }
}

我的Thrift服务使用上述两个类进行认证和授权。例如:

@Singleton
public class EchoServiceImpl implements EchoService.Iface {
    private final Logger log = LoggerFactory.getLogger(EchoServiceImpl.class);

    private final ThriftAuthorizationManager authorizor;

    @Inject
    public EchoServiceImpl(ThriftAuthorizationManager authorizor) {
        this.authorizor = authorizor;
    }

    @Override
    public Echo echo(String authToken, Echo echo) throws TException {
        authorizor.checkPermissions(authToken, "echo");
        return echo;
    }
}

所以,我想我实际上有几个任务。

>

  • 我引用的错误实际上是一个错误,还是只是一个过分热心的日志消息?

    如果我从来不使用< code>ShiroUtils,我需要担心Shiro依赖于< code>ThreadContext中的任何东西吗?

    如果我不能保证每个请求一个线程的环境,那么使用< code > security utils # setSecurityManager 有什么害处吗?

    我还没有尝试使用Shiro的高级权限(org.apache.shiro.authz.Permission)。他们是否依赖于ThreadContext中的任何内容,或者做了任何我应该尽早研究的奇怪事情?

    我有没有做过其他可能给我带来问题的事情,或者我能改进什么?

  • 共有2个答案

    沈凡
    2023-03-14

    这是我在遇到同样的问题时发现的:我将shiro filter语句添加到我的web.xml文件中,然后在我的bean中直接调用Subject。我补充道:

    <filter>
            <filter-name>ShiroFilter</filter-name>
            <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
    </filter>
    <filter-mapping>
            <filter-name>ShiroFilter</filter-name>
            <url-pattern>/*</url-pattern>
            <dispatcher>REQUEST</dispatcher>
            <dispatcher>FORWARD</dispatcher>
            <dispatcher>INCLUDE</dispatcher>
            <dispatcher>ERROR</dispatcher>
    </filter-mapping>
    

    在那之后,我继续写了下面的声明:

    Subject subject = SecurityUtils.getSubject();
    
    施令雪
    2023-03-14

    >

  • 只有当您想调用SecurityUtils.getSecurityManager()时,引用的错误才是错误。非常欢迎您手动传递它或将其作为SecurityUtils使用之外的依赖项注入。

    SecurityUtils主要是为那些想要使用它的人提供便利。Shiro内部只有少数东西明确地调用它:Shiro的AspectJ集成调用它,Shiro的一些Web支持调用它(Servlet Filters,JSP和JSF taglibs)。但是,在Shiro中使用它的这些情况下,它(我认为)总是由模板方法调用,允许您重写该方法以在需要时从其他地方获取主题。

    调用<code>SecurityUtils没有任何危害。setSecurityManager只要您对整个JVM的单个SecurityManager实例感到满意。如果您有多个Shiro应用程序在同一JVM中使用该方法,则可能会导致问题。尽管如此,因为静态内存调用和全局状态是邪恶的,如果你能找到引用SecurityManager的另一种方法,那就更好了(例如依赖注入)

    Shiro权限不依赖于任何与<code>ThreadContext

    你的代码看起来很不错。< code > ThriftAuthorizationManager 授权检查的一个建议是直接委托给主题(主题又委托给SecurityManager)。我认为这比当前的方法要快一点,并且更好地自文档化:

    return getSubject(authToken).checkPermission(permission);
    

    感谢您分享您的问题。看到框架的非Web用法总是很高兴的,因为它是为所有工作负载设计的。

  •  类似资料:
    • 我在应用程序中使用Spring MVC,一般来说,bean、服务、DAO等配置都是通过xml或java配置的。 然而,有些配置不属于任何bean,例如应用程序名称、应用程序版本等,我想将它们保存到一个独立的文件中,但我确实需要在其他bean或jsp页面中使用这些类型的属性。 当然,我可以创建一个名为AppConfig的bean,并通过xml将属性注入bean,然后将AppConfig注入任何需要它

    • 本文向大家介绍浅谈shiro的SecurityManager类结构,包括了浅谈shiro的SecurityManager类结构的使用技巧和注意事项,需要的朋友参考一下 (一) shiro的SecurityManager类结构为: 总结: 1.SecurityManager主要作用于登录、登出用创建主题Subject,并继承自Authenticator身份验证器和权限验证器和sessionManag

    • 我正在努力弄清楚我为React应用程序配置Firebase的尝试出了什么问题。我已经遵循了至少10个不同的教程,但无法让配置变量注册为已识别的。 然后,我有一个firebase/firebase.js文件,如下所示: 我一直试图弄清楚为什么配置变量被记录为未定义。 有没有人知道如何成功配置一个firebase应用程序。我之前关于这个主题的问题建议删除env.development文件中的:和值之间

    • 10-07 00:23:49.443 126 8-1268/?E/HWServiceManager:BINDER_SET_INHERIT_FIFO_PRIO失败,错误为-1 10-07 00:23:51.504 126 8-1268/?E/HW-IPPCThreadState:绑定线程池(1个线程)1871 ms饥饿10-07 00:23:55.324 127 1-1280/?E/vold:关闭/

    • 我能够使用shiro.ini和spring运行shiro,但我想使用shiro注释,所以我尝试使用没有ini文件的Shiro-。但这让我很难过,错误: ConfigurationException:未找到Shiro INI配置或发现为空/未配置。在org.apache.shiro.web.env.init(iniwebenvironment.java:87)在org.apache.shiro.ut

    • 刚开始学习shiro, 实现的效果非常简单,首页点击添加(/user/add),应该能直接访问. 而点击更新(/user/update),应该提示登录.但是实际都可以直接点开,不知道哪里出问题了,shiro的配置没生效,请高手赐教,谢谢 环境:java 17 springboot 3.1.1 shiro 1.11.0 pom.xml controller/MyController.java res