How to customize a spring-security form-login. Part 2

An article from my zk integration series.

part 1
part 3
part 4
part 5

Bellow are the used main classes for a spring-security form-login:

UsernamePasswordAuthenticationToken.java:
This class is an authentication implementation that represents a simple username and password. We modifiy it for the TenantId and change the name to UsernamePasswordTenantAuthenticationToken.java

UsernamePasswordTenantAuthenticationToken.java

@SuppressWarnings("serial")
public class UsernamePasswordTenantAuthenticationToken extends AbstractAuthenticationToken {
	// ~ Instance fields
	// ================================================================================================

	private final Object credentials;
	private final Object principal;
	private final Object tenantId;

	// ~ Constructors
	// ===================================================================================================

	/**
	 * This constructor can be safely used by any code that wishes to create a
	 * <code>UsernamePasswordAuthenticationToken</code>, as the
	 * {@link #isAuthenticated()} will return <code>false</code>.
	 * 
	 */
	public UsernamePasswordTenantAuthenticationToken(Object principal, Object credentials, Object tenantId) {
		super(null);
		this.principal = principal;
		this.credentials = credentials;
		this.tenantId = tenantId;
		setAuthenticated(false);
	}

	/**
	 * @deprecated use the list of authorities version
	 */
	public UsernamePasswordTenantAuthenticationToken(Object principal, Object credentials, GrantedAuthority[] authorities) {
		this(principal, credentials, Arrays.asList(authorities));
	}

	/**
	 * This constructor should only be used by
	 * <code>AuthenticationManager</code> or <code>AuthenticationProvider</code>
	 * implementations that are satisfied with producing a trusted (i.e.
	 * {@link #isAuthenticated()} = <code>true</code>) authentication token.
	 * 
	 * @param principal
	 * @param credentials
	 * @param authorities
	 */
	public UsernamePasswordTenantAuthenticationToken(Object principal, Object credentials, Collection<GrantedAuthority> authorities, Object tenantId) {
		super(authorities);
		this.principal = principal;
		this.credentials = credentials;
		this.tenantId = tenantId;
		super.setAuthenticated(true); // must use super, as we override
	}

	// ~ Methods
	// ========================================================================================================

	public Object getCredentials() {
		return this.credentials;
	}

	public Object getPrincipal() {
		return this.principal;
	}

	public Object getTenantId() {
		return this.tenantId;
	}

	public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
		if (isAuthenticated) {
			throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
		}

		super.setAuthenticated(false);
	}
}

UserPasswordAuthenticationFilter.java
This class processes the submission of an authentication form, means our ZKLoginDialog . This filter responds per default to the /j_spring_security_check URL.

UserPasswordAuthenticationFilter.java

