View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.shiro.web.filter.authc;
20  
21  import org.apache.shiro.authc.AuthenticationException;
22  import org.apache.shiro.authc.AuthenticationToken;
23  import org.apache.shiro.authc.UsernamePasswordToken;
24  import org.apache.shiro.authz.UnauthenticatedException;
25  import org.apache.shiro.subject.Subject;
26  
27  import javax.servlet.ServletException;
28  import javax.servlet.ServletRequest;
29  import javax.servlet.ServletResponse;
30  import java.io.IOException;
31  import java.util.Arrays;
32  
33  /**
34   * An <code>AuthenticationFilter</code> that is capable of automatically performing an authentication attempt
35   * based on the incoming request.
36   *
37   * @since 0.9
38   */
39  public abstract class AuthenticatingFilter extends AuthenticationFilter {
40      public static final String PERMISSIVE = "permissive";
41  
42      //TODO - complete JavaDoc
43  
44      protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
45          AuthenticationToken token = createToken(request, response);
46          if (token == null) {
47              String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " +
48                      "must be created in order to execute a login attempt.";
49              throw new IllegalStateException(msg);
50          }
51          try {
52              Subject subject = getSubject(request, response);
53              subject.login(token);
54              return onLoginSuccess(token, subject, request, response);
55          } catch (AuthenticationException e) {
56              return onLoginFailure(token, e, request, response);
57          }
58      }
59  
60      protected abstract AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception;
61  
62      protected AuthenticationToken createToken(String username, String password,
63                                                ServletRequest request, ServletResponse response) {
64          boolean rememberMe = isRememberMe(request);
65          String host = getHost(request);
66          return createToken(username, password, rememberMe, host);
67      }
68  
69      protected AuthenticationToken createToken(String username, String password,
70                                                boolean rememberMe, String host) {
71          return new UsernamePasswordToken(username, password, rememberMe, host);
72      }
73  
74      protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
75                                       ServletRequest request, ServletResponse response) throws Exception {
76          return true;
77      }
78  
79      protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e,
80                                       ServletRequest request, ServletResponse response) {
81          return false;
82      }
83  
84      /**
85       * Returns the host name or IP associated with the current subject.  This method is primarily provided for use
86       * during construction of an <code>AuthenticationToken</code>.
87       * <p/>
88       * The default implementation merely returns {@link ServletRequest#getRemoteHost()}.
89       *
90       * @param request the incoming ServletRequest
91       * @return the <code>InetAddress</code> to associate with the login attempt.
92       */
93      protected String getHost(ServletRequest request) {
94          return request.getRemoteHost();
95      }
96  
97      /**
98       * Returns <code>true</code> if &quot;rememberMe&quot; should be enabled for the login attempt associated with the
99       * 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 &quot;rememberMe&quot; 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 &quot;permissive&quot; 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 }