4

我最近一直在研究 Spring Security,我需要知道如何使用数据库动态定义拦截 URL(在 Spring Security 中)。

我已经深入研究了整个互联网,但在这方面我找不到任何独特的(当然有用的)教程。

所以这就是我所做的:


首先我实现了 FilterInvocationSecurityMetadataSource 抽象类:

public class MyFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {


    public List<ConfigAttribute> getAttributes(Object object) {
        FilterInvocation fi = (FilterInvocation) object;
        String url = fi.getRequestUrl();
        List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();

        attributes = getAttributesByURL(url);

        return attributes;
    }

    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }

    public List<ConfigAttribute> getAttributesByURL(String inputUrl)
    {
        List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();

        Connection connection = null;
        String url = "jdbc:mysql://173.0.0.22:3306/";
        String dbName = "kheirkhahandb";
        String driverName = "com.mysql.jdbc.Driver";
        String userName = "kheirkhahan";
        String password = "kheirkhahan";
        try{
            Class.forName(driverName).newInstance();
            connection = DriverManager.getConnection(url+dbName, userName, password);
            try{
                Statement stmt = connection.createStatement();
                String selectquery = "select * from URL_ACCESS where URL = '" + inputUrl +"'";
                ResultSet rs = stmt.executeQuery(selectquery);
                while(rs.next()){
                    MyConfigAttribute temp = new MyConfigAttribute();
                    String attr = rs.getString("ACCESS").toString();
                    temp.setAttr(attr);
                    attributes.add(temp);
                }
            }
            catch(SQLException s){
                System.out.println(s);
            }
            connection.close();
        }
        catch (Exception e){
            e.printStackTrace();
        }
        return attributes;
    }



我将我的security.xml设置为:

<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
        <sec:filter-chain-map path-type="ant">
            <sec:filter-chain pattern="/css/**" filters="none" />
            <sec:filter-chain pattern="/images/**" filters="none" />
            <sec:filter-chain pattern="/login.jsp*" filters="none" />
            <sec:filter-chain pattern="/**"
                filters="
            securityContextPersistenceFilter,
            logoutFilter,
            authenticationProcessingFilter,
            exceptionTranslationFilter,
            filterSecurityInterceptor" />
        </sec:filter-chain-map>
    </bean>

    <bean id="securityContextPersistenceFilter"
        class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
    </bean>

    <bean id="exceptionTranslationFilter"
        class="org.springframework.security.web.access.ExceptionTranslationFilter">
        <property name="authenticationEntryPoint" ref="authenticationEntryPoint" />
        <property name="accessDeniedHandler" ref="accessDeniedHandler" />
    </bean>

    <bean id="authenticationEntryPoint"
        class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
        <property name="loginFormUrl" value="/login.jsp?error=entryPoint" />
    </bean>

    <bean id="accessDeniedHandler"
        class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
        <property name="errorPage" value="/login.jsp?error=access_denied" />
    </bean>

    <bean id="authenticationProcessingFilter"
        class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
        <property name="authenticationManager" ref="authenticationManager" />
    </bean>

    <bean id="filterSecurityInterceptor"
        class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
        <property name="authenticationManager" ref="authenticationManager" />
        <property name="accessDecisionManager" ref="accessDecisionManager" />
        <property name="securityMetadataSource" ref="myFilterInvocationSecurityMetadataSource" />
    </bean>

    <bean id="myFilterInvocationSecurityMetadataSource" class="com.datx.dao.MyFilterSecurityMetadataSource">
    </bean>

    <bean id="logoutFilter"
        class="org.springframework.security.web.authentication.logout.LogoutFilter">
        <constructor-arg value="/login.jsp?error=logout" />
        <constructor-arg ref="logoutHandler">
        </constructor-arg>
    </bean>

    <bean id="logoutHandler"
        class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"></bean>
<sec:authentication-manager alias="authenticationManager">
        <sec:authentication-provider>
            <sec:jdbc-user-service data-source-ref="dataSource"
                group-authorities-by-username-query="
                                        SELECT acg.ID, acg.GROUP_NAME, a.AUTHORITY_NAME AS AUTHORITY 
                                        FROM ACCESS_GROUPS acg, ACCESS_GROUP_MEMBERSHIP agm, GROUP_AUTHORITIES ga, AUTHORITIES a
                                        WHERE agm.USERNAME = ? and acg.ID = ga.GROUP_ID and acg.ID = agm.GROUP_ID and ga.AUTHORITY_ID = a.ID
                                    "
                users-by-username-query="SELECT USERNAME,PASSWORD,IS_ACTIVE FROM USER where USERNAME = ?"
                authorities-by-username-query="
                                        SELECT ua.USERNAME, a.AUTHORITY_NAME AS AUTHORITY 
                                        FROM USER_AUTHORITIES ua, AUTHORITIES a 
                                        WHERE ua.USERNAME = ? and ua.AUTHORITY_ID = a.ID
                                    " />
        </sec:authentication-provider>
    </sec:authentication-manager>

    <bean id="accessDecisionManager"
        class="org.springframework.security.access.vote.AffirmativeBased">
        <property name="decisionVoters">
            <list>
                <ref bean="roleVoter" />
            </list>
        </property>
    </bean>
    <bean id="roleVoter"
        class="org.springframework.security.access.vote.RoleHierarchyVoter">
        <property name="rolePrefix" value="" />
        <constructor-arg ref="roleHierarchy" />
    </bean>

