본문 바로가기

Backend Development/Spring boot

[Spring boot] Spring Security 분석 - FilterSecurityInterceptor (Session 인증 기반)

서버 기동 시 embedded tomcat container 기본 설정

 

org/apache/tomcat/embed/tomcat-embed-core/9.0.54/tomcat-embed-core-9.0.54-sources.jar!/org/apache/catalina/session/ManagerBase.java:388

 

@Override
public void setContext(Context context) {
    if (this.context == context) {
        // NO-OP
        return;
    }
    if (!getState().equals(LifecycleState.NEW)) {
        throw new IllegalStateException(sm.getString("managerBase.setContextNotNew"));
    }
    Context oldContext = this.context;
    this.context = context;
    support.firePropertyChange("context", oldContext, this.context);
}

context = {TomcatEmbeddedContext@6191} "StandardEngine[Tomcat].StandardHost[localhost].TomcatEmbeddedContext[]"
 starter = {TomcatStarter@6212} 
 allowCasualMultipartParsing = false
 swallowAbortedUploads = true
 altDDName = null
 instanceManager = null
 antiResourceLocking = false
 applicationListeners = {String[0]@6213} 
 applicationListenersLock = {Object@6214} 
 noPluggabilityListeners = {HashSet@6215}  size = 0
 applicationEventListenersList = {CopyOnWriteArrayList@6216}  size = 0
 applicationLifecycleListenersObjects = {Object[0]@6217} 
 initializers = {LinkedHashMap@6218}  size = 2
 applicationParameters = {ApplicationParameter[0]@6219} 
 applicationParametersLock = {Object@6221} 
 broadcaster = {NotificationBroadcasterSupport@6222} 
 charsetMapper = {CharsetMapper@6223} 
 charsetMapperClass = "org.apache.catalina.util.CharsetMapper"
 configFile = null
 configured = false
 constraints = {SecurityConstraint[0]@6225} 
 constraintsLock = {Object@6227} 
 context = null
 noPluggabilityServletContext = null
 cookies = true
 crossContext = false
 encodedPath = ""
 path = ""
 delegate = false
 denyUncoveredHttpMethods = false
 displayName = "application"
 defaultContextXml = null
 defaultWebXml = null
 distributable = false
 docBase = "E:\Data\Github\sdp_43_kiosk\src\main\webapp"
 errorPageSupport = {ErrorPageSupport@6232} 
 filterConfigs = {HashMap@6233}  size = 0
 filterDefs = {HashMap@6234}  size = 0
 filterMaps = {StandardContext$ContextFilterMaps@6235} 
 ignoreAnnotations = false
 loader = {WebappLoader@6236} "WebappLoader[StandardEngine[Tomcat].StandardHost[localhost].TomcatEmbeddedContext[]]"
 loaderLock = {ReentrantReadWriteLock@6237} "java.util.concurrent.locks.ReentrantReadWriteLock@55fb2ef5[Write locks = 0, Read locks = 0]"
 loginConfig = null
 manager = {StandardManager@6190} "StandardManager[Container is null]"
 managerLock = {ReentrantReadWriteLock@6238} "java.util.concurrent.locks.ReentrantReadWriteLock@31b96488[Write locks = 1, Read locks = 0]"
 namingContextListener = null
 namingResources = null
 messageDestinations = {HashMap@6239}  size = 0
 mimeMappings = {HashMap@6240}  size = 176
 parameters = {ConcurrentHashMap@6241}  size = 0
 paused = false
 publicId = null
 reloadable = false
 unpackWAR = true
 copyXML = false
 override = false
 originalDocBase = null
 privileged = false
 replaceWelcomeFiles = false
 roleMappings = {HashMap@6242}  size = 0
 securityRoles = {String[0]@6243} 
 securityRolesLock = {Object@6244} 
 servletMappings = {HashMap@6245}  size = 2
 servletMappingsLock = {Object@6246} 
 sessionTimeout = 1
 sequenceNumber = {AtomicLong@6247} "0"
 swallowOutput = false
 unloadDelay = 2000
 watchedResources = {String[0]@6248} 
 watchedResourcesLock = {Object@6249} 
 welcomeFiles = {String[0]@6250} 
 welcomeFilesLock = {Object@6251} 
 wrapperLifecycles = {String[0]@6252} 
 wrapperLifecyclesLock = {Object@6253} 
 wrapperListeners = {String[0]@6254} 
 wrapperListenersLock = {Object@6255} 
 workDir = null
 wrapperClassName = "org.apache.catalina.core.StandardWrapper"
 wrapperClass = null
 useNaming = true
 namingContextName = null
 resources = {TomcatServletWebServerFactory$LoaderHidingResourceRoot@6257} 
 resourcesLock = {ReentrantReadWriteLock@6258} "java.util.concurrent.locks.ReentrantReadWriteLock@360984c1[Write locks = 0, Read locks = 0]"
 startupTime = 0
 startTime = 0
 tldScanTime = 0
 j2EEApplication = "none"
 j2EEServer = "none"
 webXmlValidation = false
 webXmlNamespaceAware = false
 xmlBlockExternal = true
 tldValidation = false
 sessionCookieName = null
 useHttpOnly = true
 sessionCookieDomain = null
 sessionCookiePath = null
 sessionCookiePathUsesTrailingSlash = false
 jarScanner = {StandardJarScanner@6260} 
 clearReferencesRmiTargets = true
 clearReferencesStopThreads = false
 clearReferencesStopTimerThreads = false
 clearReferencesHttpClientKeepAliveThread = true
 renewThreadsWhenStoppingContext = true
 clearReferencesObjectStreamClassCaches = true
 clearReferencesThreadLocals = true
 skipMemoryLeakChecksOnJvmShutdown = false
 logEffectiveWebXml = false
 effectiveMajorVersion = 3
 effectiveMinorVersion = 0
 jspConfigDescriptor = null
 resourceOnlyServlets = {HashSet@6261}  size = 1
 webappVersion = ""
 addWebinfClassesResources = false
 fireRequestListenersOnForwards = false
 createdServlets = {HashSet@6262}  size = 0
 preemptiveAuthentication = false
 sendRedirectBody = false
 jndiExceptionOnFailedWrite = true
 postConstructMethods = {HashMap@6263}  size = 0
 preDestroyMethods = {HashMap@6264}  size = 0
 containerSciFilter = null
 failCtxIfServletStartFails = {Boolean@6265} true
 threadBindingListener = {StandardContext$1@6266} 
 namingToken = {Object@6267} 
 cookieProcessor = null
 validateClientProvidedNewSessionId = true
 mapperContextRootRedirectEnabled = true
 mapperDirectoryRedirectEnabled = false
 useRelativeRedirects = true
 dispatchersUseEncodedPaths = true
 requestEncoding = null
 responseEncoding = null
 allowMultipleLeadingForwardSlashInPath = false
 inProgressAsyncCount = {AtomicLong@6268} "0"
 createUploadTargets = true
 parallelAnnotationScanning = false
 useBloomFilterForArchives = false
 notificationInfo = null
 server = null
 javaVMs = null
 children = {HashMap@6269}  size = 1
 backgroundProcessorDelay = -1
 backgroundProcessorFuture = null
 monitorFuture = null
 listeners = {CopyOnWriteArrayList@6270}  size = 0
 logger = null
 logName = null
 cluster = null
 clusterLock = {ReentrantReadWriteLock@6271} "java.util.concurrent.locks.ReentrantReadWriteLock@1a557ae4[Write locks = 0, Read locks = 0]"
 name = ""
 parent = {StandardHost@6272} "StandardEngine[Tomcat].StandardHost[localhost]"
 parentClassLoader = {RestartClassLoader@6273} 
 pipeline = {StandardPipeline@6274} "StandardPipeline[StandardEngine[Tomcat].StandardHost[localhost].TomcatEmbeddedContext[]]"
 realm = null
 realmLock = {ReentrantReadWriteLock@6275} "java.util.concurrent.locks.ReentrantReadWriteLock@16d573dd[Write locks = 0, Read locks = 0]"
 startChildren = true
 support = {PropertyChangeSupport@6276} 
 accessLog = null
 accessLogScanComplete = false
 startStopThreads = 1
 startStopExecutor = null
 domain = null
 oname = null
 mserver = null
 lifecycleListeners = {CopyOnWriteArrayList@6277}  size = 3
 state = {LifecycleState@6207} "NEW"
 throwOnFailure = true