public class UserPasswordTenantAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

	// ~ Static fields/initializers
	// =====================================================================================

	public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_username";
	public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "j_password";
	public static final String SPRING_SECURITY_FORM_TENANTID_KEY = "j_tenantid";
	public static final String SPRING_SECURITY_LAST_USERNAME_KEY = "SPRING_SECURITY_LAST_USERNAME";

	private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
	private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
	private String tenantParameter = SPRING_SECURITY_FORM_TENANTID_KEY;
	private boolean postOnly = true;

	// ~ Constructors
	// ===================================================================================================

	public UserPasswordTenantAuthenticationFilter() {
		super("/j_spring_security_check");
	}

	// ~ Methods
	// ========================================================================================================

	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
		if (postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
		}

		String username = obtainUsername(request);
		String password = obtainPassword(request);
		String tenantId = obtainTenantId(request);

		if (username == null) {
			username = "";
		}

		if (password == null) {
			password = "";
		}

		if (tenantId == null) {
			tenantId = "";
		}

		username = username.trim();
		tenantId = tenantId.trim();

		UsernamePasswordTenantAuthenticationToken authRequest = new UsernamePasswordTenantAuthenticationToken(username, password, null, tenantId);

		// Place the last username attempted into HttpSession for views
		HttpSession session = request.getSession(false);

		if (session != null || getAllowSessionCreation()) {
			request.getSession().setAttribute(SPRING_SECURITY_LAST_USERNAME_KEY, TextEscapeUtils.escapeEntities(username));
		}

		// Allow subclasses to set the "details" property
		setDetails(request, authRequest);

		return this.getAuthenticationManager().authenticate(authRequest);
	}

	/**
	 * Enables subclasses to override the composition of the password, such as
	 * by including additional values and a separator.
	 * <p>
	 * This might be used for example if a postcode/zipcode was required in
	 * addition to the password. A delimiter such as a pipe (|) should be used
	 * to separate the password and extended value(s). The
	 * <code>AuthenticationDao</code> will need to generate the expected
	 * password in a corresponding manner.
	 * </p>
	 * 
	 * @param request
	 *            so that request attributes can be retrieved
	 * 
	 * @return the password that will be presented in the
	 *         <code>Authentication</code> request token to the
	 *         <code>AuthenticationManager</code>
	 */
	protected String obtainPassword(HttpServletRequest request) {
		return request.getParameter(passwordParameter);
	}

	/**
	 * Enables subclasses to override the composition of the username, such as
	 * by including additional values and a separator.
	 * 
	 * @param request
	 *            so that request attributes can be retrieved
	 * 
	 * @return the username that will be presented in the
	 *         <code>Authentication</code> request token to the
	 *         <code>AuthenticationManager</code>
	 */
	protected String obtainUsername(HttpServletRequest request) {
		return request.getParameter(usernameParameter);
	}

	/**
	 * Enables subclasses to override the composition of the tenantID, such as
	 * by including additional values and a separator.
	 * 
	 * @param request
	 *            so that request attributes can be retrieved
	 * 
	 * @return the tenantID that will be presented in the
	 *         <code>Authentication</code> request token to the
	 *         <code>AuthenticationManager</code>
	 */
	protected String obtainTenantId(HttpServletRequest request) {
		return request.getParameter(tenantParameter);
	}

	/**
	 * Provided so that subclasses may configure what is put into the
	 * authentication request's details property.
	 * 
	 * @param request
	 *            that an authentication request is being created for
	 * @param authRequest
	 *            the authentication request object that should have its details
	 *            set
	 */
	protected void setDetails(HttpServletRequest request, UsernamePasswordTenantAuthenticationToken authRequest) {
		authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
	}

	/**
	 * Sets the parameter name which will be used to obtain the username from
	 * the login request.
	 * 
	 * @param usernameParameter
	 *            the parameter name. Defaults to "j_username".
	 */
	public void setUsernameParameter(String usernameParameter) {
		Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
		this.usernameParameter = usernameParameter;
	}

	/**
	 * Sets the parameter name which will be used to obtain the password from
	 * the login request..
	 * 
	 * @param passwordParameter
	 *            the parameter name. Defaults to "j_password".
	 */
	public void setPasswordParameter(String passwordParameter) {
		Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
		this.passwordParameter = passwordParameter;
	}

	/**
	 * Sets the parameter name which will be used to obtain the password from
	 * the login request..
	 * 
	 * @param passwordParameter
	 *            the parameter name. Defaults to "j_password".
	 */
	public void setTenantIdParameter(String tenantParameter) {
		Assert.hasText(tenantParameter, "Tenant ID parameter must not be empty or null");
		this.tenantParameter = tenantParameter;
	}

	/**
	 * Defines whether only HTTP POST requests will be allowed by this filter.
	 * If set to true, and an authentication request is received which is not a
	 * POST request, an exception will be raised immediately and authentication
	 * will not be attempted. The <tt>unsuccessfulAuthentication()</tt> method
	 * will be called as if handling a failed authentication.
	 * <p>
	 * Defaults to <tt>true</tt> but may be overridden by subclasses.
	 */
	public void setPostOnly(boolean postOnly) {
		this.postOnly = postOnly;
	}

	public final String getUsernameParameter() {
		return usernameParameter;
	}

	public final String getPasswordParameter() {
		return passwordParameter;
	}

	public final String getTenantParameter() {
		return tenantParameter;
	}
}

Part 3 of this article you can read here.

Samples are hostet in the Zksample2 project on

Have fun with it.

Stephan Gerth

Dipl.rer.pol.


PS: Help to prevent the global warming by writing cool software

Leave a Reply

You must be logged in to post a comment.