blob: e8fbaf7906fe8e5ba511e803bba172128c210f2f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.ki.web.filter.authc;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.ki.authc.AuthenticationException;
import org.apache.ki.authc.AuthenticationToken;
import org.apache.ki.authc.UsernamePasswordToken;
import org.apache.ki.subject.Subject;
import org.apache.ki.web.WebUtils;
/**
* Requires the requesting user to be authenticated for the request to continue, and if they are not, forces the user
* to login via by redirecting them to the {@link #setLoginUrl(String) loginUrl} you configure.
*
* <p>This filter constructs a {@link UsernamePasswordToken UsernamePasswordToken} with the values found in
* {@link #setUsernameParam(String) username}, {@link #setPasswordParam(String) password},
* and {@link #setRememberMeParam(String) rememberMe} request parameters. It then calls
* {@link org.apache.ki.subject.Subject#login(org.apache.ki.authc.AuthenticationToken) Subject.login(usernamePasswordToken)},
* effectively automatically performing a login attempt. Note that the login attempt will only occur when the
* {@link #isLoginSubmission(javax.servlet.ServletRequest, javax.servlet.ServletResponse) isLoginSubmission(request,response)}
* is <code>true</code>, which by default occurs when the request is for the {@link #setLoginUrl(String) loginUrl} and
* is a POST request.
*
* <p>If the login attempt fails, the resulting <code>AuthenticationException</code> fully qualified class name will
* be set as a request attribute under the {@link #setFailureKeyAttribute(String) failureKeyAttribute} key. This
* FQCN can be used as an i18n key or lookup mechanism to explain to the user why their login attempt failed
* (e.g. no account, incorrect password, etc).
*
* <p>If you would prefer to handle the authentication validation and login in your own code, consider using the
* {@link PassThruAuthenticationFilter} instead, which allows requests to the
* {@link #loginUrl} to pass through to your application's code directly.
*
* @author Les Hazlewood
* @author Jeremy Haile
* @see PassThruAuthenticationFilter
* @since 0.9
*/
public class FormAuthenticationFilter extends AuthenticatingFilter {
//TODO - complete JavaDoc
public static final String DEFAULT_ERROR_KEY_ATTRIBUTE_NAME = "jsecLoginFailure";
public static final String DEFAULT_USERNAME_PARAM = "username";
public static final String DEFAULT_PASSWORD_PARAM = "password";
public static final String DEFAULT_REMEMBER_ME_PARAM = "rememberMe";
private static final Logger log = LoggerFactory.getLogger(FormAuthenticationFilter.class);
private String usernameParam = DEFAULT_USERNAME_PARAM;
private String passwordParam = DEFAULT_PASSWORD_PARAM;
private String rememberMeParam = DEFAULT_REMEMBER_ME_PARAM;
private String failureKeyAttribute = DEFAULT_ERROR_KEY_ATTRIBUTE_NAME;
public FormAuthenticationFilter() {
setLoginUrl(DEFAULT_LOGIN_URL);
}
public String getUsernameParam() {
return usernameParam;
}
/**
* Sets the request parameter name to look for when acquiring the username. Unless overridden by calling this
* method, the default is <code>username</code>.
*
* @param usernameParam the name of the request param to check for acquiring the username.
*/
public void setUsernameParam(String usernameParam) {
this.usernameParam = usernameParam;
}
public String getPasswordParam() {
return passwordParam;
}
/**
* Sets the request parameter name to look for when acquiring the password. Unless overridden by calling this
* method, the default is <code>password</code>.
*
* @param passwordParam the name of the request param to check for acquiring the password.
*/
public void setPasswordParam(String passwordParam) {
this.passwordParam = passwordParam;
}
public String getRememberMeParam() {
return rememberMeParam;
}
/**
* Sets the request parameter name to look for when acquiring the rememberMe boolean value. Unless overridden
* by calling this method, the default is <code>rememberMe</code>.
* <p/>
* RememberMe will be <code>true</code> if the parameter value equals any of those supported by
* {@link WebUtils#isTrue(javax.servlet.ServletRequest, String) WebUtils.isTrue(request,value)}, <code>false</code>
* otherwise.
*
* @param rememberMeParam the name of the request param to check for acquiring the rememberMe boolean value.
*/
public void setRememberMeParam(String rememberMeParam) {
this.rememberMeParam = rememberMeParam;
}
public String getFailureKeyAttribute() {
return failureKeyAttribute;
}
public void setFailureKeyAttribute(String failureKeyAttribute) {
this.failureKeyAttribute = failureKeyAttribute;
}
@Override
protected void onFilterConfigSet() throws Exception {
if (log.isTraceEnabled()) {
log.trace("Adding default login url to applied paths.");
}
this.appliedPaths.put(getLoginUrl(), null);
}
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
if (isLoginRequest(request, response)) {
if (isLoginSubmission(request, response)) {
if (log.isTraceEnabled()) {
log.trace("Login submission detected. Attempting to execute login.");
}
return executeLogin(request, response);
} else {
if (log.isTraceEnabled()) {
log.trace("Login page view.");
}
//allow them to see the login page ;)
return true;
}
} else {
if (log.isTraceEnabled()) {
log.trace("Attempting to access a path which requires authentication. Forwarding to the " +
"Authentication url [" + getLoginUrl() + "]");
}
saveRequestAndRedirectToLogin(request, response);
return false;
}
}
/**
* This default implementation merely returns <code>true</code> if the request is an HTTP <code>POST</code>,
* <code>false</code> otherwise. Can be overridden by subclasses for custom login submission detection behavior.
*
* @param request the incoming ServletRequest
* @param response the outgoing ServletResponse.
* @return <code>true</code> if the request is an HTTP <code>POST</code>, <code>false</code> otherwise.
*/
protected boolean isLoginSubmission(ServletRequest request, ServletResponse response) {
return (request instanceof HttpServletRequest) && WebUtils.toHttp(request).getMethod().equalsIgnoreCase(POST_METHOD);
}
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
String username = getUsername(request);
String password = getPassword(request);
return createToken(username, password, request, response);
}
protected boolean isRememberMe(ServletRequest request) {
return WebUtils.isTrue(request, getRememberMeParam());
}
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
ServletRequest request, ServletResponse response) throws Exception {
issueSuccessRedirect(request, response);
//we handled the success redirect directly, prevent the chain from continuing:
return false;
}
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e,
ServletRequest request, ServletResponse response) {
setFailureAttribute(request, e);
//login failed, let request continue back to the login page:
return true;
}
protected void setFailureAttribute(ServletRequest request, AuthenticationException ae) {
String className = ae.getClass().getName();
request.setAttribute(getFailureKeyAttribute(), className);
}
protected String getUsername(ServletRequest request) {
return WebUtils.getCleanParam(request, getUsernameParam());
}
protected String getPassword(ServletRequest request) {
return WebUtils.getCleanParam(request, getPasswordParam());
}
}