blob: 39bfba68c407d688d27b6de4a0bd4c9ece06a360 [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../../jacoco-resources/report.gif" type="image/gif"/><title>DefaultSubjectDAO.java</title><link rel="stylesheet" href="../../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../../index.html" class="el_report">Apache Shiro :: All (aggregate jar)</a> &gt; <a href="../index.html" class="el_bundle">shiro-core</a> &gt; <a href="index.source.html" class="el_package">org.apache.shiro.mgt</a> &gt; <span class="el_source">DefaultSubjectDAO.java</span></div><h1>DefaultSubjectDAO.java</h1><pre class="source lang-java linenums">/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* &quot;License&quot;); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.shiro.mgt;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.subject.support.DelegatingSubject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
/**
* Default {@code SubjectDAO} implementation that stores Subject state in the Subject's Session by default (but this
* can be disabled - see below). The Subject instance
* can be re-created at a later time by first acquiring the associated Session (typically from a
* {@link org.apache.shiro.session.mgt.SessionManager SessionManager}) via a session ID or session key and then
* building a {@code Subject} instance from {@code Session} attributes.
* &lt;h2&gt;Controlling how Sessions are used&lt;/h2&gt;
* Whether or not a {@code Subject}'s {@code Session} is used or not to persist its own state is controlled on a
* &lt;em&gt;per-Subject&lt;/em&gt; basis as determined by the configured
* {@link #setSessionStorageEvaluator(SessionStorageEvaluator) sessionStorageEvaluator}.
* The default {@code Evaluator} is a {@link DefaultSessionStorageEvaluator}, which supports enabling or disabling
* session usage for Subject persistence at a global level for all subjects (and defaults to allowing sessions to be
* used).
* &lt;h3&gt;Disabling Session Persistence Entirely&lt;/h3&gt;
* Because the default {@code SessionStorageEvaluator} instance is a {@link DefaultSessionStorageEvaluator}, you
* can disable Session usage for Subject state entirely by configuring that instance directly, e.g.:
* &lt;pre&gt;
* ((DefaultSessionStorageEvaluator)sessionDAO.getSessionStorageEvaluator()).setSessionStorageEnabled(false);
* &lt;/pre&gt;
* or, for example, in {@code shiro.ini}:
* &lt;pre&gt;
* securityManager.subjectDAO.sessionStorageEvaluator.sessionStorageEnabled = false
* &lt;/pre&gt;
* but &lt;b&gt;note:&lt;/b&gt; ONLY do this your
* application is 100% stateless and you &lt;em&gt;DO NOT&lt;/em&gt; need subjects to be remembered across remote
* invocations, or in a web environment across HTTP requests.
* &lt;h3&gt;Supporting Both Stateful and Stateless Subject paradigms&lt;/h3&gt;
* Perhaps your application needs to support a hybrid approach of both stateful and stateless Subjects:
* &lt;ul&gt;
* &lt;li&gt;Stateful: Stateful subjects might represent web end-users that need their identity and authentication
* state to be remembered from page to page.&lt;/li&gt;
* &lt;li&gt;Stateless: Stateless subjects might represent API clients (e.g. REST clients) that authenticate on every
* request, and therefore don't need authentication state to be stored across requests in a session.&lt;/li&gt;
* &lt;/ul&gt;
* To support the hybrid &lt;em&gt;per-Subject&lt;/em&gt; approach, you will need to create your own implementation of the
* {@link SessionStorageEvaluator} interface and configure it via the
* {@link #setSessionStorageEvaluator(SessionStorageEvaluator)} method, or, with {@code shiro.ini}:
* &lt;pre&gt;
* myEvaluator = com.my.CustomSessionStorageEvaluator
* securityManager.subjectDAO.sessionStorageEvaluator = $myEvaluator
* &lt;/pre&gt;
* &lt;p/&gt;
* Unless overridden, the default evaluator is a {@link DefaultSessionStorageEvaluator}, which enables session usage for
* Subject state by default.
*
* @see #isSessionStorageEnabled(org.apache.shiro.subject.Subject)
* @see SessionStorageEvaluator
* @see DefaultSessionStorageEvaluator
* @since 1.2
*/
public class DefaultSubjectDAO implements SubjectDAO {
<span class="fc" id="L83"> private static final Logger log = LoggerFactory.getLogger(DefaultSubjectDAO.class);</span>
/**
* Evaluator that determines if a Subject's session may be used to store the Subject's own state.
*/
private SessionStorageEvaluator sessionStorageEvaluator;
<span class="fc" id="L90"> public DefaultSubjectDAO() {</span>
//default implementation allows enabling/disabling session usages at a global level for all subjects:
<span class="fc" id="L92"> this.sessionStorageEvaluator = new DefaultSessionStorageEvaluator();</span>
<span class="fc" id="L93"> }</span>
/**
* Determines if the subject's session will be used to persist subject state or not. This implementation
* merely delegates to the internal {@link SessionStorageEvaluator} (a
* {@code DefaultSessionStorageEvaluator} by default).
*
* @param subject the subject to inspect to determine if the subject's session will be used to persist subject
* state or not.
* @return {@code true} if the subject's session will be used to persist subject state, {@code false} otherwise.
* @see #setSessionStorageEvaluator(SessionStorageEvaluator)
* @see DefaultSessionStorageEvaluator
*/
protected boolean isSessionStorageEnabled(Subject subject) {
<span class="fc" id="L107"> return getSessionStorageEvaluator().isSessionStorageEnabled(subject);</span>
}
/**
* Returns the {@code SessionStorageEvaluator} that will determine if a {@code Subject}'s state may be persisted in
* the Subject's session. The default instance is a {@link DefaultSessionStorageEvaluator}.
*
* @return the {@code SessionStorageEvaluator} that will determine if a {@code Subject}'s state may be persisted in
* the Subject's session.
* @see DefaultSessionStorageEvaluator
*/
public SessionStorageEvaluator getSessionStorageEvaluator() {
<span class="fc" id="L119"> return sessionStorageEvaluator;</span>
}
/**
* Sets the {@code SessionStorageEvaluator} that will determine if a {@code Subject}'s state may be persisted in
* the Subject's session. The default instance is a {@link DefaultSessionStorageEvaluator}.
*
* @param sessionStorageEvaluator the {@code SessionStorageEvaluator} that will determine if a {@code Subject}'s
* state may be persisted in the Subject's session.
* @see DefaultSessionStorageEvaluator
*/
public void setSessionStorageEvaluator(SessionStorageEvaluator sessionStorageEvaluator) {
<span class="fc" id="L131"> this.sessionStorageEvaluator = sessionStorageEvaluator;</span>
<span class="fc" id="L132"> }</span>
/**
* Saves the subject's state to the subject's {@link org.apache.shiro.subject.Subject#getSession() session} only
* if {@link #isSessionStorageEnabled(Subject) sessionStorageEnabled(subject)}. If session storage is not enabled
* for the specific {@code Subject}, this method does nothing.
* &lt;p/&gt;
* In either case, the argument {@code Subject} is returned directly (a new Subject instance is not created).
*
* @param subject the Subject instance for which its state will be created or updated.
* @return the same {@code Subject} passed in (a new Subject instance is not created).
*/
public Subject save(Subject subject) {
<span class="fc bfc" id="L145" title="All 2 branches covered."> if (isSessionStorageEnabled(subject)) {</span>
<span class="fc" id="L146"> saveToSession(subject);</span>
} else {
<span class="fc" id="L148"> log.trace(&quot;Session storage of subject state for Subject [{}] has been disabled: identity and &quot; +</span>
&quot;authentication state are expected to be initialized on every request or invocation.&quot;, subject);
}
<span class="fc" id="L152"> return subject;</span>
}
/**
* Saves the subject's state (it's principals and authentication state) to its
* {@link org.apache.shiro.subject.Subject#getSession() session}. The session can be retrieved at a later time
* (typically from a {@link org.apache.shiro.session.mgt.SessionManager SessionManager} to be used to recreate
* the {@code Subject} instance.
*
* @param subject the subject for which state will be persisted to its session.
*/
protected void saveToSession(Subject subject) {
//performs merge logic, only updating the Subject's session if it does not match the current state:
<span class="fc" id="L165"> mergePrincipals(subject);</span>
<span class="fc" id="L166"> mergeAuthenticationState(subject);</span>
<span class="fc" id="L167"> }</span>
private static boolean isEmpty(PrincipalCollection pc) {
<span class="pc bpc" id="L170" title="1 of 4 branches missed."> return pc == null || pc.isEmpty();</span>
}
/**
* Merges the Subject's current {@link org.apache.shiro.subject.Subject#getPrincipals()} with whatever may be in
* any available session. Only updates the Subject's session if the session does not match the current principals
* state.
*
* @param subject the Subject for which principals will potentially be merged into the Subject's session.
*/
protected void mergePrincipals(Subject subject) {
//merge PrincipalCollection state:
<span class="fc" id="L183"> PrincipalCollection currentPrincipals = null;</span>
//SHIRO-380: added if/else block - need to retain original (source) principals
//This technique (reflection) is only temporary - a proper long term solution needs to be found,
//but this technique allowed an immediate fix that is API point-version forwards and backwards compatible
//
//A more comprehensive review / cleaning of runAs should be performed for Shiro 1.3 / 2.0 +
<span class="pc bpc" id="L190" title="1 of 4 branches missed."> if (subject.isRunAs() &amp;&amp; subject instanceof DelegatingSubject) {</span>
try {
<span class="fc" id="L192"> Field field = DelegatingSubject.class.getDeclaredField(&quot;principals&quot;);</span>
<span class="fc" id="L193"> field.setAccessible(true);</span>
<span class="fc" id="L194"> currentPrincipals = (PrincipalCollection)field.get(subject);</span>
<span class="nc" id="L195"> } catch (Exception e) {</span>
<span class="nc" id="L196"> throw new IllegalStateException(&quot;Unable to access DelegatingSubject principals property.&quot;, e);</span>
<span class="fc" id="L197"> }</span>
}
<span class="pc bpc" id="L199" title="1 of 4 branches missed."> if (currentPrincipals == null || currentPrincipals.isEmpty()) {</span>
<span class="fc" id="L200"> currentPrincipals = subject.getPrincipals();</span>
}
<span class="fc" id="L203"> Session session = subject.getSession(false);</span>
<span class="fc bfc" id="L205" title="All 2 branches covered."> if (session == null) {</span>
<span class="fc bfc" id="L206" title="All 2 branches covered."> if (!isEmpty(currentPrincipals)) {</span>
<span class="fc" id="L207"> session = subject.getSession();</span>
<span class="fc" id="L208"> session.setAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY, currentPrincipals);</span>
}
// otherwise no session and no principals - nothing to save
} else {
<span class="fc" id="L212"> PrincipalCollection existingPrincipals =</span>
<span class="fc" id="L213"> (PrincipalCollection) session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);</span>
<span class="fc bfc" id="L215" title="All 2 branches covered."> if (isEmpty(currentPrincipals)) {</span>
<span class="fc bfc" id="L216" title="All 2 branches covered."> if (!isEmpty(existingPrincipals)) {</span>
<span class="fc" id="L217"> session.removeAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);</span>
}
// otherwise both are null or empty - no need to update the session
} else {
<span class="pc bpc" id="L221" title="1 of 2 branches missed."> if (!currentPrincipals.equals(existingPrincipals)) {</span>
<span class="fc" id="L222"> session.setAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY, currentPrincipals);</span>
}
// otherwise they're the same - no need to update the session
}
}
<span class="fc" id="L227"> }</span>
/**
* Merges the Subject's current authentication state with whatever may be in
* any available session. Only updates the Subject's session if the session does not match the current
* authentication state.
*
* @param subject the Subject for which principals will potentially be merged into the Subject's session.
*/
protected void mergeAuthenticationState(Subject subject) {
<span class="fc" id="L238"> Session session = subject.getSession(false);</span>
<span class="fc bfc" id="L240" title="All 2 branches covered."> if (session == null) {</span>
<span class="fc bfc" id="L241" title="All 2 branches covered."> if (subject.isAuthenticated()) {</span>
<span class="fc" id="L242"> session = subject.getSession();</span>
<span class="fc" id="L243"> session.setAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY, Boolean.TRUE);</span>
}
//otherwise no session and not authenticated - nothing to save
} else {
<span class="fc" id="L247"> Boolean existingAuthc = (Boolean) session.getAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY);</span>
<span class="fc bfc" id="L249" title="All 2 branches covered."> if (subject.isAuthenticated()) {</span>
<span class="pc bpc" id="L250" title="1 of 4 branches missed."> if (existingAuthc == null || !existingAuthc) {</span>
<span class="fc" id="L251"> session.setAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY, Boolean.TRUE);</span>
}
//otherwise authc state matches - no need to update the session
} else {
<span class="fc bfc" id="L255" title="All 2 branches covered."> if (existingAuthc != null) {</span>
//existing doesn't match the current state - remove it:
<span class="fc" id="L257"> session.removeAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY);</span>
}
//otherwise not in the session and not authenticated - no need to update the session
}
}
<span class="fc" id="L262"> }</span>
/**
* Removes any existing subject state from the Subject's session (if the session exists). If the session
* does not exist, this method does not do anything.
*
* @param subject the subject for which any existing subject state will be removed from its session.
*/
protected void removeFromSession(Subject subject) {
<span class="fc" id="L271"> Session session = subject.getSession(false);</span>
<span class="fc bfc" id="L272" title="All 2 branches covered."> if (session != null) {</span>
<span class="fc" id="L273"> session.removeAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY);</span>
<span class="fc" id="L274"> session.removeAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);</span>
}
<span class="fc" id="L276"> }</span>
/**
* Removes any existing subject state from the subject's session (if the session exists).
*
* @param subject the Subject instance for which any persistent state should be deleted.
*/
public void delete(Subject subject) {
<span class="fc" id="L284"> removeFromSession(subject);</span>
<span class="fc" id="L285"> }</span>
}
</pre><div class="footer"><span class="right">Created with <a href="http://www.eclemma.org/jacoco">JaCoCo</a> 0.7.7.201606060606</span></div></body></html>