将自定义AuthenticationProcessingFilter与<form-login>一起使用(auto-config =“true”)</form-login>

时间:2012-09-04 10:32:13

标签: java spring authentication spring-security

Spring security(2.0.x)http命名空间,form-login定义自动使用AuthenticationProcessingFilter。

<form-login login-page='/logon.jsp' 
default-target-url='/home.jsp' 
always-use-default-target='true' />

我也知道,如果我设置auto-config="false",我可以通过提供自定义bean定义来自定义身份验证。

我有CustomAuthenticationProcessingFilter,它扩展了AuthenticationProcessingFilter,覆盖了acquireUsername,并使用自定义逻辑获取用户名而不是传递的用户名。

protected String obtainUsername(HttpServletRequest request) {
   // custom logic to return username from parameter/cookies/header etc ... 
}

是否可以在使用auto-config="true" <form-login>的同时使用CustomAuthenticationProcessingFilter而无需定义customAuthFilter和所有依赖bean?

  <beans:bean id="customAuthFilter" class="x.y.z.CustomAuthenticationProcessingFilter">
    <custom-filter  position="AUTHENTICATION_PROCESSING_FILTER" />
    <beans:property name="defaultTargetUrl" value="/home.jsp"></beans:property>
    ...
    ...
  </beans:bean>

3 个答案:

答案 0 :(得分:1)

简介

Spring Security 2.0处于维护模式,因此不会有任何官方更新。但是,您可以使用一些方法来解决此问题。

的BeanPostProcessor

您可以从Spring Security FAQ使用的技巧是使用BeanPostProcessor。您可以返回自定义过滤器,而不是修改属性。一个例子可能是这样的:

public class CustomFilterBeanPostProcessor implements BeanPostProcessor {
    private Filter customFilter;

    public Object postProcessAfterInitialization(Object bean, String name) {
        if (bean instanceof AuthenticationProcessingFilter) {
           return customFilter;
        }
        return bean;
    }

    public Object postProcessBeforeInitialization(Object bean, String name) {
        return bean;
    }

    public void setFilter(Filter filter) {
        this.customFilter = filter;
    }
}

然后您的配置将包括以下内容:

  <beans:bean class="CustomFilterBeanPostProcessor">
    <beans:property name="filter" ref="customAuthFilter"/>
  </beans:bean>

在属性

之前使用

另一种方法是在AuthenticationProcessingFilter之前插入自定义Filter。这将有一个额外的过滤器,但它应该是微创的,因为它很小并且不应该到达(即,因为自定义过滤器仅在AuthenticationProcessingFilter忽略请求时继续FilterChain)。使用此方法的示例配置如下所示:

<beans:bean id="customAuthFilter" class="x.y.z.CustomAuthenticationProcessingFilter">
    <custom-filter before="AUTHENTICATION_PROCESSING_FILTER" />
    <beans:property name="defaultTargetUrl" value="/home.jsp"></beans:property>
    ...
    ...
  </beans:bean>

答案 1 :(得分:0)

唉,正如它出现的那样(如果我没有错),由于AuthenticationProcessingFilter类名在<HttpSecurityBeanDefinitionParser>中被硬编码,所以无法做多少:(

if (formLoginElt != null || autoConfig) {
  FormLoginBeanDefinitionParser parser = 
     new FormLoginBeanDefinitionParser("/j_spring_security_check", 
     "org.springframework.security.ui.webapp.AuthenticationProcessingFilter");

如果过滤器类是一个配置属性并且可以在外部进行控制(就像default-target-url那样)会更好 可能正在使用属性authentication-filter-class

<form-login login-page='/logon.jsp' 
default-target-url='/home.jsp' 
always-use-default-target='true'
authentication-filter-class='x.y.z.CustomAuthenticationProcessingFilter'
/>

希望春天的人们正在倾听;)

答案 2 :(得分:0)

事实是,spring的命名空间处理程序在内部为AuthenticationProcessingFilter定义了名为_formLoginFilter的bean(参见BeanIds完整列表)。有一些方法可以解决这个问题(即使用DaoAuthenticationProvider中的j_username之外的其他东西进行身份验证,比如说从头部获取用户名等等)。

使用Spring AOP bean()语法拦截doFilter()

定义一个切入点,查找名称为_formLoginFilter的bean并截取doFilter方法。 (AuthenticationProcessingFilter.doFilter() method)并有条件地委托其他人

public class AuthenticationProcessingFilterAspect {
  private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationProcessingFilterAspect.class);
  public Object intercept(ProceedingJoinPoint pjp) throws Throwable {
    LOGGER.info("intercept------------------{}",pjp.toLongString());
    //Delegate to customised method instead of default  pjp.proceed()
    return pjp.proceed();
  }
}

