1 /** 2 * Waffle (https://github.com/dblock/waffle) 3 * 4 * Copyright (c) 2010 - 2015 Application Security, Inc. 5 * 6 * All rights reserved. This program and the accompanying materials 7 * are made available under the terms of the Eclipse Public License v1.0 8 * which accompanies this distribution, and is available at 9 * http://www.eclipse.org/legal/epl-v10.html 10 * 11 * Contributors: 12 * Application Security, Inc. 13 */ 14 package waffle.shiro.dynamic; 15 16 import waffle.shiro.negotiate.NegotiateAuthenticationFilter; 17 import org.apache.shiro.authc.AuthenticationToken; 18 import org.apache.shiro.subject.Subject; 19 import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; 20 import org.slf4j.Logger; 21 import org.slf4j.LoggerFactory; 22 23 import javax.servlet.ServletRequest; 24 import javax.servlet.ServletResponse; 25 26 /** 27 * When combined with the {@link waffle.shiro.negotiate.NegotiateAuthenticationStrategy}, this filter can be used to 28 * allow a client to choose which authentication filter is used at runtime. This filter assumes the shiro.ini is 29 * configured with both the {@link waffle.shiro.negotiate.NegotiateAuthenticationRealm} and some User/Password Realm 30 * like: {@link waffle.shiro.GroupMappingWaffleRealm}. 31 * 32 * Requires use of {@link waffle.shiro.negotiate.NegotiateAuthenticationStrategy} when more than one realm is configured 33 * in shiro.ini (which should be the case for multiple authentication type options). 34 * 35 * To use {@link waffle.shiro.negotiate.NegotiateAuthenticationRealm}, the client must pass the parameter 36 * {@link #PARAM_NAME_AUTHTYPE} with a value of {@link #PARAM_VAL_AUTHTYPE_NEGOTIATE}. 37 * 38 * Example shiro.ini snippet below: 39 * 40 * <pre> 41 * # ======================= 42 * # Shiro INI configuration 43 * # ======================= 44 * 45 * [main] 46 * 47 * # Setup custom AuthenticationRealm 48 * waffleRealmSSO = waffle.shiro.negotiate.NegotiateAuthenticationRealm 49 * waffleUserPass = waffle.shiro.GroupMappingWaffleRealm 50 * securityManager.realms = $waffleRealmSSO, $waffleUserPass 51 * 52 * 53 * # Use the configured native session manager: 54 * sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager 55 * securityManager.sessionManager = $sessionManager 56 * 57 * # the following call is only necessary in a web-configured ShiroFilter (otherwise 58 * # a native session manager is already enabled): 59 * securityManager.sessionMode = native 60 * 61 * 62 * # cookie for single sign on 63 * cookie = org.apache.shiro.web.servlet.SimpleCookie 64 * cookie.name = SSOcookie 65 * cookie.path = / 66 * securityManager.sessionManager.sessionIdCookie = $cookie 67 * 68 * 69 * authcStrategy = waffle.shiro.negotiate.NegotiateAuthenticationStrategy 70 * securityManager.authenticator.authenticationStrategy = $authcStrategy 71 * 72 * # Waffle filter 73 * waffleFilter = waffle.shiro.dynamic.DynamicAuthenticationFilter 74 * 75 * #Configure filter chains and filter parameters 76 * authc.loginUrl = /login.jsp 77 * waffleFilter.loginUrl = /login.jsp 78 * logout.redirectUrl = login.jsp 79 * 80 * ... 81 * 82 * [urls] 83 * # The 'urls' section is used for url-based security 84 * /logout = logout 85 * /* = waffleFilter 86 * 87 * </pre> 88 * 89 * @author Dan Rollo Date: 2/21/13 Time: 9:08 PM 90 */ 91 public class DynamicAuthenticationFilter extends FormAuthenticationFilter { 92 93 /** The Constant LOGGER. */ 94 private static final Logger LOGGER = LoggerFactory 95 .getLogger(DynamicAuthenticationFilter.class); 96 97 /** The Constant PARAM_NAME_AUTHTYPE. */ 98 public static final String PARAM_NAME_AUTHTYPE = "authType"; 99 100 /** The Constant PARAM_VAL_AUTHTYPE_NEGOTIATE. */ 101 public static final String PARAM_VAL_AUTHTYPE_NEGOTIATE = "j_negotiate"; 102 103 /** 104 * Wrapper to make protected methods in different package callable from here. 105 */ 106 private static final class WrapNegotiateAuthenticationFilter extends NegotiateAuthenticationFilter { 107 108 /** The parent. */ 109 private final DynamicAuthenticationFilter parent; 110 111 /** 112 * Instantiates a new wrap negotiate authentication filter. 113 * 114 * @param newParent 115 * the new parent 116 */ 117 WrapNegotiateAuthenticationFilter(final DynamicAuthenticationFilter newParent) { 118 this.parent = newParent; 119 } 120 121 /* (non-Javadoc) 122 * @see waffle.shiro.negotiate.NegotiateAuthenticationFilter#onAccessDenied(javax.servlet.ServletRequest, javax.servlet.ServletResponse) 123 */ 124 @Override 125 public boolean onAccessDenied(final ServletRequest request, final ServletResponse response) throws Exception { 126 return super.onAccessDenied(request, response); 127 } 128 129 /* (non-Javadoc) 130 * @see waffle.shiro.negotiate.NegotiateAuthenticationFilter#onLoginSuccess(org.apache.shiro.authc.AuthenticationToken, org.apache.shiro.subject.Subject, javax.servlet.ServletRequest, javax.servlet.ServletResponse) 131 */ 132 @Override 133 protected boolean onLoginSuccess(final AuthenticationToken token, final Subject subject, 134 final ServletRequest request, final ServletResponse response) throws Exception { 135 return this.parent.onLoginSuccess(token, subject, request, response); 136 } 137 } 138 139 /** The filter negotiate. */ 140 private final WrapNegotiateAuthenticationFilter filterNegotiate = new WrapNegotiateAuthenticationFilter(this); 141 142 /** 143 * Wrapper to make protected methods in different package callable from here. 144 */ 145 private static final class WrapFormAuthenticationFilter extends FormAuthenticationFilter { 146 147 /** The parent. */ 148 private final DynamicAuthenticationFilter parent; 149 150 /** 151 * Instantiates a new wrap form authentication filter. 152 * 153 * @param newParent 154 * the new parent 155 */ 156 WrapFormAuthenticationFilter(final DynamicAuthenticationFilter newParent) { 157 this.parent = newParent; 158 } 159 160 /* (non-Javadoc) 161 * @see org.apache.shiro.web.filter.authc.FormAuthenticationFilter#onAccessDenied(javax.servlet.ServletRequest, javax.servlet.ServletResponse) 162 */ 163 @Override 164 public boolean onAccessDenied(final ServletRequest request, final ServletResponse response) throws Exception { 165 return super.onAccessDenied(request, response); 166 } 167 168 /* (non-Javadoc) 169 * @see org.apache.shiro.web.filter.authc.FormAuthenticationFilter#onLoginSuccess(org.apache.shiro.authc.AuthenticationToken, org.apache.shiro.subject.Subject, javax.servlet.ServletRequest, javax.servlet.ServletResponse) 170 */ 171 @Override 172 protected boolean onLoginSuccess(final AuthenticationToken token, final Subject subject, 173 final ServletRequest request, final ServletResponse response) throws Exception { 174 return this.parent.onLoginSuccess(token, subject, request, response); 175 } 176 } 177 178 /** The filter form authc. */ 179 private final WrapFormAuthenticationFilter filterFormAuthc = new WrapFormAuthenticationFilter(this); 180 181 /** 182 * Call 183 * {@link org.apache.shiro.web.filter.AccessControlFilter#onAccessDenied(javax.servlet.ServletRequest, javax.servlet.ServletResponse)} 184 * for the user selected authentication type, which performs login logic. 185 * 186 * {@inheritDoc} 187 */ 188 @Override 189 protected boolean executeLogin(final ServletRequest request, final ServletResponse response) throws Exception { 190 if (this.isAuthTypeNegotiate(request)) { 191 DynamicAuthenticationFilter.LOGGER.debug("using filterNegotiate"); 192 return this.filterNegotiate.onAccessDenied(request, response); 193 } 194 DynamicAuthenticationFilter.LOGGER.debug("using filterFormAuthc"); 195 return this.filterFormAuthc.onAccessDenied(request, response); 196 } 197 198 /** 199 * Checks if is auth type negotiate. 200 * 201 * @param request 202 * the request 203 * @return true, if is auth type negotiate 204 */ 205 boolean isAuthTypeNegotiate(final ServletRequest request) { 206 final String authType = request.getParameter(DynamicAuthenticationFilter.PARAM_NAME_AUTHTYPE); 207 return authType != null && DynamicAuthenticationFilter.PARAM_VAL_AUTHTYPE_NEGOTIATE.equalsIgnoreCase(authType); 208 } 209 210 }