001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.apache.shiro.web.filter.authc;
020
021 import org.apache.shiro.authc.AuthenticationException;
022 import org.apache.shiro.authc.AuthenticationToken;
023 import org.apache.shiro.authc.UsernamePasswordToken;
024 import org.apache.shiro.authz.UnauthenticatedException;
025 import org.apache.shiro.subject.Subject;
026
027 import javax.servlet.ServletException;
028 import javax.servlet.ServletRequest;
029 import javax.servlet.ServletResponse;
030 import java.io.IOException;
031 import java.util.Arrays;
032
033 /**
034 * An <code>AuthenticationFilter</code> that is capable of automatically performing an authentication attempt
035 * based on the incoming request.
036 *
037 * @since 0.9
038 */
039 public abstract class AuthenticatingFilter extends AuthenticationFilter {
040 public static final String PERMISSIVE = "permissive";
041
042 //TODO - complete JavaDoc
043
044 protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
045 AuthenticationToken token = createToken(request, response);
046 if (token == null) {
047 String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " +
048 "must be created in order to execute a login attempt.";
049 throw new IllegalStateException(msg);
050 }
051 try {
052 Subject subject = getSubject(request, response);
053 subject.login(token);
054 return onLoginSuccess(token, subject, request, response);
055 } catch (AuthenticationException e) {
056 return onLoginFailure(token, e, request, response);
057 }
058 }
059
060 protected abstract AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception;
061
062 protected AuthenticationToken createToken(String username, String password,
063 ServletRequest request, ServletResponse response) {
064 boolean rememberMe = isRememberMe(request);
065 String host = getHost(request);
066 return createToken(username, password, rememberMe, host);
067 }
068
069 protected AuthenticationToken createToken(String username, String password,
070 boolean rememberMe, String host) {
071 return new UsernamePasswordToken(username, password, rememberMe, host);
072 }
073
074 protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
075 ServletRequest request, ServletResponse response) throws Exception {
076 return true;
077 }
078
079 protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e,
080 ServletRequest request, ServletResponse response) {
081 return false;
082 }
083
084 /**
085 * Returns the host name or IP associated with the current subject. This method is primarily provided for use
086 * during construction of an <code>AuthenticationToken</code>.
087 * <p/>
088 * The default implementation merely returns {@link ServletRequest#getRemoteHost()}.
089 *
090 * @param request the incoming ServletRequest
091 * @return the <code>InetAddress</code> to associate with the login attempt.
092 */
093 protected String getHost(ServletRequest request) {
094 return request.getRemoteHost();
095 }
096
097 /**
098 * Returns <code>true</code> if "rememberMe" should be enabled for the login attempt associated with the
099 * current <code>request</code>, <code>false</code> otherwise.
100 * <p/>
101 * This implementation always returns <code>false</code> and is provided as a template hook to subclasses that
102 * support <code>rememberMe</code> logins and wish to determine <code>rememberMe</code> in a custom mannner
103 * based on the current <code>request</code>.
104 *
105 * @param request the incoming ServletRequest
106 * @return <code>true</code> if "rememberMe" should be enabled for the login attempt associated with the
107 * current <code>request</code>, <code>false</code> otherwise.
108 */
109 protected boolean isRememberMe(ServletRequest request) {
110 return false;
111 }
112
113 /**
114 * Determines whether the current subject should be allowed to make the current request.
115 * <p/>
116 * The default implementation returns <code>true</code> if the user is authenticated. Will also return
117 * <code>true</code> if the {@link #isLoginRequest} returns false and the "permissive" flag is set.
118 *
119 * @return <code>true</code> if request should be allowed access
120 */
121 @Override
122 protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
123 return super.isAccessAllowed(request, response, mappedValue) ||
124 (!isLoginRequest(request, response) && isPermissive(mappedValue));
125 }
126
127 /**
128 * Returns <code>true</code> if the mappedValue contains the {@link #PERMISSIVE} qualifier.
129 *
130 * @return <code>true</code> if this filter should be permissive
131 */
132 protected boolean isPermissive(Object mappedValue) {
133 if(mappedValue != null) {
134 String[] values = (String[]) mappedValue;
135 return Arrays.binarySearch(values, PERMISSIVE) >= 0;
136 }
137 return false;
138 }
139
140 /**
141 * Overrides the default behavior to call {@link #onAccessDenied} and swallow the exception if the exception is
142 * {@link UnauthenticatedException}.
143 */
144 @Override
145 protected void cleanup(ServletRequest request, ServletResponse response, Exception existing) throws ServletException, IOException {
146 if (existing instanceof UnauthenticatedException || (existing instanceof ServletException && existing.getCause() instanceof UnauthenticatedException))
147 {
148 try {
149 onAccessDenied(request, response);
150 existing = null;
151 } catch (Exception e) {
152 existing = e;
153 }
154 }
155 super.cleanup(request, response, existing);
156
157 }
158 }