Shiro会话超时后重定向至登录页的配置问题求助
会话超时自动重定向登录页的Shiro配置问题
我正在用Java+Angular开发Web应用,想要实现会话超时后自动重定向到登录页,现在有两个疑问:
- Shiro会自动处理这个重定向逻辑吗?
- 如果需要手动实现,正确的配置或代码方式是什么?
我已经尝试了两种方法但都没效果:
- 在
web.xml中配置session-timeout但未生效(目前该配置是注释状态) - 修改
shiro.ini后,应用直接无法登录了!
麻烦帮忙排查我的配置文件问题,或者告知这类场景的正确处理方式,提前感谢!
我的配置文件内容
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 credentialsMatche
问题排查与解决方案
一、先看你的配置问题
1. web.xml的问题
- 你注释掉的
session-config和error-page,其实和Shiro的会话管理不冲突,但Shiro自身的会话配置优先级更高,就算开启session-timeout也可能被Shiro覆盖,所以核心问题不在这。 - 另外,
error-page是针对JSF的ViewExpiredException,但你用的是Angular+REST后端,这个异常基本不会触发,注释掉也没问题。
2. shiro.ini的问题
一眼就看到你最后一行是credentialsMatche——明显拼写错误!应该是credentialsMatcher(少了个r),而且你只定义了这个凭证匹配器,却没把它绑定到对应的Realm上,这直接导致Shiro认证逻辑异常,所以应用无法登录。
另外,你同时配置了ldapRealm和jdbcRealm,但没明确多Realm的认证策略,这也是潜在隐患:多个Realm共存时,必须告诉Shiro怎么处理认证结果。
二、正确实现会话超时重定向的步骤
1. 修复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 credentialsMatcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher 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 # 把凭证匹配器绑定到JDBC Realm(因为JDBC需要校验密码哈希) jdbcRealm.credentialsMatcher = $credentialsMatcher # 配置多Realm认证策略:只要一个Realm认证成功就算登录 authenticator = org.apache.shiro.authc.pam.ModularRealmAuthenticator authenticator.authenticationStrategy = org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy securityManager.authenticator = $authenticator # 把两个Realm添加到SecurityManager securityManager.realms = $ldapRealm, $jdbcRealm # 核心:会话超时配置 sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager # 设置会话超时时间,单位毫秒,这里是30分钟=1800000ms sessionManager.globalSessionTimeout = 1800000 # 开启定时清理过期会话 sessionManager.sessionValidationSchedulerEnabled = true securityManager.sessionManager = $sessionManager # 登录重定向配置:未认证/会话超时访问受限资源时,自动跳转到登录页 authc.loginUrl = /Login.html
2. 添加Shiro的URL访问规则
在shiro.ini末尾加上[urls]段,定义哪些资源需要认证:
[urls] # 登录页面允许匿名访问 /Login.html = anon # Angular入口页和静态资源允许匿名 /index.html = anon /static/** = anon # 其他所有资源必须认证后才能访问 /** = authc
3. 前端Angular的配合
因为是单页应用,后端返回的是REST接口,AJAX请求不会自动跳转页面,所以需要在前端统一处理401未授权响应:
可以写一个HTTP拦截器,捕获401状态码后跳转到登录页:
import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpRequest, HttpHandler, HttpErrorResponse } from '@angular/common/http'; import { catchError } from 'rxjs/operators'; import { throwError } from 'rxjs'; import { Router } from '@angular/router'; @Injectable() export class AuthInterceptor implements HttpInterceptor { constructor(private router: Router) {} intercept(req: HttpRequest<any>, next: HttpHandler) { return next.handle(req).pipe( catchError((error: HttpErrorResponse) => { if (error.status === 401) { // 会话超时或未登录,跳转到登录页 this.router.navigate(['/login']); } return throwError(error); }) ); } }
4. 测试会话超时
- 把
sessionManager.globalSessionTimeout临时设为60000ms(1分钟) - 登录后等待1分钟,刷新页面或发起API请求,验证是否自动跳转到登录页
三、关键知识点
- Shiro不会完全自动处理所有场景:对于普通HTTP页面请求,配置
authc.loginUrl后会自动跳转;但AJAX请求需要前端配合处理401响应。 - Shiro的会话配置优先级高于
web.xml的session-timeout,所以必须在Shiro里设置globalSessionTimeout。 - 多Realm共存时,一定要明确认证策略,否则会出现认证逻辑混乱。
内容的提问来源于stack exchange,提问作者zbeedatm
相关产品推荐
相关产品推荐

