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