1 /* 2 * MIT License 3 * 4 * Copyright (c) 2010-2022 The Waffle Project Contributors: https://github.com/Waffle/waffle/graphs/contributors 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in all 14 * copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 */ 24 package waffle.shiro.dynamic; 25 26 import javax.servlet.ServletRequest; 27 import javax.servlet.ServletResponse; 28 29 import org.apache.shiro.authc.AuthenticationToken; 30 import org.apache.shiro.subject.Subject; 31 import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; 32 import org.slf4j.Logger; 33 import org.slf4j.LoggerFactory; 34 35 import waffle.shiro.negotiate.NegotiateAuthenticationFilter; 36 37 /** 38 * When combined with the {@link waffle.shiro.negotiate.NegotiateAuthenticationStrategy}, this filter can be used to 39 * allow a client to choose which authentication filter is used at runtime. This filter assumes the shiro.ini is 40 * configured with both the {@link waffle.shiro.negotiate.NegotiateAuthenticationRealm} and some User/Password Realm 41 * like: {@link waffle.shiro.GroupMappingWaffleRealm}. 42 * <p> 43 * Requires use of {@link waffle.shiro.negotiate.NegotiateAuthenticationStrategy} when more than one realm is configured 44 * in shiro.ini (which should be the case for multiple authentication type options). 45 * <p> 46 * To use {@link waffle.shiro.negotiate.NegotiateAuthenticationRealm}, the client must pass the parameter 47 * {@link #PARAM_NAME_AUTHTYPE} with a value of {@link #PARAM_VAL_AUTHTYPE_NEGOTIATE}. 48 * <p> 49 * Example shiro.ini snippet below: 50 * 51 * <pre> 52 * # ======================= 53 * # Shiro INI configuration 54 * # ======================= 55 * 56 * [main] 57 * 58 * # Setup custom AuthenticationRealm 59 * waffleRealmSSO = waffle.shiro.negotiate.NegotiateAuthenticationRealm 60 * waffleUserPass = waffle.shiro.GroupMappingWaffleRealm 61 * securityManager.realms = $waffleRealmSSO, $waffleUserPass 62 * 63 * 64 * # Use the configured native session manager: 65 * sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager 66 * securityManager.sessionManager = $sessionManager 67 * 68 * # the following call is only necessary in a web-configured ShiroFilter (otherwise 69 * # a native session manager is already enabled): 70 * securityManager.sessionMode = native 71 * 72 * 73 * # cookie for single sign on 74 * cookie = org.apache.shiro.web.servlet.SimpleCookie 75 * cookie.name = SSOcookie 76 * cookie.path = / 77 * securityManager.sessionManager.sessionIdCookie = $cookie 78 * 79 * 80 * authcStrategy = waffle.shiro.negotiate.NegotiateAuthenticationStrategy 81 * securityManager.authenticator.authenticationStrategy = $authcStrategy 82 * 83 * # Waffle filter 84 * waffleFilter = waffle.shiro.dynamic.DynamicAuthenticationFilter 85 * 86 * #Configure filter chains and filter parameters 87 * authc.loginUrl = /login.jsp 88 * waffleFilter.loginUrl = /login.jsp 89 * logout.redirectUrl = login.jsp 90 * 91 * ... 92 * 93 * [urls] 94 * # The 'urls' section is used for url-based security 95 * /logout = logout 96 * /* = waffleFilter 97 * </pre> 98 * 99 * @author Dan Rollo Date: 2/21/13 Time: 9:08 PM 100 */ 101 public class DynamicAuthenticationFilter extends FormAuthenticationFilter { 102 103 /** The Constant LOGGER. */ 104 private static final Logger LOGGER = LoggerFactory.getLogger(DynamicAuthenticationFilter.class); 105 106 /** The Constant PARAM_NAME_AUTHTYPE. */ 107 public static final String PARAM_NAME_AUTHTYPE = "authType"; 108 109 /** The Constant PARAM_VAL_AUTHTYPE_NEGOTIATE. */ 110 public static final String PARAM_VAL_AUTHTYPE_NEGOTIATE = "j_negotiate"; 111 112 /** 113 * Wrapper to make protected methods in different package callable from here. 114 */ 115 private static final class WrapNegotiateAuthenticationFilter extends NegotiateAuthenticationFilter { 116 117 /** The parent. */ 118 private final DynamicAuthenticationFilter parent; 119 120 /** 121 * Instantiates a new wrap negotiate authentication filter. 122 * 123 * @param newParent 124 * the new parent 125 */ 126 WrapNegotiateAuthenticationFilter(final DynamicAuthenticationFilter newParent) { 127 this.parent = newParent; 128 } 129 130 @Override 131 public boolean onAccessDenied(final ServletRequest request, final ServletResponse response) throws Exception { 132 return super.onAccessDenied(request, response); 133 } 134 135 @Override 136 protected boolean onLoginSuccess(final AuthenticationToken token, final Subject subject, 137 final ServletRequest request, final ServletResponse response) throws Exception { 138 return this.parent.onLoginSuccess(token, subject, request, response); 139 } 140 } 141 142 /** The filter negotiate. */ 143 private final WrapNegotiateAuthenticationFilter filterNegotiate = new WrapNegotiateAuthenticationFilter(this); 144 145 /** 146 * Wrapper to make protected methods in different package callable from here. 147 */ 148 private static final class WrapFormAuthenticationFilter extends FormAuthenticationFilter { 149 150 /** The parent. */ 151 private final DynamicAuthenticationFilter parent; 152 153 /** 154 * Instantiates a new wrap form authentication filter. 155 * 156 * @param newParent 157 * the new parent 158 */ 159 WrapFormAuthenticationFilter(final DynamicAuthenticationFilter newParent) { 160 this.parent = newParent; 161 } 162 163 @Override 164 public boolean onAccessDenied(final ServletRequest request, final ServletResponse response) throws Exception { 165 return super.onAccessDenied(request, response); 166 } 167 168 @Override 169 protected boolean onLoginSuccess(final AuthenticationToken token, final Subject subject, 170 final ServletRequest request, final ServletResponse response) throws Exception { 171 return this.parent.onLoginSuccess(token, subject, request, response); 172 } 173 } 174 175 /** The filter form authc. */ 176 private final WrapFormAuthenticationFilter filterFormAuthc = new WrapFormAuthenticationFilter(this); 177 178 /** 179 * Call 180 * {@link org.apache.shiro.web.filter.AccessControlFilter#onAccessDenied(javax.servlet.ServletRequest, javax.servlet.ServletResponse)} 181 * for the user selected authentication type, which performs login logic. 182 * <p> 183 * {@inheritDoc} 184 */ 185 @Override 186 protected boolean executeLogin(final ServletRequest request, final ServletResponse response) throws Exception { 187 if (this.isAuthTypeNegotiate(request)) { 188 DynamicAuthenticationFilter.LOGGER.debug("using filterNegotiate"); 189 return this.filterNegotiate.onAccessDenied(request, response); 190 } 191 DynamicAuthenticationFilter.LOGGER.debug("using filterFormAuthc"); 192 return this.filterFormAuthc.onAccessDenied(request, response); 193 } 194 195 /** 196 * Checks if is auth type negotiate. 197 * 198 * @param request 199 * the request 200 * 201 * @return true, if is auth type negotiate 202 */ 203 boolean isAuthTypeNegotiate(final ServletRequest request) { 204 final String authType = request.getParameter(DynamicAuthenticationFilter.PARAM_NAME_AUTHTYPE); 205 return authType != null && DynamicAuthenticationFilter.PARAM_VAL_AUTHTYPE_NEGOTIATE.equalsIgnoreCase(authType); 206 } 207 208 }