我创建了默认的表单登录身份验证,下面是我的配置。
<http auto-config="true" use-expressions="true">
<request-cache ref="authenticationRequestCache" />
<access-denied-handler error-page="/rest/login?error=denied" />
<form-login login-page="/rest/login"
authentication-success-handler-ref="successHandler"
authentication-failure-url="/rest/login?error" />
<intercept-url pattern="/rest/devices" access="denyAll" />
<intercept-url pattern="/rest/devices/**" access="denyAll" />
<intercept-url pattern="/rest/super/**" access="hasRole('ROLE_SUPER')" />
<intercept-url pattern="/rest/**" access="isAuthenticated()" />
<intercept-url pattern="/cavirinRest/*" />
<session-management invalid-session-url="/rest/login?error=sessionExpired"
session-authentication-strategy-ref="sas" />
<logout invalidate-session="true" logout-success-url="/rest/login" delete-cookies="JSESSIONID"/>
</http>
身份验证按预期正常工作,但最近我向应用程序添加了会话超时。我的应用程序每5秒轮询一次服务器,因此默认超时不起作用。我用Google搜索并在下面找到了带过滤器的解决方案并实现了它。
来自web.xml:
<filter>
<filter-name>XhrSessionTimeoutFilter</filter-name>
<filter-class>com.cavirin.security.filter.XhrSessionTimeoutFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>XhrSessionTimeoutFilter</filter-name>
<url-pattern>/rest/*</url-pattern>
</filter-mapping>
<session-config>
<!-- Disables URL-based sessions (no more 'jsessionid' in the URL using Tomcat) -->
<tracking-mode>COOKIE</tracking-mode>
<session-timeout>2</session-timeout>
</session-config>
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
HttpSession session = request.getSession(false);
if (session != null) {
logger.trace("doFilterInternal(): session object is not null.");
session.setMaxInactiveInterval(120); //2 mins for testing //set max inactive interval to 30 mins
// if requestURI is not null
if (request.getRequestURI() != null) {
logger.trace("doFilterInternal(): request.getRequestURI() : {} ", request.getRequestURI());
String ajaxHeader = request.getHeader("X-Requested-With");
logger.trace("doFilterInternal(): ajaxHeader : {} ", ajaxHeader);
//if it is an AJAX call
if ("XMLHttpRequest".equals(ajaxHeader)) {
logger.trace("doFilterInternal(): An AJAX call, set the last access time, if not already set.");
Long lastAccess = (Long) session.getAttribute(AJAX_DATA_LAST_ACCESS_TIME);
if (lastAccess == null) {
logger.trace("doFilterInternal(): Last access time is null, set current time as lastAccess time.");
lastAccess = System.currentTimeMillis();
session.setAttribute(AJAX_DATA_LAST_ACCESS_TIME, lastAccess);
} else {
logger.trace("doFilterInternal(): max interval: {} -- lastAccess: {} -- currentTimeMillis: {} ",
+ session.getMaxInactiveInterval(), lastAccess, System.currentTimeMillis());
if (((session.getMaxInactiveInterval() * 1000) - (System.currentTimeMillis() - lastAccess)) < 0) {
logger.debug("doFilterInternal(): session should be invalidated as inative time execeeded.");
session.invalidate();
}
}
} else {
logger.trace("doFilterInternal(): Not an AJAX call.");
session.removeAttribute(AJAX_DATA_LAST_ACCESS_TIME);
}
}
}
filterChain.doFilter(request, response);
}
Timeout也可以正常工作,但是一旦超时,我的ajax请求(我在chrome上看到它 - 开发人员工具,网络选项卡)在重定向到另一个页面时会获得302状态。即我的登录页面,其中包含会话超时错误消息。我没有点击任何东西,我再离开它2分钟(我设置2分钟超时进行测试),我再次看到一条日志消息,说明&#34;会话已过期&#34;并重定向到登录。我可以看到在会话到期后创建了一个新的jsessionid
RequestMapping(method = RequestMethod.GET)
public ModelAndView showLogin(Model model,
@RequestParam(value = "error", required = false) String errorStr,
HttpServletRequest request) {
logger.trace("Returning login page view.");
String pageToBeLoaded = "rest/login" ;
System.out.println("showLogin(): errorStr: " + errorStr);
if (errorStr != null) {
if (errorStr.equalsIgnoreCase("sessionExpired")) {
logger.debug("showLogin(): error: sessionExpired ");
System.out.println("showLogin(): error: sessionExpired");
model.addAttribute("error", "Session expired. Please log in again.");
} else if (errorStr.equalsIgnoreCase("denied")) {
logger.debug("showLogin(): error: denied ");
System.out.println("showLogin(): error: denied");
model.addAttribute("error", "Access is denined. This page is for SUPER user only.");
} else {
model.addAttribute("error", getErrorMessage(request, "SPRING_SECURITY_LAST_EXCEPTION"));
System.out.println("showLogin(): error: " + getErrorMessage(request, "SPRING_SECURITY_LAST_EXCEPTION"));
}
}
return new ModelAndView(pageToBeLoaded);
}
我知道我在超时后没有停止轮询服务器,所以它一次又一次地轮询,但是1)如何在不登录应用程序的情况下创建会话?
此外,2)我没有收到会话超时错误消息,因为它被重定向到此页面一次又一次进入登录页面。在用户再次登录之前,我无法保留此错误消息。
任何指针?
答案 0 :(得分:1)
在阅读了关于春季安全及其相关内容的大量文章后,我们才知道这一点 1)会话默认创建(http config,if-required),一旦会话超时,它将自动重定向到invalid-session-url的会话管理上的给定URL并创建另一个会话。我在spring-security调试日志中观察到了这种行为,该日志声明会话无效,如果需要,创建另一个会话。 2)一旦超时会话失效,它将重定向到invalid-session-url;但我的ajax保持从同一页面轮询,创建另一个会话并查找访问我的安全网页的身份验证,因此重定向发生在我的登录页面,但这次没有会话超时错误。
答案 1 :(得分:1)
您正确找到了答案。
让我解释一下。
默认情况下会创建会话是。它被称为ROLE_ANONYMOUS。
默认情况下,如果没有会话(没有带有JSESSIONID的cookie),它将创建一个Role = ROLE_ANONYMOUS的会话。如果已经通过身份验证,则不会创建新会话。
因此ROLE_ANONYMOUS是未经身份验证的用户。
permitAll也包括ANONYMOUS用户。匿名用户将被允许访问该资源。
只需尝试一下。像这样向匿名用户授权管理资源
.antMatchers("/app/admin/*").hasRole("ANONYMOUS")
然后只能在没有登录的情况下访问管理员资源。如果使用管理员凭据登录并尝试访问,则会收到403错误(禁止访问)。
未经授权的用户是一个匿名用户,他还将获得一个会话以标识他,因此,在超时后他的会话也将失效,因此它将重定向到在会话管理expiredUrl中配置的页面。
他是唯一无法访问AuthenticationSuccessHandler的用户