this.context = null

기본적으로 Tomcat 설정에서 sessionTimeout = 1 (분) 로 내려오고

아래 ManageBase.java에서 starndard session 생성 시 *60 (초) 로 단위 환산을 한다. 즉 기본 세션 타임아웃 설정은 1분이다.

 

org/apache/tomcat/embed/tomcat-embed-core/9.0.54/tomcat-embed-core-9.0.54-sources.jar!/org/apache/catalina/session/ManagerBase.java:699

@Override
public Session createSession(String sessionId) {

    if ((maxActiveSessions >= 0) &&
            (getActiveSessions() >= maxActiveSessions)) {
        rejectedSessions++;
        throw new TooManyActiveSessionsException(
                sm.getString("managerBase.createSession.ise"),
                maxActiveSessions);
    }

    // Recycle or create a Session instance
    Session session = createEmptySession();

    // Initialize the properties of the new session and return it
    session.setNew(true);
    session.setValid(true);
    session.setCreationTime(System.currentTimeMillis());
    session.setMaxInactiveInterval(getContext().getSessionTimeout() * 60);
    String id = sessionId;
    if (id == null) {
        id = generateSessionId();
    }
    session.setId(id);
    sessionCounter++;

    SessionTiming timing = new SessionTiming(session.getCreationTime(), 0);
    synchronized (sessionCreationTiming) {
        sessionCreationTiming.add(timing);
        sessionCreationTiming.poll();
    }
    return session;
}

 

