View Javadoc
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 }