spring security的自定义过滤器兑现URL过滤

spring security的自定义过滤器实现URL过滤
spring security的自定义过滤器实现URL过滤

最近要实现一个功能,在使用spring security判断完成权限后,还要验证一下URL是否能访问。
因为spring security是用的正则表达式,比如:
http://localhost/order/edit.do?id=13343434
拦截的URL就写成 order/edit.do?*,然后定义成为一个“订单”权限赋予用户。但是并不控制,比如这种到数据的关系,比如
id为132434,1343434,13434的这几个只能让部门经理看,id为1343431的只能让创建人看这种需求,要实现这样的需求,我计划的是在权限拦截之后,实现一个自定义的拦截器,调用业务的manager去实现验证。如果通过,向下执行;如果不通过,那么转向到没有数据权限页面。

比较土的办法,不晓得有没有其他好办法,我没有使用到spring security的AnonymousProcessFilter,我们系统内的用户都是要登录的,所以我决定重写这个filter来实现这个URL过滤的功能。

修改security-context.xml配置文件,增加如下内容:
<beans:bean id="customerAnonymousProcessFilter"   class="cn.sccl.um.security.CustomerAnonymousProcessingFilter">
   <custom-filter before="ANONYMOUS_FILTER" />
   <beans:property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS" />
   <beans:property name="key" value="springsecurity"/>
</beans:bean>

其中的ANONYMOUS_FILTER是告诉spring的filter链,我要在哪个位置加入我的自定义fitler

类CustomerAnonymousProcessingFilter.java如下,目前没有实现正则判断URL,而只是通过URL去匹配,这里还需要稍微改进下:
package cn.sccl.um.security;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.security.Authentication;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.providers.anonymous.AnonymousAuthenticationToken;
import org.springframework.security.ui.AuthenticationDetailsSource;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.ui.WebAuthenticationDetailsSource;
import org.springframework.security.userdetails.memory.UserAttribute;
import org.springframework.util.Assert;
import org.springframework.web.context.support.WebApplicationContextUtils;
import cn.sccl.common.ResourceMappingManager;
import cn.sccl.common.ResourceSystemConfiguration;
import cn.sccl.common.util.StringUtil;
public class CustomerAnonymousProcessingFilter extends SpringSecurityFilter
   implements InitializingBean {
private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
private String key;
private UserAttribute userAttribute;
private boolean removeAfterRequest = true;
public void afterPropertiesSet() throws Exception {
   Assert.notNull(userAttribute);
   Assert.hasLength(key);
}
protected boolean applyAnonymousForThisRequest(HttpServletRequest request) {
   return true;
}
protected Authentication createAuthentication(HttpServletRequest request) {
   AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(
     key, userAttribute.getPassword(), userAttribute
       .getAuthorities());
   auth.setDetails(authenticationDetailsSource
     .buildDetails((HttpServletRequest) request));
   return auth;
}

private boolean isJspResources(HttpServletRequest request) {
   boolean flag = false;
   return flag;
}

protected void doFilterHttp(HttpServletRequest request,
    HttpServletResponse response, FilterChain chain)
    throws IOException, ServletException {
   if (SecurityContextHolder.getContext() != null
     && SecurityContextHolder.getContext().getAuthentication() != null
     && SecurityContextHolder.getContext().getAuthentication()
       .isAuthenticated()) {
    //获得前面filter登陆的用户loginId
    String loginId = SecurityContextHolder.getContext()
      .getAuthentication().getName();
    //得到请求URL
    String url = request.getRequestURI().toLowerCase();
    //目前通过properties获得对应的spring bean名称
    String managerName = ResourceSystemConfiguration.getResourceMap()
      .get(url);
    boolean flag = true;
    if (StringUtil.isNotBlank(managerName)) {
     //要实现资源拦截的manager多implement一个接口ResourceMappingManager
     flag = ((ResourceMappingManager) getAppContext(request)
       .getBean(managerName)).checkResourceMapping(loginId,
       url);
     //flag = false; //测试用
     if (!flag) {
      // 不通过,那么转向没有权限页面
      response.sendRedirect(request.getContextPath()
        + "/common/noright.jsp");
      return;
     }
     //放行
    }
   }
   chain.doFilter(request, response);
}

protected ApplicationContext getAppContext(HttpServletRequest request) {
   HttpSession session = request.getSession();
   ApplicationContext ctx = WebApplicationContextUtils
     .getRequiredWebApplicationContext(session.getServletContext());
   return ctx;
}
public int getOrder() {
   return FilterChainOrder.ANONYMOUS_FILTER;
}
public String getKey() {
   return key;
}
public UserAttribute getUserAttribute() {
   return userAttribute;
}
public boolean isRemoveAfterRequest() {
   return removeAfterRequest;
}
public void setAuthenticationDetailsSource(
    AuthenticationDetailsSource authenticationDetailsSource) {
   Assert.notNull(authenticationDetailsSource,
     "AuthenticationDetailsSource required");
   this.authenticationDetailsSource = authenticationDetailsSource;
}
public void setKey(String key) {
   this.key = key;
}
public void setRemoveAfterRequest(boolean removeAfterRequest) {
   this.removeAfterRequest = removeAfterRequest;
}
public void setUserAttribute(UserAttribute userAttributeDefinition) {
   this.userAttribute = userAttributeDefinition;
}
}

