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

馋奶兔 提交于 2020-01-03 05:57:29

问题


Spring security (2.0.x) http namespace, form-login definition automatically uses AuthenticationProcessingFilter.

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

I also know that If I set auto-config="false" I can customise authentication by providing custom bean definition.

I have CustomAuthenticationProcessingFilter that extends AuthenticationProcessingFilter overrides obtainUsername and uses custom logic to get username than the one passed.

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

Is it possible to use CustomAuthenticationProcessingFilter while still using auto-config="true" <form-login> without needing to define customAuthFilter and all dependent beans ?

  <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>

回答1:


Intro

Spring Security 2.0 is in maintenance mode, so there are not going to be any official updates to it. There are, however, a few approaches you can use to get around this problem.

BeanPostProcessor

A trick you can use from the Spring Security FAQ is to use a BeanPostProcessor. Instead of modifying a property, you can return your custom Filter. An example might be something like this:

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;
    }
}

Then your configuration would include the following:

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

Use before attribute

An alternative is to insert the custom Filter before the AuthenticationProcessingFilter. This will have an additional Filter, but it should be minimally invasive since it is small and should not ever be reached (i.e. since the custom Filter only continues the FilterChain when the AuthenticationProcessingFilter ignores the request). An example configuration using this approach can be seen below:

<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>



回答2:


Alas, As it appears (If I am not wrong) nothing much could be done as AuthenticationProcessingFilter class name is hardcoded in <HttpSecurityBeanDefinitionParser> :(

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

.

.

It would have been nicer if filter class was a config attribute and be controlled externally (just like default-target-url) may be using 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'
/>

Hope Spring folks are listening ;)




回答3:


The fact is that spring's namespace handler internally defines bean with the name _formLoginFilter for AuthenticationProcessingFilter (See for BeanIds complete list). There are coulpe of ways to workaround with this issue (i.e to authenticate using something other than j_username from DaoAuthenticationProvider , like say take username from header etc... )

Use Spring AOP bean() syntax to intercept doFilter()

Define a pointcut that looks for bean with name _formLoginFilter and intercepts doFiltermethod. ( AuthenticationProcessingFilter.doFilter() method) and conditionally delegate to something else

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();
  }
}

Config

<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>

Use CustomWebAuthenticationDetails to do authentication

Define a bean postprocessor for AuthenticationProcessingFilter bean that injects CustomWebAuthenticationDetails which populates custom fields

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;
    }
  }
}

Config

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

Use thread bound request to do actual authentication (within DaoAuthenticationProvider)

Use getHttpServletRequest() to access threadbound request object and use request.getHeader("username") to do custom authentication.

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

Also need to Define this in web.xml if request is not through DispatcherServlet

<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>

If its faces application use FacesContext.getCurrentInstance()

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


来源:https://stackoverflow.com/questions/12261620/using-custom-authenticationprocessingfilter-with-form-login-auto-config-true

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!