繁体   English   中英

将Custom AuthenticationProcessingFilter与 <form-login> (自动配置=“ true”)

[英]Using Custom AuthenticationProcessingFilter with <form-login> (auto-config=“true”)

Spring安全性(2.0.x)http名称空间,表单登录定义自动使用AuthenticationProcessingFilter。

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

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

我有CustomAuthenticationProcessingFilter,它扩展了AuthenticationProcessingFilter覆盖了getUsername并使用自定义逻辑来获取比传递的用户名更多的用户名。

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>

介绍

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之前插入自定义过滤器。 这将有一个额外的过滤器,但由于它很小并且永远都不会达到(因此,自定义过滤器仅在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>

las,看来(如果我没记错的话),由于AuthenticationProcessingFilter类名称被硬编码在< HttpSecurityBeanDefinitionParser > :(

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

如果filter类是一个config属性,并且在外部进行控制(就像default-target-url )可能使用attribute 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'
/>

希望春天的人们在听;)

事实是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后处理器,该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()访问线程绑定的请求对象,并使用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();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM