重写Shiro SessionDao记录实时登录用户信息时遇UnavailableSecurityManagerException
Hey, I've dealt with this exact problem when building a Redis-backed session store for Shiro by extending EnterpriseCacheSessionDAO. Let's walk through the most likely causes and how to fix them:
1. SecurityManager isn't bound to the current thread
This exception almost always pops up when you try to call Shiro APIs that depend on the SecurityManager before it's been attached to the current thread's context. For example, if your custom RedisSessionDao methods (like session creation/update) trigger operations that need the SecurityManager, but there's no instance available in the thread context yet.
Fixes:
- Make sure
SecurityUtils.getSecurityManager()returns a valid instance before any session-related operations run. In a Spring environment, double-check that yourSecurityManagerbean is fully initialized and injected correctly. - If you need to, manually bind the SecurityManager to the thread context in your custom DAO methods:
// Get SecurityManager from Spring context (adjust based on your setup) SecurityManager securityManager = redisTemplate.getApplicationContext().getBean(SecurityManager.class); SecurityUtils.setSecurityManager(securityManager);
2. Verify RedisSessionDao initialization & dependencies
You've annotated your RedisSessionDao with @Repository, so confirm that Spring is scanning this class and creating the bean properly. Also, check that your RedisTemplate<String, Object> is being injected successfully—if this dependency fails, it can trigger cascading issues that indirectly lead to the SecurityManager exception.
Additionally, EnterpriseCacheSessionDAO relies on Shiro's caching mechanism. Ensure you've configured a valid cache manager (like a Redis-based one) for Shiro, as missing this can break the context setup.
3. Thread context gaps in overridden methods
Looking at your code snippet, you're overriding the session creation method. Be careful: some operations in the session lifecycle (like accessing Session.getPrincipal()) require an active SecurityManager context. If your custom logic runs before the context is bound, you'll hit this exception.
Fix example for doCreate:
@Override protected Serializable doCreate(Session session) { // Ensure SecurityManager is available in the thread context if (SecurityUtils.getSecurityManager() == null) { SecurityManager securityManager = redisTemplate.getApplicationContext().getBean(SecurityManager.class); SecurityUtils.setSecurityManager(securityManager); } // Proceed with default session creation logic Serializable sessionId = super.doCreate(session); // Save to Redis String redisKey = KEY_PREFIX + sessionId; redisTemplate.opsForValue().set(redisKey, session); return sessionId; }
4. Check your Shiro configuration wiring
The most critical step: make sure your custom RedisSessionDao is properly attached to Shiro's SessionManager, and that the SessionManager is linked to your SecurityManager. If this wiring is missing, Shiro can't find the SecurityManager when handling sessions.
Example Spring configuration:
@Bean public DefaultWebSessionManager sessionManager(RedisSessionDao redisSessionDao) { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setSessionDAO(redisSessionDao); // Add other session settings (timeout, cookie config, etc.) return sessionManager; } @Bean public DefaultWebSecurityManager securityManager(DefaultWebSessionManager sessionManager, YourRealm realm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setSessionManager(sessionManager); securityManager.setRealm(realm); // Add other security settings return securityManager; }
内容的提问来源于stack exchange,提问作者Ry Lin