org/apache/tomcat/embed/tomcat-embed-core/9.0.54/tomcat-embed-core-9.0.54-sources.jar!/org/apache/catalina/session/StandardSession.java:315

 

/**
 * Standard implementation of the <b>Session</b> interface.  This object is
 * serializable, so that it can be stored in persistent storage or transferred
 * to a different JVM for distributable session support.
 * <p>
 * <b>IMPLEMENTATION NOTE</b>:  An instance of this class represents both the
 * internal (Session) and application level (HttpSession) view of the session.
 * However, because the class itself is not declared public, Java logic outside
 * of the <code>org.apache.catalina.session</code> package cannot cast an
 * HttpSession view of this instance back to a Session view.
 * <p>
 * <b>IMPLEMENTATION NOTE</b>:  If you add fields to this class, you must
 * make sure that you carry them over in the read/writeObject methods so
 * that this class is properly serialized.
 *
 * @author Craig R. McClanahan
 * @author Sean Legassick
 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
 */
public class StandardSession implements HttpSession, Session, Serializable {

    private static final long serialVersionUID = 1L;

    protected static final boolean STRICT_SERVLET_COMPLIANCE;

    protected static final boolean ACTIVITY_CHECK;

    protected static final boolean LAST_ACCESS_AT_START;

    static {
        STRICT_SERVLET_COMPLIANCE = Globals.STRICT_SERVLET_COMPLIANCE;

        String activityCheck = System.getProperty(
                "org.apache.catalina.session.StandardSession.ACTIVITY_CHECK");
        if (activityCheck == null) {
            ACTIVITY_CHECK = STRICT_SERVLET_COMPLIANCE;
        } else {
            ACTIVITY_CHECK = Boolean.parseBoolean(activityCheck);
        }

        String lastAccessAtStart = System.getProperty(
                "org.apache.catalina.session.StandardSession.LAST_ACCESS_AT_START");
        if (lastAccessAtStart == null) {
            LAST_ACCESS_AT_START = STRICT_SERVLET_COMPLIANCE;
        } else {
            LAST_ACCESS_AT_START = Boolean.parseBoolean(lastAccessAtStart);
        }
    }


    // ----------------------------------------------------------- Constructors


    /**
     * Construct a new Session associated with the specified Manager.
     *
     * @param manager The manager with which this Session is associated
     */
    public StandardSession(Manager manager) {

        super();
        this.manager = manager;

        // Initialize access count
        if (ACTIVITY_CHECK) {
            accessCount = new AtomicInteger();
        }

    }


    // ----------------------------------------------------- Instance Variables


... 중략


