javasessionweb.xmlshiro

Shiro - redirect to login page after session timeout


I need to redirect my webapp (Java + angular) to login page when session is timed out. Does shiro do the redirection automatically or shall this be handled by code?

I tried several hacks but didn't work for me. I tried adding session timeout to my web.xml but it simply didn't take affect. I tried updating my shiro.ini, but then couldn't login to my app anymore!

Can you please help to find what's wrong in these files or advice what sould be done in such a case.

Thanks in advance.

web.xml:

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>ResourceManager</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  
  <!-- session-config>
    <session-timeout>1</session-timeout>
  </session-config>

  <error-page>
    <exception-type>javax.faces.application.ViewExpiredException</exception-type>
    <location>/Login.html</location>
  </error-page-->
  
  <listener>
    <listener-class>
    	com.<our customized package>.logging.AppLifeCircleListener
    	</listener-class>
  </listener>
  <listener>
    <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
  </listener>
  <filter>
    <filter-name>shiro-filter</filter-name>
    <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>shiro-filter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>ERROR</dispatcher>
  </filter-mapping>
  <servlet>
    <servlet-name>login</servlet-name>
    <jsp-file>/Login.jsp</jsp-file>
  </servlet>
  <servlet>
    <servlet-name>rest</servlet-name>
    <servlet-class>
  			org.springframework.web.servlet.DispatcherServlet
 		</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/rest-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>rest</servlet-name>
    <url-pattern>/management/*</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>rest</servlet-name>
    <url-pattern>/privilege/*</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>rest</servlet-name>
    <url-pattern>/file-explorer/*</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>login</servlet-name>
    <url-pattern>/Login.html</url-pattern>
  </servlet-mapping>
</web-app>

shiro.ini :

[main]

# LDAP Settinng
contextFactory = com.ibm.datafabrication.web.server.security.JndiLdapContextFactoryExt
contextFactory.url = ldap://<host>:389
contextFactory.systemUsername = <...>
contextFactory.keyPath = <>
contextFactory.encodedSystemPassword = ...

ldapRealm = com.<>.ActiveDirectoryRealm
ldapRealm.ldapContextFactory = $contextFactory
ldapRealm.userSearchFilter = (sAMAccountName={0})
ldapRealm.groupNameAttribute = cn
ldapRealm.searchBase = DC=<...>,DC=<...>,DC=com
ldapRealm.groupRolesMap = dfp_users:user,dfp_admins:admin

# SQLite Setting
ds = org.sqlite.SQLiteDataSource
ds.url = jdbc:sqlite:<>.db
jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource = $ds
jdbcRealm.permissionsLookupEnabled = false
jdbcRealm.authenticationQuery = SELECT Password FROM Users WHERE Name=?
jdbcRealm.userRolesQuery = WITH uname(name) AS (VALUES(?)) SELECT 'user' FROM Users, uname WHERE Users.Name=uname.name UNION ALL SELECT 'admin' FROM (SELECT Id FROM Users, uname WHERE Users.Name=uname.name) LEFT JOIN Admins on Id = Admins.UserId WHERE UserId NOT NULL
credentialsMatcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
credentialsMatcher.storedCredentialsHexEncoded = true
jdbcRealm.credentialsMatcher = $credentialsMatcher

authc.loginUrl = /Login.html
securityManager.realms = $jdbcRealm

authcStrategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategy
securityManager.authenticator.authenticationStrategy = $authcStrategy

>>>>>>>>>>>>>>>>>>>>> What I tired

# session timeout
sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
securityManager.sessionMode=native
securityManager.sessionManager.globalSessionTimeout = 60000
securityManager.sessionManager = $sessionManager

sessionValidationScheduler = org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler
sessionValidationScheduler.interval = 30000
securityManager.sessionManager.sessionValidationScheduler = $sessionValidationScheduler

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

[urls]
/logout = logout
/css/** = anon
/images/** = anon
/plugins/** = anon
/favicon.ico = anon
/tdf.ico = anon
/Login.html = authc
...
/** = authc, roles[user]


Solution

  • You can leave your web.xml as it is since Shiro has nothing to do with the configruation in this file. There is a difference in the HttpSession used by the HttpServletRequest of your servlet and the Shiro Session. They are completely different things. So don't get confused by the session configuration by the servlet and the one by Shiro. We let Shiro handle your session in this case.

    Problems with your shiro.ini
    You are overriding the settings you are writing to the DefaultWebSessionManager:

    sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager // Define a sessionManager variable
    securityManager.sessionMode=native
    securityManager.sessionManager.globalSessionTimeout = 60000 // set a securityManager.sessionManager property
    securityManager.sessionManager = $sessionManager // override the property by setting the object
    

    You have to set the object first:

    sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
    securityManager.sessionManager = $sessionManager
    securityManager.sessionMode=native
    securityManager.sessionManager.globalSessionTimeout = 60000
    

    You don't need the ExecutorServiceSessionValidationScheduler since a valdiator is provided by the DefaultWebSessionManager already. A working example could look like the following:

    sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
    # Session timeout token_ttl_ms = 14 days
    sessionManager.globalSessionTimeout = 1209600000
    # Session valdiation = 15 minutes
    sessionManager.sessionValidationInterval = 900000
    
    securityManager = org.apache.shiro.web.mgt.DefaultWebSecurityManager
    securityManager.sessionManager = $sessionManager
    

    DefaultWebSessionManager

    The DefaultWebSessionManager provides all the functionality you are looking for out of the box (basically). When a session expires the DefaultWebSessionManager calls onExpiration on its super which notifys all its listeners about the expiration in the SessionManager. You simply could register a SessionListener to your SessionManager and trigger a reload or redirect at its onExpiration method. Maybe this approach is rather suitable for jsp/jspx applications.
    Anyway, read how to register a session listener here in the official docs.

    Alternative approach

    A different approach could be to have your client manage the session state and let it continuously check for expiration by calling your service. This could be done by a timer or in a loop in JavaScript.

    Note

    org.apache.shiro.authc.credential.Sha256CredentialsMatcher is deprecated since 2010.
    Use the HashedCredentialsMatcher directly and set its hashAlgorithmName property.