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 */ 019package org.apache.shiro.web.filter.authc; 020 021import org.apache.shiro.authc.AuthenticationException; 022import org.apache.shiro.authc.AuthenticationToken; 023import org.apache.shiro.authc.UsernamePasswordToken; 024import org.apache.shiro.authz.UnauthenticatedException; 025import org.apache.shiro.subject.Subject; 026 027import javax.servlet.ServletException; 028import javax.servlet.ServletRequest; 029import javax.servlet.ServletResponse; 030import java.io.IOException; 031import 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 */ 039public 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}