Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
DefaultSecurityManager |
|
| 2.3529411764705883;2.353 |
1 | /* | |
2 | * Licensed to the Apache Software Foundation (ASF) under one | |
3 | * or more contributor license agreements. See the NOTICE file | |
4 | * distributed with this work for additional information | |
5 | * regarding copyright ownership. The ASF licenses this file | |
6 | * to you under the Apache License, Version 2.0 (the | |
7 | * "License"); you may not use this file except in compliance | |
8 | * with the License. You may obtain a copy of the License at | |
9 | * | |
10 | * http://www.apache.org/licenses/LICENSE-2.0 | |
11 | * | |
12 | * Unless required by applicable law or agreed to in writing, | |
13 | * software distributed under the License is distributed on an | |
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
15 | * KIND, either express or implied. See the License for the | |
16 | * specific language governing permissions and limitations | |
17 | * under the License. | |
18 | */ | |
19 | package org.apache.shiro.mgt; | |
20 | ||
21 | import org.apache.shiro.authc.*; | |
22 | import org.apache.shiro.authz.Authorizer; | |
23 | import org.apache.shiro.realm.Realm; | |
24 | import org.apache.shiro.session.InvalidSessionException; | |
25 | import org.apache.shiro.session.Session; | |
26 | import org.apache.shiro.session.mgt.DefaultSessionContext; | |
27 | import org.apache.shiro.session.mgt.DefaultSessionKey; | |
28 | import org.apache.shiro.session.mgt.SessionContext; | |
29 | import org.apache.shiro.session.mgt.SessionKey; | |
30 | import org.apache.shiro.subject.PrincipalCollection; | |
31 | import org.apache.shiro.subject.Subject; | |
32 | import org.apache.shiro.subject.SubjectContext; | |
33 | import org.apache.shiro.subject.support.DefaultSubjectContext; | |
34 | import org.apache.shiro.util.CollectionUtils; | |
35 | import org.slf4j.Logger; | |
36 | import org.slf4j.LoggerFactory; | |
37 | ||
38 | import java.io.Serializable; | |
39 | import java.util.Collection; | |
40 | ||
41 | /** | |
42 | * The Shiro framework's default concrete implementation of the {@link SecurityManager} interface, | |
43 | * based around a collection of {@link org.apache.shiro.realm.Realm}s. This implementation delegates its | |
44 | * authentication, authorization, and session operations to wrapped {@link Authenticator}, {@link Authorizer}, and | |
45 | * {@link org.apache.shiro.session.mgt.SessionManager SessionManager} instances respectively via superclass | |
46 | * implementation. | |
47 | * <p/> | |
48 | * To greatly reduce and simplify configuration, this implementation (and its superclasses) will | |
49 | * create suitable defaults for all of its required dependencies, <em>except</em> the required one or more | |
50 | * {@link Realm Realm}s. Because {@code Realm} implementations usually interact with an application's data model, | |
51 | * they are almost always application specific; you will want to specify at least one custom | |
52 | * {@code Realm} implementation that 'knows' about your application's data/security model | |
53 | * (via {@link #setRealm} or one of the overloaded constructors). All other attributes in this class hierarchy | |
54 | * will have suitable defaults for most enterprise applications. | |
55 | * <p/> | |
56 | * <b>RememberMe notice</b>: This class supports the ability to configure a | |
57 | * {@link #setRememberMeManager RememberMeManager} | |
58 | * for {@code RememberMe} identity services for login/logout, BUT, a default instance <em>will not</em> be created | |
59 | * for this attribute at startup. | |
60 | * <p/> | |
61 | * Because RememberMe services are inherently client tier-specific and | |
62 | * therefore aplication-dependent, if you want {@code RememberMe} services enabled, you will have to specify an | |
63 | * instance yourself via the {@link #setRememberMeManager(RememberMeManager) setRememberMeManager} | |
64 | * mutator. However if you're reading this JavaDoc with the | |
65 | * expectation of operating in a Web environment, take a look at the | |
66 | * {@code org.apache.shiro.web.DefaultWebSecurityManager} implementation, which | |
67 | * <em>does</em> support {@code RememberMe} services by default at startup. | |
68 | * | |
69 | * @since 0.2 | |
70 | */ | |
71 | public class DefaultSecurityManager extends SessionsSecurityManager { | |
72 | ||
73 | 1 | private static final Logger log = LoggerFactory.getLogger(DefaultSecurityManager.class); |
74 | ||
75 | protected RememberMeManager rememberMeManager; | |
76 | protected SubjectDAO subjectDAO; | |
77 | protected SubjectFactory subjectFactory; | |
78 | ||
79 | /** | |
80 | * Default no-arg constructor. | |
81 | */ | |
82 | public DefaultSecurityManager() { | |
83 | 36 | super(); |
84 | 36 | this.subjectFactory = new DefaultSubjectFactory(); |
85 | 36 | this.subjectDAO = new DefaultSubjectDAO(); |
86 | 36 | } |
87 | ||
88 | /** | |
89 | * Supporting constructor for a single-realm application. | |
90 | * | |
91 | * @param singleRealm the single realm used by this SecurityManager. | |
92 | */ | |
93 | public DefaultSecurityManager(Realm singleRealm) { | |
94 | 8 | this(); |
95 | 8 | setRealm(singleRealm); |
96 | 8 | } |
97 | ||
98 | /** | |
99 | * Supporting constructor for multiple {@link #setRealms realms}. | |
100 | * | |
101 | * @param realms the realm instances backing this SecurityManager. | |
102 | */ | |
103 | public DefaultSecurityManager(Collection<Realm> realms) { | |
104 | 0 | this(); |
105 | 0 | setRealms(realms); |
106 | 0 | } |
107 | ||
108 | /** | |
109 | * Returns the {@code SubjectFactory} responsible for creating {@link Subject} instances exposed to the application. | |
110 | * | |
111 | * @return the {@code SubjectFactory} responsible for creating {@link Subject} instances exposed to the application. | |
112 | */ | |
113 | public SubjectFactory getSubjectFactory() { | |
114 | 46 | return subjectFactory; |
115 | } | |
116 | ||
117 | /** | |
118 | * Sets the {@code SubjectFactory} responsible for creating {@link Subject} instances exposed to the application. | |
119 | * | |
120 | * @param subjectFactory the {@code SubjectFactory} responsible for creating {@link Subject} instances exposed to the application. | |
121 | */ | |
122 | public void setSubjectFactory(SubjectFactory subjectFactory) { | |
123 | 0 | this.subjectFactory = subjectFactory; |
124 | 0 | } |
125 | ||
126 | /** | |
127 | * Returns the {@code SubjectDAO} responsible for persisting Subject state, typically used after login or when an | |
128 | * Subject identity is discovered (eg after RememberMe services). Unless configured otherwise, the default | |
129 | * implementation is a {@link DefaultSubjectDAO}. | |
130 | * | |
131 | * @return the {@code SubjectDAO} responsible for persisting Subject state, typically used after login or when an | |
132 | * Subject identity is discovered (eg after RememberMe services). | |
133 | * @see DefaultSubjectDAO | |
134 | * @since 1.2 | |
135 | */ | |
136 | public SubjectDAO getSubjectDAO() { | |
137 | 0 | return subjectDAO; |
138 | } | |
139 | ||
140 | /** | |
141 | * Sets the {@code SubjectDAO} responsible for persisting Subject state, typically used after login or when an | |
142 | * Subject identity is discovered (eg after RememberMe services). Unless configured otherwise, the default | |
143 | * implementation is a {@link DefaultSubjectDAO}. | |
144 | * | |
145 | * @param subjectDAO the {@code SubjectDAO} responsible for persisting Subject state, typically used after login or when an | |
146 | * Subject identity is discovered (eg after RememberMe services). | |
147 | * @see DefaultSubjectDAO | |
148 | * @since 1.2 | |
149 | */ | |
150 | public void setSubjectDAO(SubjectDAO subjectDAO) { | |
151 | 0 | this.subjectDAO = subjectDAO; |
152 | 0 | } |
153 | ||
154 | public RememberMeManager getRememberMeManager() { | |
155 | 59 | return rememberMeManager; |
156 | } | |
157 | ||
158 | public void setRememberMeManager(RememberMeManager rememberMeManager) { | |
159 | 0 | this.rememberMeManager = rememberMeManager; |
160 | 0 | } |
161 | ||
162 | protected SubjectContext createSubjectContext() { | |
163 | 17 | return new DefaultSubjectContext(); |
164 | } | |
165 | ||
166 | /** | |
167 | * Creates a {@code Subject} instance for the user represented by the given method arguments. | |
168 | * | |
169 | * @param token the {@code AuthenticationToken} submitted for the successful authentication. | |
170 | * @param info the {@code AuthenticationInfo} of a newly authenticated user. | |
171 | * @param existing the existing {@code Subject} instance that initiated the authentication attempt | |
172 | * @return the {@code Subject} instance that represents the context and session data for the newly | |
173 | * authenticated subject. | |
174 | */ | |
175 | protected Subject createSubject(AuthenticationToken token, AuthenticationInfo info, Subject existing) { | |
176 | 17 | SubjectContext context = createSubjectContext(); |
177 | 17 | context.setAuthenticated(true); |
178 | 17 | context.setAuthenticationToken(token); |
179 | 17 | context.setAuthenticationInfo(info); |
180 | 17 | if (existing != null) { |
181 | 17 | context.setSubject(existing); |
182 | } | |
183 | 17 | return createSubject(context); |
184 | } | |
185 | ||
186 | /** | |
187 | * Binds a {@code Subject} instance created after authentication to the application for later use. | |
188 | * <p/> | |
189 | * As of Shiro 1.2, this method has been deprecated in favor of {@link #save(org.apache.shiro.subject.Subject)}, | |
190 | * which this implementation now calls. | |
191 | * | |
192 | * @param subject the {@code Subject} instance created after authentication to be bound to the application | |
193 | * for later use. | |
194 | * @see #save(org.apache.shiro.subject.Subject) | |
195 | * @deprecated in favor of {@link #save(org.apache.shiro.subject.Subject) save(subject)}. | |
196 | */ | |
197 | @Deprecated | |
198 | protected void bind(Subject subject) { | |
199 | 0 | save(subject); |
200 | 0 | } |
201 | ||
202 | protected void rememberMeSuccessfulLogin(AuthenticationToken token, AuthenticationInfo info, Subject subject) { | |
203 | 17 | RememberMeManager rmm = getRememberMeManager(); |
204 | 17 | if (rmm != null) { |
205 | try { | |
206 | 0 | rmm.onSuccessfulLogin(subject, token, info); |
207 | 0 | } catch (Exception e) { |
208 | 0 | if (log.isWarnEnabled()) { |
209 | 0 | String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() + |
210 | "] threw an exception during onSuccessfulLogin. RememberMe services will not be " + | |
211 | "performed for account [" + info + "]."; | |
212 | 0 | log.warn(msg, e); |
213 | } | |
214 | 0 | } |
215 | } else { | |
216 | 17 | if (log.isTraceEnabled()) { |
217 | 17 | log.trace("This " + getClass().getName() + " instance does not have a " + |
218 | "[" + RememberMeManager.class.getName() + "] instance configured. RememberMe services " + | |
219 | "will not be performed for account [" + info + "]."); | |
220 | } | |
221 | } | |
222 | 17 | } |
223 | ||
224 | protected void rememberMeFailedLogin(AuthenticationToken token, AuthenticationException ex, Subject subject) { | |
225 | 4 | RememberMeManager rmm = getRememberMeManager(); |
226 | 4 | if (rmm != null) { |
227 | try { | |
228 | 0 | rmm.onFailedLogin(subject, token, ex); |
229 | 0 | } catch (Exception e) { |
230 | 0 | if (log.isWarnEnabled()) { |
231 | 0 | String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() + |
232 | "] threw an exception during onFailedLogin for AuthenticationToken [" + | |
233 | token + "]."; | |
234 | 0 | log.warn(msg, e); |
235 | } | |
236 | 0 | } |
237 | } | |
238 | 4 | } |
239 | ||
240 | protected void rememberMeLogout(Subject subject) { | |
241 | 9 | RememberMeManager rmm = getRememberMeManager(); |
242 | 9 | if (rmm != null) { |
243 | try { | |
244 | 0 | rmm.onLogout(subject); |
245 | 0 | } catch (Exception e) { |
246 | 0 | if (log.isWarnEnabled()) { |
247 | 0 | String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() + |
248 | "] threw an exception during onLogout for subject with principals [" + | |
249 | (subject != null ? subject.getPrincipals() : null) + "]"; | |
250 | 0 | log.warn(msg, e); |
251 | } | |
252 | 0 | } |
253 | } | |
254 | 9 | } |
255 | ||
256 | /** | |
257 | * First authenticates the {@code AuthenticationToken} argument, and if successful, constructs a | |
258 | * {@code Subject} instance representing the authenticated account's identity. | |
259 | * <p/> | |
260 | * Once constructed, the {@code Subject} instance is then {@link #bind bound} to the application for | |
261 | * subsequent access before being returned to the caller. | |
262 | * | |
263 | * @param token the authenticationToken to process for the login attempt. | |
264 | * @return a Subject representing the authenticated user. | |
265 | * @throws AuthenticationException if there is a problem authenticating the specified {@code token}. | |
266 | */ | |
267 | public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException { | |
268 | AuthenticationInfo info; | |
269 | try { | |
270 | 21 | info = authenticate(token); |
271 | 4 | } catch (AuthenticationException ae) { |
272 | try { | |
273 | 4 | onFailedLogin(token, ae, subject); |
274 | 0 | } catch (Exception e) { |
275 | 0 | if (log.isInfoEnabled()) { |
276 | 0 | log.info("onFailedLogin method threw an " + |
277 | "exception. Logging and propagating original AuthenticationException.", e); | |
278 | } | |
279 | 4 | } |
280 | 4 | throw ae; //propagate |
281 | 17 | } |
282 | ||
283 | 17 | Subject loggedIn = createSubject(token, info, subject); |
284 | ||
285 | 17 | onSuccessfulLogin(token, info, loggedIn); |
286 | ||
287 | 17 | return loggedIn; |
288 | } | |
289 | ||
290 | protected void onSuccessfulLogin(AuthenticationToken token, AuthenticationInfo info, Subject subject) { | |
291 | 17 | rememberMeSuccessfulLogin(token, info, subject); |
292 | 17 | } |
293 | ||
294 | protected void onFailedLogin(AuthenticationToken token, AuthenticationException ae, Subject subject) { | |
295 | 4 | rememberMeFailedLogin(token, ae, subject); |
296 | 4 | } |
297 | ||
298 | protected void beforeLogout(Subject subject) { | |
299 | 9 | rememberMeLogout(subject); |
300 | 9 | } |
301 | ||
302 | protected SubjectContext copy(SubjectContext subjectContext) { | |
303 | 46 | return new DefaultSubjectContext(subjectContext); |
304 | } | |
305 | ||
306 | /** | |
307 | * This implementation functions as follows: | |
308 | * <p/> | |
309 | * <ol> | |
310 | * <li>Ensures the {@code SubjectContext} is as populated as it can be, using heuristics to acquire | |
311 | * data that may not have already been available to it (such as a referenced session or remembered principals).</li> | |
312 | * <li>Calls {@link #doCreateSubject(org.apache.shiro.subject.SubjectContext)} to actually perform the | |
313 | * {@code Subject} instance creation.</li> | |
314 | * <li>calls {@link #save(org.apache.shiro.subject.Subject) save(subject)} to ensure the constructed | |
315 | * {@code Subject}'s state is accessible for future requests/invocations if necessary.</li> | |
316 | * <li>returns the constructed {@code Subject} instance.</li> | |
317 | * </ol> | |
318 | * | |
319 | * @param subjectContext any data needed to direct how the Subject should be constructed. | |
320 | * @return the {@code Subject} instance reflecting the specified contextual data. | |
321 | * @see #ensureSecurityManager(org.apache.shiro.subject.SubjectContext) | |
322 | * @see #resolveSession(org.apache.shiro.subject.SubjectContext) | |
323 | * @see #resolvePrincipals(org.apache.shiro.subject.SubjectContext) | |
324 | * @see #doCreateSubject(org.apache.shiro.subject.SubjectContext) | |
325 | * @see #save(org.apache.shiro.subject.Subject) | |
326 | * @since 1.0 | |
327 | */ | |
328 | public Subject createSubject(SubjectContext subjectContext) { | |
329 | //create a copy so we don't modify the argument's backing map: | |
330 | 46 | SubjectContext context = copy(subjectContext); |
331 | ||
332 | //ensure that the context has a SecurityManager instance, and if not, add one: | |
333 | 46 | context = ensureSecurityManager(context); |
334 | ||
335 | //Resolve an associated Session (usually based on a referenced session ID), and place it in the context before | |
336 | //sending to the SubjectFactory. The SubjectFactory should not need to know how to acquire sessions as the | |
337 | //process is often environment specific - better to shield the SF from these details: | |
338 | 46 | context = resolveSession(context); |
339 | ||
340 | //Similarly, the SubjectFactory should not require any concept of RememberMe - translate that here first | |
341 | //if possible before handing off to the SubjectFactory: | |
342 | 46 | context = resolvePrincipals(context); |
343 | ||
344 | 46 | Subject subject = doCreateSubject(context); |
345 | ||
346 | //save this subject for future reference if necessary: | |
347 | //(this is needed here in case rememberMe principals were resolved and they need to be stored in the | |
348 | //session, so we don't constantly rehydrate the rememberMe PrincipalCollection on every operation). | |
349 | //Added in 1.2: | |
350 | 46 | save(subject); |
351 | ||
352 | 46 | return subject; |
353 | } | |
354 | ||
355 | /** | |
356 | * Actually creates a {@code Subject} instance by delegating to the internal | |
357 | * {@link #getSubjectFactory() subjectFactory}. By the time this method is invoked, all possible | |
358 | * {@code SubjectContext} data (session, principals, et. al.) has been made accessible using all known heuristics | |
359 | * and will be accessible to the {@code subjectFactory} via the {@code subjectContext.resolve*} methods. | |
360 | * | |
361 | * @param context the populated context (data map) to be used by the {@code SubjectFactory} when creating a | |
362 | * {@code Subject} instance. | |
363 | * @return a {@code Subject} instance reflecting the data in the specified {@code SubjectContext} data map. | |
364 | * @see #getSubjectFactory() | |
365 | * @see SubjectFactory#createSubject(org.apache.shiro.subject.SubjectContext) | |
366 | * @since 1.2 | |
367 | */ | |
368 | protected Subject doCreateSubject(SubjectContext context) { | |
369 | 46 | return getSubjectFactory().createSubject(context); |
370 | } | |
371 | ||
372 | /** | |
373 | * Saves the subject's state to a persistent location for future reference if necessary. | |
374 | * <p/> | |
375 | * This implementation merely delegates to the internal {@link #setSubjectDAO(SubjectDAO) subjectDAO} and calls | |
376 | * {@link SubjectDAO#save(org.apache.shiro.subject.Subject) subjectDAO.save(subject)}. | |
377 | * | |
378 | * @param subject the subject for which state will potentially be persisted | |
379 | * @see SubjectDAO#save(org.apache.shiro.subject.Subject) | |
380 | * @since 1.2 | |
381 | */ | |
382 | protected void save(Subject subject) { | |
383 | 46 | this.subjectDAO.save(subject); |
384 | 46 | } |
385 | ||
386 | /** | |
387 | * Removes (or 'unbinds') the Subject's state from the application, typically called during {@link #logout}.. | |
388 | * <p/> | |
389 | * This implementation merely delegates to the internal {@link #setSubjectDAO(SubjectDAO) subjectDAO} and calls | |
390 | * {@link SubjectDAO#delete(org.apache.shiro.subject.Subject) delete(subject)}. | |
391 | * | |
392 | * @param subject the subject for which state will be removed | |
393 | * @see SubjectDAO#delete(org.apache.shiro.subject.Subject) | |
394 | * @since 1.2 | |
395 | */ | |
396 | protected void delete(Subject subject) { | |
397 | 9 | this.subjectDAO.delete(subject); |
398 | 9 | } |
399 | ||
400 | /** | |
401 | * Determines if there is a {@code SecurityManager} instance in the context, and if not, adds 'this' to the | |
402 | * context. This ensures the SubjectFactory instance will have access to a SecurityManager during Subject | |
403 | * construction if necessary. | |
404 | * | |
405 | * @param context the subject context data that may contain a SecurityManager instance. | |
406 | * @return The SubjectContext to use to pass to a {@link SubjectFactory} for subject creation. | |
407 | * @since 1.0 | |
408 | */ | |
409 | @SuppressWarnings({"unchecked"}) | |
410 | protected SubjectContext ensureSecurityManager(SubjectContext context) { | |
411 | 46 | if (context.resolveSecurityManager() != null) { |
412 | 42 | log.trace("Context already contains a SecurityManager instance. Returning."); |
413 | 42 | return context; |
414 | } | |
415 | 4 | log.trace("No SecurityManager found in context. Adding self reference."); |
416 | 4 | context.setSecurityManager(this); |
417 | 4 | return context; |
418 | } | |
419 | ||
420 | /** | |
421 | * Attempts to resolve any associated session based on the context and returns a | |
422 | * context that represents this resolved {@code Session} to ensure it may be referenced if necessary by the | |
423 | * invoked {@link SubjectFactory} that performs actual {@link Subject} construction. | |
424 | * <p/> | |
425 | * If there is a {@code Session} already in the context because that is what the caller wants to be used for | |
426 | * {@code Subject} construction, or if no session is resolved, this method effectively does nothing | |
427 | * returns the context method argument unaltered. | |
428 | * | |
429 | * @param context the subject context data that may resolve a Session instance. | |
430 | * @return The context to use to pass to a {@link SubjectFactory} for subject creation. | |
431 | * @since 1.0 | |
432 | */ | |
433 | @SuppressWarnings({"unchecked"}) | |
434 | protected SubjectContext resolveSession(SubjectContext context) { | |
435 | 46 | if (context.resolveSession() != null) { |
436 | 0 | log.debug("Context already contains a session. Returning."); |
437 | 0 | return context; |
438 | } | |
439 | try { | |
440 | //Context couldn't resolve it directly, let's see if we can since we have direct access to | |
441 | //the session manager: | |
442 | 46 | Session session = resolveContextSession(context); |
443 | 46 | if (session != null) { |
444 | 0 | context.setSession(session); |
445 | } | |
446 | 0 | } catch (InvalidSessionException e) { |
447 | 0 | log.debug("Resolved SubjectContext context session is invalid. Ignoring and creating an anonymous " + |
448 | "(session-less) Subject instance.", e); | |
449 | 46 | } |
450 | 46 | return context; |
451 | } | |
452 | ||
453 | protected Session resolveContextSession(SubjectContext context) throws InvalidSessionException { | |
454 | 46 | SessionKey key = getSessionKey(context); |
455 | 46 | if (key != null) { |
456 | 0 | return getSession(key); |
457 | } | |
458 | 46 | return null; |
459 | } | |
460 | ||
461 | protected SessionKey getSessionKey(SubjectContext context) { | |
462 | 46 | Serializable sessionId = context.getSessionId(); |
463 | 46 | if (sessionId != null) { |
464 | 0 | return new DefaultSessionKey(sessionId); |
465 | } | |
466 | 46 | return null; |
467 | } | |
468 | ||
469 | /** | |
470 | * Attempts to resolve an identity (a {@link PrincipalCollection}) for the context using heuristics. This | |
471 | * implementation functions as follows: | |
472 | * <ol> | |
473 | * <li>Check the context to see if it can already {@link SubjectContext#resolvePrincipals resolve an identity}. If | |
474 | * so, this method does nothing and returns the method argument unaltered.</li> | |
475 | * <li>Check for a RememberMe identity by calling {@link #getRememberedIdentity}. If that method returns a | |
476 | * non-null value, place the remembered {@link PrincipalCollection} in the context.</li> | |
477 | * </ol> | |
478 | * | |
479 | * @param context the subject context data that may provide (directly or indirectly through one of its values) a | |
480 | * {@link PrincipalCollection} identity. | |
481 | * @return The Subject context to use to pass to a {@link SubjectFactory} for subject creation. | |
482 | * @since 1.0 | |
483 | */ | |
484 | @SuppressWarnings({"unchecked"}) | |
485 | protected SubjectContext resolvePrincipals(SubjectContext context) { | |
486 | ||
487 | 46 | PrincipalCollection principals = context.resolvePrincipals(); |
488 | ||
489 | 46 | if (CollectionUtils.isEmpty(principals)) { |
490 | 29 | log.trace("No identity (PrincipalCollection) found in the context. Looking for a remembered identity."); |
491 | ||
492 | 29 | principals = getRememberedIdentity(context); |
493 | ||
494 | 29 | if (!CollectionUtils.isEmpty(principals)) { |
495 | 0 | log.debug("Found remembered PrincipalCollection. Adding to the context to be used " + |
496 | "for subject construction by the SubjectFactory."); | |
497 | ||
498 | 0 | context.setPrincipals(principals); |
499 | ||
500 | // The following call was removed (commented out) in Shiro 1.2 because it uses the session as an | |
501 | // implementation strategy. Session use for Shiro's own needs should be controlled in a single place | |
502 | // to be more manageable for end-users: there are a number of stateless (e.g. REST) applications that | |
503 | // use Shiro that need to ensure that sessions are only used when desirable. If Shiro's internal | |
504 | // implementations used Subject sessions (setting attributes) whenever we wanted, it would be much | |
505 | // harder for end-users to control when/where that occurs. | |
506 | // | |
507 | // Because of this, the SubjectDAO was created as the single point of control, and session state logic | |
508 | // has been moved to the DefaultSubjectDAO implementation. | |
509 | ||
510 | // Removed in Shiro 1.2. SHIRO-157 is still satisfied by the new DefaultSubjectDAO implementation | |
511 | // introduced in 1.2 | |
512 | // Satisfies SHIRO-157: | |
513 | // bindPrincipalsToSession(principals, context); | |
514 | ||
515 | } else { | |
516 | 29 | log.trace("No remembered identity found. Returning original context."); |
517 | } | |
518 | } | |
519 | ||
520 | 46 | return context; |
521 | } | |
522 | ||
523 | protected SessionContext createSessionContext(SubjectContext subjectContext) { | |
524 | 0 | DefaultSessionContext sessionContext = new DefaultSessionContext(); |
525 | 0 | if (!CollectionUtils.isEmpty(subjectContext)) { |
526 | 0 | sessionContext.putAll(subjectContext); |
527 | } | |
528 | 0 | Serializable sessionId = subjectContext.getSessionId(); |
529 | 0 | if (sessionId != null) { |
530 | 0 | sessionContext.setSessionId(sessionId); |
531 | } | |
532 | 0 | String host = subjectContext.resolveHost(); |
533 | 0 | if (host != null) { |
534 | 0 | sessionContext.setHost(host); |
535 | } | |
536 | 0 | return sessionContext; |
537 | } | |
538 | ||
539 | public void logout(Subject subject) { | |
540 | ||
541 | 9 | if (subject == null) { |
542 | 0 | throw new IllegalArgumentException("Subject method argument cannot be null."); |
543 | } | |
544 | ||
545 | 9 | beforeLogout(subject); |
546 | ||
547 | 9 | PrincipalCollection principals = subject.getPrincipals(); |
548 | 9 | if (principals != null && !principals.isEmpty()) { |
549 | 8 | if (log.isDebugEnabled()) { |
550 | 8 | log.debug("Logging out subject with primary principal {}", principals.getPrimaryPrincipal()); |
551 | } | |
552 | 8 | Authenticator authc = getAuthenticator(); |
553 | 8 | if (authc instanceof LogoutAware) { |
554 | 8 | ((LogoutAware) authc).onLogout(principals); |
555 | } | |
556 | } | |
557 | ||
558 | try { | |
559 | 9 | delete(subject); |
560 | 0 | } catch (Exception e) { |
561 | 0 | if (log.isDebugEnabled()) { |
562 | 0 | String msg = "Unable to cleanly unbind Subject. Ignoring (logging out)."; |
563 | 0 | log.debug(msg, e); |
564 | } | |
565 | } finally { | |
566 | 0 | try { |
567 | 9 | stopSession(subject); |
568 | 0 | } catch (Exception e) { |
569 | 0 | if (log.isDebugEnabled()) { |
570 | 0 | String msg = "Unable to cleanly stop Session for Subject [" + subject.getPrincipal() + "] " + |
571 | "Ignoring (logging out)."; | |
572 | 0 | log.debug(msg, e); |
573 | } | |
574 | 9 | } |
575 | 0 | } |
576 | 9 | } |
577 | ||
578 | protected void stopSession(Subject subject) { | |
579 | 9 | Session s = subject.getSession(false); |
580 | 9 | if (s != null) { |
581 | 9 | s.stop(); |
582 | } | |
583 | 9 | } |
584 | ||
585 | /** | |
586 | * Unbinds or removes the Subject's state from the application, typically called during {@link #logout}. | |
587 | * <p/> | |
588 | * This has been deprecated in Shiro 1.2 in favor of the {@link #delete(org.apache.shiro.subject.Subject) delete} | |
589 | * method. The implementation has been updated to invoke that method. | |
590 | * | |
591 | * @param subject the subject to unbind from the application as it will no longer be used. | |
592 | * @deprecated in Shiro 1.2 in favor of {@link #delete(org.apache.shiro.subject.Subject)} | |
593 | */ | |
594 | @Deprecated | |
595 | @SuppressWarnings({"UnusedDeclaration"}) | |
596 | protected void unbind(Subject subject) { | |
597 | 0 | delete(subject); |
598 | 0 | } |
599 | ||
600 | protected PrincipalCollection getRememberedIdentity(SubjectContext subjectContext) { | |
601 | 29 | RememberMeManager rmm = getRememberMeManager(); |
602 | 29 | if (rmm != null) { |
603 | try { | |
604 | 0 | return rmm.getRememberedPrincipals(subjectContext); |
605 | 0 | } catch (Exception e) { |
606 | 0 | if (log.isWarnEnabled()) { |
607 | 0 | String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() + |
608 | "] threw an exception during getRememberedPrincipals()."; | |
609 | 0 | log.warn(msg, e); |
610 | } | |
611 | } | |
612 | } | |
613 | 29 | return null; |
614 | } | |
615 | } |