[main]
# Set session timeout to 1 hour (in milliseconds)
securityManager.sessionManager.globalSessionTimeout = 3600000
|
Handy Hint
|
Shiro v1 version notice
As of February 28, 2024, Shiro v1 was superseded by v2.
|
This page covers common issues that users encounter when configuring and using Apache Shiro. Each section provides practical solutions and debugging tips to help you resolve problems quickly.
By default, Shiro sessions have a 30-minute timeout. If your sessions expire faster than expected, check your SessionManager configuration.
You can adjust the global session timeout in your shiro.ini:
[main]
# Set session timeout to 1 hour (in milliseconds)
securityManager.sessionManager.globalSessionTimeout = 3600000
For Spring Boot applications, you can configure this in application.properties:
shiro.sessionManager.globalSessionTimeout = 3600000
Also verify that your application is calling session.touch() when needed for long-running operations, particularly in Rich Internet Application (RIA) scenarios where users may be active on the page without triggering server requests.
See the Session Management documentation for more details.
Shiro supports session clustering through its SessionDAO abstraction. To enable clustering, you need to:
Configure a distributed cache (such as Ehcache, Redis, or Hazelcast)
Implement or configure an appropriate SessionDAO
Set up the CacheManager
Example configuration using Ehcache:
[main]
cacheManager = org.apache.shiro.cache.ehcache.EhCacheManager
cacheManager.cacheManagerConfigFile = classpath:ehcache.xml
sessionDAO = org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
sessionDAO.activeSessionsCacheName = shiro-activeSessionCache
sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
sessionManager.sessionDAO = $sessionDAO
sessionManager.cacheManager = $cacheManager
securityManager.sessionManager = $sessionManager
securityManager.cacheManager = $cacheManager
Make sure your ehcache.xml includes a properly configured distributed cache for the shiro-activeSessionCache.
By default, Shiro uses an in-memory SessionDAO that does not persist sessions. To maintain sessions across restarts, you need to configure a persistent SessionDAO backed by a database, file system, or distributed cache.
Consider using EnterpriseCacheSessionDAO with a persistent cache or implementing a custom SessionDAO that stores sessions in your preferred data store.
There are several common causes for this issue:
Password encoding mismatch: Ensure the password stored in your data source uses the same hashing algorithm configured in your Realm.
Realm not finding the account: Verify your Realm can locate the user account. Enable debug logging to see what Shiro is doing:
log4j.logger.org.apache.shiro = DEBUG
Multiple Realms configured incorrectly: If you have multiple Realms, check your AuthenticationStrategy. The default AtLeastOneSuccessfulStrategy requires at least one Realm to succeed.
Case sensitivity: Usernames may be case-sensitive depending on your Realm implementation. Verify the case matches exactly.
Enable Shiro’s debug logging to trace the authentication flow:
<!-- For Log4j2 -->
<Logger name="org.apache.shiro" level="DEBUG"/>
<Logger name="org.apache.shiro.realm" level="TRACE"/>
You can also catch and inspect the AuthenticationException for more details:
try {
currentUser.login(token);
} catch (UnknownAccountException uae) {
log.info("No account found for user: " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
log.info("Incorrect password for user: " + token.getPrincipal());
} catch (LockedAccountException lae) {
log.info("Account is locked: " + token.getPrincipal());
} catch (AuthenticationException ae) {
log.error("Unexpected authentication error", ae);
}
See the Authentication documentation for a complete overview.
This error indicates that Shiro cannot find any Realm to authenticate against. You must configure at least one Realm in your application.
In shiro.ini:
[main]
myRealm = com.mycompany.security.MyCustomRealm
securityManager.realms = $myRealm
For Spring Boot, define a Realm bean:
@Bean
public Realm realm() {
return new MyCustomRealm();
}
Common reasons for permission check failures:
Incorrect permission string format: Shiro uses a colon-delimited format. Ensure you’re using consistent formatting:
// These are different permissions
subject.isPermitted("document:read"); // domain:action
subject.isPermitted("document:read:123"); // domain:action:instance
Wildcard permissions: Understand how wildcards work. document:* grants all actions on documents, while *:read is typically not valid (wildcards work left-to-right).
Role vs Permission confusion: Roles and permissions are different. Use hasRole() for role checks and isPermitted() for permission checks.
See the Permissions documentation for the complete wildcard permission syntax.
Roles can be assigned to users in your Realm implementation. In your Realm’s doGetAuthorizationInfo method:
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// Add roles
info.addRole("user");
if (isAdmin(username)) {
info.addRole("admin");
}
// Add permissions
info.addStringPermission("document:read");
return info;
}
For web applications, you can also use filter chain definitions:
[urls]
/admin/** = authc, roles[admin]
/user/** = authc, roles[user]
Ensure you have the correct starter dependency:
For web applications:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>2.0.6</version>
</dependency>
compile 'org.apache.shiro:shiro-spring-boot-web-starter:2.0.6'
libraryDependencies += "org.apache.shiro" % "shiro-spring-boot-web-starter" % "2.0.6"
<dependency org="org.apache.shiro" name="shiro-spring-boot-web-starter" rev="2.0.6"/>
[org.apache.shiro/shiro-spring-boot-web-starter "2.0.6"]
'org.apache.shiro:shiro-spring-boot-web-starter:jar:2.0.6'
For non-web applications:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>2.0.6</version>
</dependency>
compile 'org.apache.shiro:shiro-spring-boot-starter:2.0.6'
libraryDependencies += "org.apache.shiro" % "shiro-spring-boot-starter" % "2.0.6"
<dependency org="org.apache.shiro" name="shiro-spring-boot-starter" rev="2.0.6"/>
[org.apache.shiro/shiro-spring-boot-starter "2.0.6"]
'org.apache.shiro:shiro-spring-boot-starter:jar:2.0.6'
Also verify that you have defined a Realm bean in your configuration.
See the Spring Boot Integration guide for complete setup instructions.
Define a ShiroFilterChainDefinition bean in your configuration:
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
// Static resources
chainDefinition.addPathDefinition("/css/**", "anon");
chainDefinition.addPathDefinition("/js/**", "anon");
// Login page
chainDefinition.addPathDefinition("/login", "anon");
// Admin section requires admin role
chainDefinition.addPathDefinition("/admin/**", "authc, roles[admin]");
// Everything else requires authentication
chainDefinition.addPathDefinition("/**", "authc");
return chainDefinition;
}
Remember that filter chain order matters. More specific paths should be defined before general ones.
Several factors can cause Remember Me to fail:
Cookie configuration: The Remember Me cookie may not be set correctly. Check your cookie settings:
[main]
securityManager.rememberMeManager.cookie.name = rememberMe
securityManager.rememberMeManager.cookie.maxAge = 2592000
securityManager.rememberMeManager.cookie.path = /
Cipher key not set: In production, you should set a consistent cipher key across cluster nodes:
@Bean
public CookieRememberMeManager rememberMeManager() {
CookieRememberMeManager manager = new CookieRememberMeManager();
manager.setCipherKey(Base64.decode("your-base64-encoded-key"));
return manager;
}
Using isAuthenticated() instead of isRemembered(): These are different states. A remembered user is NOT fully authenticated:
// User who logged in this session
subject.isAuthenticated();
// User recognized via Remember Me cookie
subject.isRemembered();
Generate a secure key using Shiro’s AesCipherService:
AesCipherService cipherService = new AesCipherService();
Key key = cipherService.generateNewKey();
String base64Key = Base64.encodeToString(key.getEncoded());
System.out.println("Cipher key: " + base64Key);
Store this key securely and use the same key across all nodes in a clustered environment.
Define each Realm and assign them to the SecurityManager:
[main]
ldapRealm = org.apache.shiro.realm.ldap.JndiLdapRealm
ldapRealm.contextFactory.url = ldap://localhost:389
jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource = $dataSource
securityManager.realms = $ldapRealm, $jdbcRealm
By default, Shiro uses AtLeastOneSuccessfulStrategy for authentication, meaning the user is authenticated if any Realm succeeds.
See the Realm documentation for more advanced configurations.
Verify the following:
The Realm is properly registered with the SecurityManager
The Realm supports the AuthenticationToken type being submitted
The Realm is enabled (check isAuthenticationCachingEnabled if using caching)
Override supports() in your Realm if you need to handle specific token types:
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
Configure a CacheManager and enable caching in your Realms:
[main]
cacheManager = org.apache.shiro.cache.ehcache.EhCacheManager
cacheManager.cacheManagerConfigFile = classpath:ehcache.xml
securityManager.cacheManager = $cacheManager
myRealm = com.mycompany.security.MyRealm
myRealm.authenticationCachingEnabled = true
myRealm.authorizationCachingEnabled = true
Caching can significantly improve performance, especially for authorization lookups that may involve database queries.
If you have caching enabled, authorization information is cached for performance. When a user’s permissions change, you need to clear their cached authorization:
// Clear authorization cache for a specific user
PrincipalCollection principals = subject.getPrincipals();
realm.getAuthorizationCache().remove(principals);
Or clear the entire cache:
realm.getAuthorizationCache().clear();
For Log4j2, add these entries to your logging configuration:
<Loggers>
<Logger name="org.apache.shiro" level="DEBUG"/>
<Logger name="org.apache.shiro.realm" level="TRACE"/>
<Logger name="org.apache.shiro.session" level="DEBUG"/>
<Logger name="org.apache.shiro.web" level="DEBUG"/>
</Loggers>
For Logback (common in Spring Boot):
<logger name="org.apache.shiro" level="DEBUG"/>
Or in application.properties:
logging.level.org.apache.shiro=DEBUG
Shiro uses Ant-style path matching. Remember these rules:
* matches any characters within a path segment
** matches any path segments
Patterns are evaluated in order; first match wins
Example patterns:
[urls]
/login = anon
/logout = logout
/admin/** = authc, roles[admin]
/api/** = authc, rest
/** = authc
Place more specific patterns before general ones. The /** catch-all should always be last.
anon: Allows anonymous access; no authentication required
authc: Requires the user to be authenticated (logged in during this session)
user: Allows access if the user is authenticated OR remembered
Common filter chain:
[urls]
/public/** = anon
/login = anon
/account/** = user
/admin/** = authc, roles[admin]
/** = authc
This error occurs when Shiro’s SecurityUtils.getSubject() is called before the SecurityManager is initialized.
Ensure the SecurityManager is set up before any security operations:
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
In web applications, verify that Shiro’s filter is configured in your web.xml or equivalent configuration and loads before your application code runs.
Check the following:
Filter configuration: Ensure ShiroFilter is mapped correctly in your web.xml:
<filter>
<filter-name>ShiroFilter</filter-name>
<filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ShiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Environment listener: Add the environment loader listener:
<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>
Configuration file location: By default, Shiro looks for /WEB-INF/shiro.ini. You can customize this with a context parameter.
For more detailed information, refer to these documentation pages:
Reference Manual - Complete Shiro reference documentation
10 Minute Tutorial - Quick start guide
Authentication Guide - Detailed authentication walkthrough
Authorization Guide - Detailed authorization walkthrough
Configuration - Configuration options and formats
If you cannot find a solution here, consider reaching out to the community:
Mailing Lists - Ask questions and get help from the community
Issue Tracker - Report bugs or request features