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 041 /** 042 * permissive key. 043 */ 044 public static final String PERMISSIVE = "permissive"; 045 046 047 protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception { 048 AuthenticationToken token = createToken(request, response); 049 if (token == null) { 050 String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " 051 + "must be created in order to execute a login attempt."; 052 throw new IllegalStateException(msg); 053 } 054 try { 055 Subject subject = getSubject(request, response); 056 subject.login(token); 057 return onLoginSuccess(token, subject, request, response); 058 } catch (AuthenticationException e) { 059 return onLoginFailure(token, e, request, response); 060 } 061 } 062 063 protected abstract AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception; 064 065 protected AuthenticationToken createToken(String username, String password, 066 ServletRequest request, ServletResponse response) { 067 boolean rememberMe = isRememberMe(request); 068 String host = getHost(request); 069 return createToken(username, password, rememberMe, host); 070 } 071 072 protected AuthenticationToken createToken(String username, String password, 073 boolean rememberMe, String host) { 074 return new UsernamePasswordToken(username, password, rememberMe, host); 075 } 076 077 protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, 078 ServletRequest request, ServletResponse response) throws Exception { 079 return true; 080 } 081 082 protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, 083 ServletRequest request, ServletResponse response) { 084 return false; 085 } 086 087 /** 088 * Returns the host name or IP associated with the current subject. This method is primarily provided for use 089 * during construction of an <code>AuthenticationToken</code>. 090 * <p/> 091 * The default implementation merely returns {@link ServletRequest#getRemoteHost()}. 092 * 093 * @param request the incoming ServletRequest 094 * @return the <code>InetAddress</code> to associate with the login attempt. 095 */ 096 protected String getHost(ServletRequest request) { 097 return request.getRemoteHost(); 098 } 099 100 /** 101 * Returns <code>true</code> if "rememberMe" should be enabled for the login attempt associated with the 102 * current <code>request</code>, <code>false</code> otherwise. 103 * <p/> 104 * This implementation always returns <code>false</code> and is provided as a template hook to subclasses that 105 * support <code>rememberMe</code> logins and wish to determine <code>rememberMe</code> in a custom manner 106 * based on the current <code>request</code>. 107 * 108 * @param request the incoming ServletRequest 109 * @return <code>true</code> if "rememberMe" should be enabled for the login attempt associated with the 110 * current <code>request</code>, <code>false</code> otherwise. 111 */ 112 protected boolean isRememberMe(ServletRequest request) { 113 return false; 114 } 115 116 /** 117 * Determines whether the current subject should be allowed to make the current request. 118 * <p/> 119 * The default implementation returns <code>true</code> if the user is authenticated. Will also return 120 * <code>true</code> if the {@link #isLoginRequest} returns false and the "permissive" flag is set. 121 * 122 * @return <code>true</code> if request should be allowed access 123 */ 124 @Override 125 protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { 126 return super.isAccessAllowed(request, response, mappedValue) 127 || (!isLoginRequest(request, response) && isPermissive(mappedValue)); 128 } 129 130 /** 131 * Returns <code>true</code> if the mappedValue contains the {@link #PERMISSIVE} qualifier. 132 * 133 * @return <code>true</code> if this filter should be permissive 134 */ 135 protected boolean isPermissive(Object mappedValue) { 136 if (mappedValue != null) { 137 String[] values = (String[]) mappedValue; 138 return Arrays.binarySearch(values, PERMISSIVE) >= 0; 139 } 140 return false; 141 } 142 143 /** 144 * Overrides the default behavior to call {@link #onAccessDenied} and swallow the exception if the exception is 145 * {@link UnauthenticatedException}. 146 */ 147 @Override 148 protected void cleanup(ServletRequest request, 149 ServletResponse response, Exception existing) throws ServletException, IOException { 150 if (existing instanceof UnauthenticatedException 151 || (existing instanceof ServletException && existing.getCause() instanceof UnauthenticatedException)) { 152 try { 153 onAccessDenied(request, response); 154 existing = null; 155 } catch (Exception e) { 156 existing = e; 157 } 158 } 159 super.cleanup(request, response, existing); 160 161 } 162}