Is it possible to send more data in form based aut

2019-04-02 16:33发布

问题:

I am relatively new to the Spring Framework and Spring security.

I have used a custom authentication scheme, HTML:

<form action="j_spring_security_check">
    <input type="text" name="j_username" value="abc"/>
    <input type="text" name="j_password" value="abc"/>
    <input type="text" name="myCustom1" value="pqr"/> <!-- maybe type="hidden" -->
    <input type="text" name="myCustom2" value="pqr"/> <!-- maybe type="hidden" -->
</form>

and the corresponding code:

public class CustomAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider
{
    @Override protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken)
    throws AuthenticationException
    {
        System.out.println("Method invoked : additionalAuthenticationChecks isAuthenticated ? :"+usernamePasswordAuthenticationToken.isAuthenticated());
    }

    @Override protected UserDetails retrieveUser(String username,UsernamePasswordAuthenticationToken authentication)
    throws AuthenticationException
    {
        System.out.println("Method invoked : retrieveUser");
        //I have Username,password:
        //HOW CAN I ACCESS "myCustom1", "myCustom2" here ?
    }
}

回答1:

If you need to use additional form parameters in order to manipulate the username and password, you can implement your own AuthenticationProcessingFilter

http://static.springsource.org/spring-security/site/apidocs/org/springframework/security/ui/webapp/AuthenticationProcessingFilter.html

This class will have full access to the HttpRequest and therefore all the additional parameters you submit. If your goal is to somehow use these values to modify the username and password, this is where you would do it.



回答2:

The trick here is that you need to create a new AuthenicationToken (maybe) extending UsernameAndPasswordAuthenicationToken and as @emills says you need to then implement a new AuthenciationProcessingFilter to map the request values to the token and submit these to the AuthenicationManager.

Basically there are a couple of parts to implementing a custom authenication chain in spring-security

  • AuthenicationToken - details of the authenication request and it's result, ie contains credentials you require to authenticate
  • AuthenicationProvider - registered with the AuthenicationManager, accepts your AuthenicationToken and validates the user and returns a token with the granted authorities set
  • AuthenciationFilter - doesn't actually have to be a filter just using AbstractProcessingFilter will make your life a little easier


回答3:

I would go this way:

<bean id="authenticationProcessingFilter"  
    class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
  ...
  <property name="authenticationDetailsSource">
    <bean class="org.acegisecurity.ui.AuthenticationDetailsSourceImpl">
        <property name="clazz"  
           value="com.MyAuthenticationDetails"/>
    </bean>
  </property>
</bean>  

This is the class that holds the properties:

package com;
import javax.servlet.http.HttpServletRequest;
import org.acegisecurity.ui.WebAuthenticationDetails;
public class MyAuthenticationDetails extends WebAuthenticationDetails {
    public MyAuthenticationDetails() {
      super();
    }
    //This constructor will be invoqued by the filter
    public MyAuthenticationDetails(HttpServletRequest request) {
        super(request);
        this.myCustom1 = request.getParameter("myCustom1");
    }
    public String getMyCustom1() {
        return myCustom1;
    }
    private String myCustom1;
}

Now you have the username, password and the details.



回答4:

I've done a similar thing, but different then anyone has suggested here. I am not saying this is the "right" way to do it - but it's worked very well for me. In the Principal object there is the user and there is also a Details object in the AuthenticationToken that you can store a Map(String, String) of other login info.

public class RequestFormDeatils extends SpringSecurityFilter {

   protected void doFilterHttp(HttpServletRequest request, ...) {
      SecurityContext sec = SecurityContextHolder.getContent();
      AbstractAuthenticationToken auth = (AbstractAuthenticationToken)sec.getAuthentication();
      Map<String, String> m = new HashMap<String, String>;
      m.put("myCustom1", request.getParamtere("myCustom1"));
      m.put("myCustom2", request.getParameter("myCustom2"));
      auth.setDetails(m);
}

Now anywhere in your code you get use the SecurityContext to propagate this security related info without having to couple it to your UserDetails object, or pass it as arguments. I do this code in a SecurityFilter at the end of the Spring Security Filter chain.

<bean id="requestFormFilter" class="...RequestFormDetails">
   <custom-filter position="LAST" />
</bean> 


回答5:

All above are great and perfect solutions. But I have used a workaround kind of solution which works perfectly fine. Used multitenant id for ThreadLocal

package com.mypackage.servlet;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.springframework.util.Assert;

public class ThreadLocalContextUtil implements Filter{
     private static final ThreadLocal<Object> contextHolder =
                new ThreadLocal<Object>();

       public static void setTenantId(Object tenantId) {
          Assert.notNull(tenantId, "customerType cannot be null");
          contextHolder.set(tenantId);
       }

       public static Object getTenantId() {
          return contextHolder.get();
       }

       public static void clearTenant() {
          contextHolder.remove();
       }

    public void destroy() {

    }

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        // Set the tenant Id into a ThreadLocal object
        ThreadLocalContextUtil.setTenantId(request);
        if(chain != null)
            chain.doFilter(request, response);
        else {
            //error
        }
    }

    public void init(FilterConfig filterconfig) throws ServletException {

    }
}

spring security xml

<security:http auto-config="true" use-expressions="true" access-denied-page="/forms/auth/403" >
    <security:custom-filter before="FIRST" ref="tenantFilter" />
    ......
    </security:http>

Access request object in your Authentication Class

HttpServletRequest currRequest = (HttpServletRequest) ThreadLocalContextUtil.getTenantId();

Then use the request object to get your custom parameters