How to customize a spring-security form-login. Part 2
An article from my zk integration series.
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