    /**
     * Set the creation time for this session.  This method is called by the
     * Manager when an existing Session instance is reused.
     *
     * @param time The new creation time
     */
    @Override
    public void setCreationTime(long time) {

        this.creationTime = time;
        this.lastAccessedTime = time;
        this.thisAccessedTime = time;

    }

 

Call Stack

setCreationTime:310, StandardSession (org.apache.catalina.session)
createSession:718, ManagerBase (org.apache.catalina.session)
doGetSession:3093, Request (org.apache.catalina.connector)
getSession:2493, Request (org.apache.catalina.connector)
getSession:908, RequestFacade (org.apache.catalina.connector)
getSession:920, RequestFacade (org.apache.catalina.connector)
getSession:253, HttpServletRequestWrapper (javax.servlet.http)
getSession:253, HttpServletRequestWrapper (javax.servlet.http)
getSession:253, HttpServletRequestWrapper (javax.servlet.http)
getSession:253, HttpServletRequestWrapper (javax.servlet.http)
getSession:253, HttpServletRequestWrapper (javax.servlet.http)
preHandle:30, Interceptor (com.sdp.common.interceptor)
applyPreHandle:148, HandlerExecutionChain (org.springframework.web.servlet)
doDispatch:1062, DispatcherServlet (org.springframework.web.servlet)
doService:963, DispatcherServlet (org.springframework.web.servlet)
processRequest:1006, FrameworkServlet (org.springframework.web.servlet)
doGet:898, FrameworkServlet (org.springframework.web.servlet)
service:655, HttpServlet (javax.servlet.http)
service:883, FrameworkServlet (org.springframework.web.servlet)
service:764, HttpServlet (javax.servlet.http)
internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:218, AbstractAuthenticationProcessingFilter (org.springframework.security.web.authentication)
doFilter:212, AbstractAuthenticationProcessingFilter (org.springframework.security.web.authentication)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:36, XssEscapeServletFilter (com.navercorp.lucy.security.xss.servletfilter)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:327, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
invoke:115, FilterSecurityInterceptor (org.springframework.security.web.access.intercept)
doFilter:81, FilterSecurityInterceptor (org.springframework.security.web.access.intercept)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:121, ExceptionTranslationFilter (org.springframework.security.web.access)
doFilter:115, ExceptionTranslationFilter (org.springframework.security.web.access)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:126, SessionManagementFilter (org.springframework.security.web.session)
doFilter:81, SessionManagementFilter (org.springframework.security.web.session)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:105, AnonymousAuthenticationFilter (org.springframework.security.web.authentication)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:149, SecurityContextHolderAwareRequestFilter (org.springframework.security.web.servletapi)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:63, RequestCacheAwareFilter (org.springframework.security.web.savedrequest)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:149, BasicAuthenticationFilter (org.springframework.security.web.authentication.www)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:103, LogoutFilter (org.springframework.security.web.authentication.logout)
doFilter:89, LogoutFilter (org.springframework.security.web.authentication.logout)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doHeadersAfter:90, HeaderWriterFilter (org.springframework.security.web.header)
doFilterInternal:75, HeaderWriterFilter (org.springframework.security.web.header)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:110, SecurityContextPersistenceFilter (org.springframework.security.web.context)
doFilter:80, SecurityContextPersistenceFilter (org.springframework.security.web.context)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:55, WebAsyncManagerIntegrationFilter (org.springframework.security.web.context.request.async)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:211, FilterChainProxy (org.springframework.security.web)
doFilter:183, FilterChainProxy (org.springframework.security.web)
invokeDelegate:358, DelegatingFilterProxy (org.springframework.web.filter)
doFilter:271, DelegatingFilterProxy (org.springframework.web.filter)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:100, RequestContextFilter (org.springframework.web.filter)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:93, FormContentFilter (org.springframework.web.filter)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:201, CharacterEncodingFilter (org.springframework.web.filter)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
invoke:197, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:540, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:135, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:357, CoyoteAdapter (org.apache.catalina.connector)
service:382, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:895, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1722, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:834, Thread (java.lang)

 

Standard Session 만료 시간 설정

 

org/apache/tomcat/embed/tomcat-embed-core/9.0.54/tomcat-embed-core-9.0.54-sources.jar!/org/apache/catalina/session/StandardSession.java:565

/**
 * Set the maximum time interval, in seconds, between client requests
 * before the servlet container will invalidate the session.  A zero or
 * negative time indicates that the session should never time out.
 *
 * @param interval The new maximum interval
 */
@Override
public void setMaxInactiveInterval(int interval) {
    this.maxInactiveInterval = interval;
}

 

세션 만료시간 valid 체크

 

org/apache/tomcat/embed/tomcat-embed-core/9.0.54/tomcat-embed-core-9.0.54-sources.jar!/org/apache/catalina/session/StandardSession.java:638

/**
 * Return the <code>isValid</code> flag for this session.
 */
@Override
public boolean isValid() {

    if (!this.isValid) {
        return false;
    }

    if (this.expiring) {
        return true;
    }

    if (ACTIVITY_CHECK && accessCount.get() > 0) {
        return true;
    }

    if (maxInactiveInterval > 0) {
        int timeIdle = (int) (getIdleTimeInternal() / 1000L);
        if (timeIdle >= maxInactiveInterval) {
            expire(true);
        }
    }

    return this.isValid;
}

 

/**
 * Return the idle time from last client access time without invalidation check
 * @see #getIdleTime()
 */
@Override
public long getIdleTimeInternal() {
    long timeNow = System.currentTimeMillis();
    long timeIdle;
    if (LAST_ACCESS_AT_START) {
        timeIdle = timeNow - lastAccessedTime;
    } else {
        timeIdle = timeNow - thisAccessedTime;
    }
    return timeIdle;
}