用到的类ResourceSystemConfiguration.java如下,这个是参考同事获得properties文件的工具类写的:
package cn.sccl.common;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
public class ResourceSystemConfiguration {
private static CompositeConfiguration config;
private static PropertiesConfiguration propertiesConfig;
static {
   config = new CompositeConfiguration();
   if (propertiesConfig == null) {
    try {
     propertiesConfig = new PropertiesConfiguration(
       "resouces.properties");
     propertiesConfig
       .setReloadingStrategy(new FileChangedReloadingStrategy());
     config.addConfiguration(propertiesConfig);
    } catch (ConfigurationException e) {
     e.printStackTrace();
    }
   }
}
private ResourceSystemConfiguration() {
}
public static String getString(String propertyKey) {
   return config.getString(propertyKey);
}
public static String getString(String propertyKey, String defaultValue) {
   return config.getString(propertyKey, defaultValue);
}
public static int getInt(String propertyKey) {
   return config.getInt(propertyKey);
}
public static int getInt(String key, int defaultValue) {
   return config.getInt(key, defaultValue);
}
public static float getFloat(String propertyKey) {
   return config.getFloat(propertyKey);
}
public static float getFloat(String propertyKey, float defaultValue) {
   return config.getFloat(propertyKey, defaultValue);
}
public static boolean getBoolean(String propertyKey) {
   return config.getBoolean(propertyKey);
}
public static boolean getBoolean(String propertyKey, boolean defualtValue) {
   return config.getBoolean(propertyKey, defualtValue);
}
public static String[] getStringArray(String propertyKey) {
   return config.getStringArray(propertyKey);
}
public static List<String> getStringList(String propertyKey) {
   List<String> list = new ArrayList<String>();
   String[] strArr = getStringArray(propertyKey);
   for (String value : strArr) {
    list.add(value);
   }
   return list;
}
public static Map<String, String> getResourceMap() {
   Iterator<?> itkey = config.getKeys();
   Map<String, String> map = new HashMap<String, String>();
   for (; itkey.hasNext();) {
    String key = (String) itkey.next();
    String value = config.getString(key);
    map.put(key, value);
   }
   return map;
}
@SuppressWarnings("unchecked")
public static List getList(String propertyKey) {
   return config.getList(propertyKey);
}
}

最后一个resources.properties如下,以后可以修改为数据库来存放这个对应关系:
/icpmis/user/edit.do*=userManager,id
我们的UserManagerImpl.java只要实现接口ResourceMappingManager就行了:
public class UserManagerImpl extends BaseManagerImpl<User> implements
   UserManager ,ResourceMappingManager
接口ResourceMappingManager.java里面要各个业务manager实现这个接口就行了,到时候MAPPING的这个FITLER就自动调用各个业务方法的MANAGER来验证是否通过:
package cn.sccl.common;
public interface ResourceMappingManager {
public boolean checkResourceMapping(String loginId, String bussinessId);
}