<强>配置

<beans:bean id="authFilterAspect" class="x.y.z.AuthenticationProcessingFilterAspect" />
<aop:config>
  <aop:aspect ref="authFilterAspect">
    <aop:around pointcut="bean(_formLoginFilter) &amp;&amp; execution(* doFilter(..))" method="intercept"/>
  </aop:aspect>
</aop:config>

使用CustomWebAuthenticationDetails进行身份验证

为AuthenticationProcessingFilter bean定义一个bean后处理器,它注入了填充自定义字段的CustomWebAuthenticationDetails

public class AuthenticationProcessingFilterBeanPostProcessor implements
    BeanPostProcessor {

  private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationProcessingFilterBeanPostProcessor.class);

  public Object postProcessAfterInitialization(Object bean, String beanName)
      throws BeansException {
    if ("_formLoginFilter".equals(beanName) && bean instanceof AuthenticationProcessingFilter) {
      AuthenticationProcessingFilter filter = (AuthenticationProcessingFilter) bean;
      WebAuthenticationDetailsSource source = (WebAuthenticationDetailsSource) filter.getAuthenticationDetailsSource();
      source.setClazz(CustomWebAuthenticationDetails.class);
    }
    return bean;
  }

  public Object postProcessBeforeInitialization(Object bean, String beanName)
      throws BeansException {
    return bean;
  }

  @SuppressWarnings("serial")
  public static class CustomWebAuthenticationDetails extends
      WebAuthenticationDetails {
    private String customAttribute;//customfield
    public CustomWebAuthenticationDetails(HttpServletRequest request) {
      super(request);
      //Build custom attributes that could be used elsewhere (say in DaoAuthenticationProvider ) 
      //with (CustomWebAuthenticationDetails)authentication.getDetails()
      customAttribute = request.getHeader("username");
    }
    public boolean getCustomAttribute() {
      return customAttribute;
    }
  }
}

<强>配置

<beans:bean id="authFilterProcessor" class="x.y.z.AuthenticationProcessingFilterBeanPostProcessor" />

使用线程绑定请求进行实际身份验证(在DaoAuthenticationProvider中)

使用getHttpServletRequest()访问threadbound请求对象,并使用request.getHeader(“username”)进行自定义身份验证。

public static HttpServletRequest getHttpServletRequest(){
  return((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
}

如果请求不是通过DispatcherServlet

,还需要在web.xml中定义它
<filter>
  <filter-name>requestContextFilter</filter-name>
  <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>requestContextFilter</filter-name>
  <url-pattern>/j_spring_security_check</url-pattern>
  <dispatcher>FORWARD</dispatcher>
  <dispatcher>REQUEST</dispatcher>
</filter-mapping>
<filter-mapping>
  <filter-name>requestContextFilter</filter-name>
  <url-pattern>/j_spring_security_logout</url-pattern>
  <dispatcher>FORWARD</dispatcher>
  <dispatcher>REQUEST</dispatcher>
</filter-mapping>

如果其面部应用程序使用FacesContext.getCurrentInstance()

public static HttpServletRequest getHttpServletRequest(){
    FacesContext context = FacesContext.getCurrentInstance();
    return (HttpServletRequest) context.getExternalContext().getRequest();
}