<bean id="roleHierarchy" class="com.datx.dao.MyRoleHierarchyImpl">
        <property name="roleHierarchyEntryDaoJdbc" ref="RoleHierarchyEntryDaoJdbc" />
    </bean>

</beans>



有一些我找不到的问题:
1. 我在 URL_ACCESS 数据库中插入了一些像 <"URL" , "ROLE"> 的对。但我不确定 getAttributes 方法是否工作正常
2. 我是否必须实现我在



3. 当用户使用错误的用户名/密码或尝试访问不允许的页面而不是被重定向到 login.jsp 时,我收到异常。这是为什么?

提前致谢

4

2 回答 2

4

首先,我会确保您已经查阅了有关此问题的常见问题解答,以确保您确实想要这样做。正如汤姆所暗示的那样,通常不建议将此类信息放在数据库中。

就您当前的代码是否/为什么工作而言,如果没有更多细节,很难说。例如,您在日志中看到的错误是什么?#2 中的问题似乎并不完整。Spring Security 日志说什么?

如果要坚持这个计划,我将继续充分使用命名空间配置并利用 BeanPostProcessor(如常见问题解答中所述)来交换FilterInvocationServiceSecurityMetadataSource. 一个实现可能看起来像这样:

public class FilterInvocationServiceSecurityMetadataSourceBeanPostProcessor 
    implements BeanPostProcessor { 
    private FilterInvocationServiceSecurityMetadataSource metadataSource;

    public void setMetadataSource(FilterInvocationServiceSecurityMetadataSource metadataSource) {
        this.metadataSource = metadataSource;
    }

    public Object postProcessBeforeInitialization(Object bean, String beanName) { 
        if(bean instanceof FilterInvocationSecurityMetadataSource) { 
            return metadataSource; 
        } 
        return bean; 
    } 

    public Object postProcessAfterInitialization(Object bean, String beanName) { 
        return bean; 
    } 
}

然后FilterInvocationServiceSecurityMetadataSource可以在 Spring 配置中指定您的自定义以及FilterInvocationServiceSecurityMetadataSourceBeanPostProcessor.

<bean id="fiMds" class="FilterInvocationServiceSecurityMetadataSourceBeanPostProcessor">
  <property name="metadataSource">
    <bean id="myFilterInvocationSecurityMetadataSource" class="com.datx.dao.MyFilterSecurityMetadataSource"/>
  </property>
</bean>
于 2012-06-11T15:47:37.023 回答
2

感谢 Tom 和 Rob 的快速回复。

首先,我完全意识到“在数据库中存储 url 模式不是一个好主意”。但是,我们正在尝试动态管理所有内容。所以没有其他选择。

事实证明,我的代码存在一些小问题。在这里,我一一回答我的每一个问题。

  1. 我的 getAttributes 方法工作得很好。但是有另一种加载 url-patterns 的方法。我可以先将所有 url 模式及其相应的角色分别加载到 HashedMap 中。而在 getAttributes 方法中,我可以只查找 HasehdMap 。简而言之,它会是这样的:

    • HashedMap url-access = myDBManager.getAllUrlAccess();
      在我的 getAttributes 方法中,我使用了这个url-access东西。
  2. 这个问题以某种方式被修剪了!我试图询问 springSecurityFilterChain bean 中使用的那些过滤器。

    <bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
            <security:filter-chain-map path-type="ant">
                <sec:filter-chain pattern="/css/**" filters="none" />
                <sec:filter-chain pattern="/images/**" filters="none" />
                <sec:filter-chain pattern="/login.jsp*" filters="none" />
                <sec:filter-chain pattern="/**"
                    filters="
                securityContextPersistenceFilter,
                logoutFilter,
                authenticationProcessingFilter,
                exceptionTranslationFilter,
                filterSecurityInterceptor" />
            </security:filter-chain-map>
    </bean>
    
  3. 我收到异常是因为在 authenticationProcessingFilter bean 中没有这样的属性。所以我像这样重写它:

    <bean id="authenticationProcessingFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
        <property name="authenticationFailureHandler" ref="authenticationFailureHandler" />
        <property name="filterProcessesUrl" value="/j_spring_security_check" />
        <property name="usernameParameter" value="j_username" />
        <property name="passwordParameter" value="j_password" />
        <property name="authenticationManager" ref="authenticationManager" />   
    </bean>
    

当然,我还必须引入 authenticationFailureHandler bean:

<bean id="authenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
        <property name="defaultFailureUrl" value="/index.jsp" />
</bean>

现在我没有收到任何异常。

但是这里出现了另一个问题:我无法理解用户名/密码是否不正确或用户名无权访问请求的页面。在这两种情况下,用户都会根据这个 bean 进行重定向:

<bean id="authenticationEntryPoint"
    class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
        <property name="loginFormUrl" value="/login.jsp?error=EntryPoint" />
</bean>

为什么它控制这两种情况?

于 2012-06-16T07:36:18.980 回答