【问题标题】:Spring Security many providersSpring Security 许多提供商
【发布时间】:2014-03-06 04:43:27
【问题描述】:

在我的应用程序中,我使用 LDAP 身份验证(称为 ldap)。对于我的一个页面,我需要使用基本的 html 弹出窗口(称为 internal)进行自己的身份验证。我得到BeanCreationException:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.filterChainProxy': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: A universal match pattern ('/**') is defined  before other patterns in the filter chain, causing them to be ignored. Please check the ordering in your <security:http> namespace or FilterChainProxy bean configuration
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1512) ~[spring-beans-3.2.5.RELEASE.jar:3.2.5.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:521) ~[spring-beans-3.2.5.RELEASE.jar:3.2.5.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458) ~[spring-beans-3.2.5.RELEASE.jar:3.2.5.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:296) ~[spring-beans-3.2.5.RELEASE.jar:3.2.5.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223) ~[spring-beans-3.2.5.RELEASE.jar:3.2.5.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:293) ~[spring-beans-3.2.5.RELEASE.jar:3.2.5.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194) ~[spring-beans-3.2.5.RELEASE.jar:3.2.5.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:628) ~[spring-beans-3.2.5.RELEASE.jar:3.2.5.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932) ~[spring-context-3.2.2.RELEASE.jar:3.2.2.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479) ~[spring-context-3.2.2.RELEASE.jar:3.2.2.RELEASE]
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:389) ~[spring-web-3.2.2.RELEASE.jar:3.2.2.RELEASE]
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:294) ~[spring-web-3.2.2.RELEASE.jar:3.2.2.RELEASE]
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:112) [spring-web-3.2.2.RELEASE.jar:3.2.2.RELEASE]
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4973) [catalina.jar:7.0.52]
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5467) [catalina.jar:7.0.52]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) [catalina.jar:7.0.52]
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559) [catalina.jar:7.0.52]
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549) [catalina.jar:7.0.52]
at java.util.concurrent.FutureTask.run(Unknown Source) [na:1.7.0_51]
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) [na:1.7.0_51]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) [na:1.7.0_51]
at java.lang.Thread.run(Unknown Source) [na:1.7.0_51]

这是我的配置:

<http authentication-manager-ref="internal">    
    <intercept-url pattern="/monitoring" access="ROLE_USER" />
    <http-basic />
</http>

<http authentication-manager-ref="ldap" auto-config='true' use-expressions="true">
    <intercept-url pattern="/**" access="permitAll" />
    <form-login login-page='/' default-target-url='/login_ok'
        always-use-default-target='true' authentication-failure-url="/login_failed" />
    <logout logout-success-url="/" />
</http>

<authentication-manager id="internal">
   <authentication-provider>
       <user-service>
       <user name="monitoring" password="monitoring" authorities="ROLE_USER" />
       </user-service>
   </authentication-provider>
</authentication-manager>

<authentication-manager erase-credentials="false" id="ldap">
    <ldap-authentication-provider
        group-search-filter="${ldap.group-search-filter}" server-ref="ldapServer"
        group-search-base="${ldap.group-search-base}" user-search-filter="${ldap.user-search-filter}"
        user-search-base="${ldap.user-search-base}" group-role-attribute="${ldap.group-role-attribute}"
        role-prefix="${ldap.role-prefix}">
    </ldap-authentication-provider>
    <authentication-provider user-service-ref="userService" />
</authentication-manager>

我了解引发异常的原因,并且知道模式顺序和范围很重要,但不知道如何正确执行。我想要的只是每个用户都可以访问“/**”,但只有监控用户可以访问 /monitoring(通过基本身份验证)。在我想添加此内部身份验证之前,一切正常。

【问题讨论】:

    标签: java spring spring-security


    【解决方案1】:

    你需要做的改变是:

    • 确保有第一个&lt;http&gt; 的模式。这是你现在问题的核心。 &lt;http&gt; 都匹配每个请求,因此第二个 &lt;http&gt; 永远不会被考虑。要解决此问题,请添加模式属性。
    • 确保为您的监控用户使用不同的角色。请记住,身份验证和授权是分离的。这意味着访问 /monitoring 仅受用户角色的限制,而不是用户如何进行身份验证。使用当前设置,用户可以通过 ldap AuthenticationManager 进行身份验证,如果他们被分配了角色“ROLE_USER”,他们就可以访问 /monitoring 并查看该 URL。
    • 虽然没有必要,但我们更新了第一个 &lt;http&gt; 的拦截 URL 以匹配每个 URL。这不是必需的,因为第一个 &lt;http&gt; 首先仅用于 /monitoring URL,但如果有人稍后更新模式属性会更安全。

    下面是一个变化的例子:

    <http authentication-manager-ref="internal" pattern="/monitoring">    
        <intercept-url pattern="/**" access="ROLE_MONITORING" />
        <http-basic />
    </http>
    
    <http authentication-manager-ref="ldap" auto-config='true' use-expressions="true">
        <intercept-url pattern="/**" access="permitAll" />
        <form-login login-page='/' default-target-url='/login_ok'
            always-use-default-target='true' authentication-failure-url="/login_failed" />
        <logout logout-success-url="/" />
    </http>
    
    <authentication-manager id="internal">
       <authentication-provider>
           <user-service>
           <user name="monitoring" password="monitoring" authorities="ROLE_MONITORING" />
           </user-service>
       </authentication-provider>
    </authentication-manager>
    
    <authentication-manager erase-credentials="false" id="ldap">
        <ldap-authentication-provider
            group-search-filter="${ldap.group-search-filter}" server-ref="ldapServer"
            group-search-base="${ldap.group-search-base}" user-search-filter="${ldap.user-search-filter}"
            user-search-base="${ldap.user-search-base}" group-role-attribute="${ldap.group-role-attribute}"
            role-prefix="${ldap.role-prefix}">
        </ldap-authentication-provider>
        <authentication-provider user-service-ref="userService" />
    </authentication-manager>
    

    我会考虑的一件事是将监控用户移至 LDAP。将此用户放置在内存实例中会鼓励不再维护该用户(即应定期轮换密码)。这也将确保您的 LDAP 用户和内部用户之间不会发生任何冲突(即,您怎么知道 ldap 身份验证中没有名为“monitoring”的用户?)。最后它简化了您的配置,您只需要单个 &lt;http&gt; 配置。如果 HTTP Basic 标头存在,它仍将使用它进行身份验证。

    【讨论】:

    • 好的。问题是,该 http 标签没有路径属性,所以 XML 中存在错误。或者我错过了一些架构或什么?关于休息 - 我同意,它可以更好地组织:)。
    • 是的,我想通了:)。将“路径”更改为“模式”解决了 XML 错误,但是当我想访问“/监控”时,我得到 403 - 拒绝访问。没有显示验证表单,只是错误
    • 您是否记得将拦截 URL 和监控用户的角色都更改为 ROLE_MONITORING?也许您的浏览器正在自动发送不同的用户名/密码(记住 http 基本)?您是否尝试以隐身模式访问它?日志是什么样的?
    • 是的,我在这两个地方都换了角色。多次单击并记录后,我发现当我打开主页时,输入 /monitoring,弹出窗口显示,我输入了正确的凭据,我可以访问 /monitoring!
    • 但是当我返回主页并尝试使用我的表单登录(ldap 凭据)登录时,我得到另一个基本的弹出窗口(?)询问 LDAP 凭据。看起来浏览器以某种方式记住了监控凭据,并希望我使用 BASIC 登录来确认我的 LDAP 凭据。另一件事是,当我在主页上登录 LDAP 时,注销(或不注销),然后键入 /monitoring,我没有弹出窗口,并且我在之前的评论中提到过 403。
    猜你喜欢
    • 1970-01-01
    • 2015-12-09
    • 2012-02-18
    • 2014-04-24
    • 2016-04-20
    • 1970-01-01
    • 1970-01-01
    • 2018-08-23
    • 2013-08-24
    相关资源
    最近更新 更多