SpringSecurity从数据库中获取url资源地址和对应的权限信息

通过Debug我们发现我们的资源地址和权限信息保存在FilterSecurityInterceptor这个类的securityMetadataSource属性中.
private FilterInvocationSecurityMetadataSource securityMetadataSource;

而这个属性(这个属性是一个对象)它的类型是:
DefaultFilterInvocationSecurityMetadataSource(也就是FilterInvocationSecurityMetadataSource的实现类)
在DefaultFilterInvocationSecurityMetadataSource类我们发现有一个requestMap属性
private final Map<RequestMatcher, Collection<ConfigAttribute>> requestMap;

在debug中看到这个requestMap保存着我们的资源地址和权限信息.
自定义资源地址和权限信息最终可以通过修改这个requestMap来达到目的.

如何修改requestMap来达到自定义的目的?

  1. 目前URL地址资源和对应的权限信息放到哪了?
    我们的url地址资源和权限信息保存在requestMap这个Map中,requestMap是securityMetadataSource这个对象的属性
    securityMetadataSource对象的类型是:DefaultFilterInvocationSecurityMetadataSource
    securityMetadataSource对象作为一个属性存在于FilterSecurityInterceptor这个类中.

  2. 这个Map是如何用来保存URL资源地址和权限信息的?
    URL地址资源和权限 保存在 securityMetadataSource属性的 requestMap中
    private final Map

配置 DefaultFilterInvocationSecurityMetadataSource 实例来修改它的requestMap属性.

配置一个DefaultFilterInvocationSecurityMetadataSource的子类对象最好的办法就是继承这个对象,但这个没有提供相应的setRequestMap方法,在构造器中我们也无法在子类构造器被调用之后传入一个自定义之后的requestMap进去,因为不允许有语句出现在super之前.

  1. 创建基础 FactoryBean 接口的 bean
public class MyDefaultFilterInvocationSecurityMetadataSource implements FactoryBean<DefaultFilterInvocationSecurityMetadataSource>

使 FactoryBean 的 getObject() 方法返回 DefaultFilterInvocationSecurityMetadataSource 的实例

  1. 创建 DefaultFilterInvocationSecurityMetadataSource 实例需要初始化其 requestMap 属性
    key: AntPathRequestMatcher. 而 AntPathRequestMatcher 实例需要一个 pattern 成员变量, 该成员变量即指向一个受保护的 URL
    value: List. 其中的元素为 SecurityConfig, 而该类有一个 config 类型的成员变量, 即一个权限的名称.

  2. 创建 BeanPostProcessor, 在其 postProcessAfterInitialization 方法中

    • 获取 FilterSecurityInterceptor 实例
    • 获取自定义的 FactoryBean 的实例MyDefaultFilterInvocationSecurityMetadataSource
    • 完成属性的替换

把配置好requestMap之后的对象 赋给FilterSecurityInterceptor对象的属性.

属性最终被保存到FilterSecurityInterceptor 对象的 securityMetadataSource 属性中,如何替换这个属性为我们自定义之后的(FactoryBean中配置的那个对象.)?
可以使用Spring的bean的后置处理器来完成, 提供BeanPostProcessor的实现类, 在其postProcessAfterInitialization重写的方法中完成属性的替换.

示例代码

package com.al0n4k.spring.bean;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.AntPathRequestMatcher;
import org.springframework.security.web.util.RequestMatcher;

/** * @author AL0n4k */
public class MyDefaultFilterInvocationSecurityMetadataSource implements
        FactoryBean<DefaultFilterInvocationSecurityMetadataSource> {

    @Override
    public DefaultFilterInvocationSecurityMetadataSource getObject()
            throws Exception {

        LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<>();

        // 需要验证权限的地址.
        AntPathRequestMatcher matcher = null;

        // 权限集合.
        List<ConfigAttribute> attributes = null;

        // 这些数据,原本需要从数据库获取.
        matcher = new AntPathRequestMatcher("/user.jsp");
        attributes = new ArrayList<ConfigAttribute>();
        attributes.add(new SecurityConfig("ROLE_USER"));

        // 放入到requestMap中.
        requestMap.put(matcher, attributes);

        // 调用构造器添加requestMap属性.
        DefaultFilterInvocationSecurityMetadataSource metadataSource = 
        new DefaultFilterInvocationSecurityMetadataSource(requestMap);


        return metadataSource;
    }

    @Override
    public Class<?> getObjectType() {
        return DefaultFilterInvocationSecurityMetadataSource.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

}


package com.al0n4k.spring.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

/** * @author AL0n4k */
public class MyBeanPostProcessor implements BeanPostProcessor {

    // 得到初始化后的FilterInvocationSecurityMetadataSource
    private FilterSecurityInterceptor filterSecurityInterceptor;

    // 默认置为null,这个实现类的对象在没有被ioc实例化之前就是null
    private MyDefaultFilterInvocationSecurityMetadataSource metadataSource = null;

    // 定义个布尔值,用来标识是否 替换完成.
    boolean isOk = false;

    @Override
    public Object postProcessAfterInitialization(Object arg0, String arg1)
            throws BeansException {
        // 如果是这个类型的对象被实例化
        if (arg0 instanceof FilterSecurityInterceptor) {
            this.filterSecurityInterceptor = (FilterSecurityInterceptor) arg0;
        }

        if (arg0 instanceof MyDefaultFilterInvocationSecurityMetadataSource) {
            this.metadataSource = (MyDefaultFilterInvocationSecurityMetadataSource) arg0;
        }

        /** * 这个后置处理器的目的主要是用来把我们自定义之后对象设置到filterSecurityInterceptor中. * 那么其他的对象被ioc创建我们不用理会.所以这里加上isOk来标识设置完毕,直接返回.(IOC容器中可能会有多个对象.) */
        if ( !isOk && filterSecurityInterceptor != null && metadataSource != null ) {
            try {
                filterSecurityInterceptor.setSecurityMetadataSource(metadataSource.getObject());
                isOk = true;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }


        return arg0; //返回当前ioc容器中的对象.
    }

    @Override
    public Object postProcessBeforeInitialization(Object arg0, String arg1)
            throws BeansException {
        return arg0;
    }

}

你可能感兴趣的:(java,SpringSecu)