Java 如何使用 Spring Security 自动注销
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27775651/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to log out automatically with Spring Security
提问by grep
I have a spring web application and I did user authentication using Spring security.
我有一个 spring web 应用程序,我使用 Spring security 进行了用户身份验证。
Everything works well. Log in and Log out works perfect!
一切正常。登录和注销工作完美!
Now, I want to implement in order to log out automatically. For example, if user has a window opened for about 30 minutes and do nothing (Sessions expired for instance) system should log out automatically. How can I implement this?
现在,我想实现以自动注销。例如,如果用户打开一个窗口大约 30 分钟并且什么都不做(例如会话过期),系统应该自动注销。我该如何实施?
It might be implemented by client side (I send requests every 1 minutes and check if session is ended). But can't I do this automatically from Spring?
它可能由客户端实现(我每 1 分钟发送一次请求并检查会话是否结束)。但是我不能从 Spring 自动执行此操作吗?
I have this config:
我有这个配置:
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/admin**" />
<access-denied-handler error-page="/403" />
<form-login login-page="/login"
default-target-url="/admin"
authentication-failure-url="/login?error"
username-parameter="NAME"
password-parameter="PASSWORD" />
<logout invalidate-session="true"
logout-success-url="/login?logout"/>
</http>
and in web.xml
并在 web.xml
<session-config>
<session-timeout>1</session-timeout>
</session-config>
after 1 minute, I see that session was destroyed. kill the session after 1 minute. but page was not redirected to /login?logout
1 分钟后,我看到该会话被破坏了。1 分钟后终止会话。但页面没有重定向到 /login?logout
采纳答案by Ye Win
How about to use security configuration.?? I hope below config: will work.
applicationContext.xml
使用安全配置怎么样??我希望下面的配置:会起作用。
应用上下文.xml
--namespace-> xmlns:security="http://www.springframework.org/schema/security"
<security:logout invalidate-session="true"
success-handler-ref="Logout"
logout-url="/logout.html" />
</security:http>
web.xml
网页.xml
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
And them, you need to write your own because success-handler-ref="Logout" is custom handler for logout:
Logout@Component
而他们,你需要自己写,因为 success-handler-ref="Logout" 是注销的自定义处理程序:
Logout@Component
public class Logout extends SimpleUrlLogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
if (authentication != null) {
// do something
}
setDefaultTargetUrl("/login");
super.onLogoutSuccess(request, response, authentication);
}
}
回答by Sezin Karli
You can use a global timeout value by putting this in your web.xml:
您可以通过将其放入 web.xml 来使用全局超时值:
<session-config>
<session-timeout>30</session-timeout>
</session-config>
回答by DanFredell
Here is the tutorial I used. There is java script and server side code. The server will calculate when the session will expire and send it back as a cookie. Then the java script will check every 10 seconds if it is expired, if so it will window.close(). http://www.javaworld.com/article/2073234/tracking-session-expiration-in-browser.html
这是我使用的教程。有java脚本和服务器端代码。服务器将计算会话何时到期并将其作为 cookie 发回。然后java脚本将每10秒检查一次是否过期,如果是,它将window.close()。 http://www.javaworld.com/article/2073234/tracking-session-expiration-in-browser.html
Here is how I implemented it
这是我如何实施它
SessionTimeout.js
会话超时.js
/**
* Monitor the session timeout cookie from Apache and log the user out when expired
*/
"use strict";
var jQuery = require("jquery").noConflict();
var jsCookie = require("js-cookie");
module.exports.registerListener = function() {
calcOffset();
checkSession();
};
/**
* We can't assume the server time and client time are the same
* so lets calcuate the difference
*/
function calcOffset() {
var serverTime = jsCookie.get('serverTime');
serverTime = serverTime==null ? null : Math.abs(serverTime);
var clientTimeOffset = (new Date()).getTime() - serverTime;
jsCookie.set('clientTimeOffset', clientTimeOffset);
}
/**
* Check the sessionExpiry cookie and see if we should send the user to /
*/
function checkSession() {
var sessionExpiry = Math.abs(jsCookie.get('sessionExpiry'));
var timeOffset = Math.abs(jsCookie.get('clientTimeOffset'));
var localTime = (new Date()).getTime();
if(!sessionExpiry){
window.console.log("Unknown session sessionExpiry");
return;
}
if (localTime - timeOffset > (sessionExpiry+15000)) { // 15 extra seconds to make sure
window.location = "/login";
jsCookie.remove('sessionExpiry');
} else {
setTimeout('checkSession()', 10000);
}
window.console.log("Session expires in " + ((sessionExpiry+15000) - localTime - timeOffset) + "ms");
}
window.checkSession = checkSession; //Used for recalling via setTimeout
SessionTimeoutCookieFilter.java
SessionTimeoutCookieFilter.java
public class SessionTimeoutCookieFilter implements Filter {
private static final Logger LOG = LoggerFactory.getLogger(SessionTimeoutCookieFilter.class);
@Override
public void init(FilterConfig config) throws ServletException {
LOG.info("Initialization SessionTimeoutCookieFilter");
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse httpResp = (HttpServletResponse) resp;
HttpServletRequest httpReq = (HttpServletRequest) req;
long currTime = System.currentTimeMillis();
String expiryTime = Long.toString(currTime + httpReq.getSession().getMaxInactiveInterval() * 1000);
Cookie cookie = new Cookie("serverTime", Long.toString(currTime));
cookie.setPath("/");
httpResp.addCookie(cookie);
if (httpReq.getRemoteUser() != null) {
cookie = new Cookie("sessionExpiry", expiryTime);
}
cookie.setPath("/");
httpResp.addCookie(cookie);
filterChain.doFilter(req, resp);
}
Add the filter
添加过滤器
public class ApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
...
@Override
protected Filter[] getServletFilters() {
return new Filter[]{new SessionTimeoutCookieFilter()};
}
}
回答by Milan Vidakovic
To redirect to login page after a session expiration add the "invalid-session-url" tag to your "session-management" bean in security context:
要在会话到期后重定向到登录页面,请将“invalid-session-url”标签添加到安全上下文中的“session-management”bean:
<session-management invalid-session-url="/error-login">
....
</session-management>
In my case, I'm redirecting to error-login page where an error message is displayed and I can re-login. Mind you, on session expiration, you're not going to be automatically redirected. You need to click on any part of your page and that will trigger the redirection.
就我而言,我正在重定向到错误登录页面,其中显示错误消息,我可以重新登录。请注意,在会话到期时,您不会被自动重定向。您需要单击页面的任何部分,这将触发重定向。
回答by PraveenKumar Lalasangi
Either it may be spring-security, spring-mvc or servlet, auto logout is not possible without perfect client side logic.
Considering application will have both type of request
它可能是 spring-security、spring-mvc 或 servlet,如果没有完美的客户端逻辑,自动注销是不可能的。
考虑到应用程序将有两种类型的请求
- AJAX and
- form submission/page reload
- AJAX 和
- 表单提交/页面重新加载
Auto logout needs very calculated logic. Presenting my autologout functionality implementation with following
自动注销需要非常计算的逻辑。使用以下内容展示我的自动注销功能实现
Advantages.
好处。
1. 没有使用额外的调用/请求来实现这一点。如果超过 10k 的活跃用户和额外的调用来实现自动注销,则考虑性能影响。
2. 使用标签的一条线路配置。
3. 即使用户打开多个选项卡或多个窗口也能完美运行。
4. 会话失效前30秒提示您,如果您填写了表单没有提交,您可以保持会话活跃(一键延长会话)。因此用户不太可能丢失未保存的数据。
Usage
用法
1. Include auto logout script in required JSP pages as given below.
1. 在所需的 JSP 页面中包含自动注销脚本,如下所示。
....
</body>
<jsp:include page="../template/autologout-script.jsp"></jsp:include>
</html>
2. Create a JSP page, autologout-script.jsp and add below code.Note: No editing/configuring is required
2. 创建一个 JSP 页面 autologout-script.jsp 并添加以下代码。注意:不需要编辑/配置
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<script>
$(document).ready(function()
{
var timeOutTimeInSeconds = ${ timeOutTimeInSeconds };
var showTimerTimeInSeconds= ${ showTimerTimeInSeconds };
var sessionCheckIntervalId = setInterval(redirectToLoginPage, timeOutTimeInSeconds * 1000);
var timerDisplayIntervalId = setInterval(showTimer, (timeOutTimeInSeconds - showTimerTimeInSeconds) * 1000);
var badgeTimerId;
window.localStorage.setItem("AjaxRequestFired", new Date());
function redirectToLoginPage(){
//location.href = '<c:url value="/" />'+'${loginPageUrl}';
window.location.reload();
}
$(document).ajaxComplete(function () {
resetTimer();
});
$(window).bind('storage', function (e) {
if(e.originalEvent.key == "AjaxRequestFired"){
console.log("Request sent from another tab, hence resetting timer")
resetTimer();
}
});
function resetTimer()
{
showTimerTimeInSeconds= ${ showTimerTimeInSeconds };
console.log("timeOutTimeInSeconds : "+timeOutTimeInSeconds)
window.localStorage.setItem("AjaxRequestFired", new Date());
window.clearInterval(sessionCheckIntervalId);
sessionCheckIntervalId = setInterval(redirectToLoginPage, timeOutTimeInSeconds * 1000);
window.clearInterval(timerDisplayIntervalId);
timerDisplayIntervalId = setInterval(showTimer, (timeOutTimeInSeconds - showTimerTimeInSeconds) * 1000);
hideTimer();
}
function showTimer()
{
$('#sessionTimeRemaining').show();
$('#sessionTimeRemainingBadge').html(showTimerTimeInSeconds--);
window.clearInterval(timerDisplayIntervalId);
badgeTimerId = setInterval(function(){
$('#sessionTimeRemainingBadge').html(showTimerTimeInSeconds--);
}, 1000);
}
function hideTimer()
{
window.clearInterval(badgeTimerId);
$('#sessionTimeRemaining').hide();
}
});
</script>
3. Configure session attributes to configuring timeout settingNote: Configure this after session creation. You can implement HttpSessionListener sessionCreated method and set the following configuration as per your requirement.
3. 配置会话属性以配置超时设置注意:在会话创建后进行配置。您可以实现 HttpSessionListener sessionCreated 方法并根据您的要求设置以下配置。
session.setMaxInactiveInterval(300);
session.setAttribute("timeOutTimeInSeconds", 300);
session.setAttribute("showTimerTimeInSeconds", 30);
4. Add below html for displaying timer.
Note: it can be moved to autologout-script template page if you are good at CSS. Hence you can avoid to add this in each and every page.
Include bootstrap or add your custom css.
4. 添加下面的 html 以显示计时器。
注意:如果你擅长CSS,它可以移动到autologout-script模板页面。因此,您可以避免在每个页面中添加此内容。
包括引导程序或添加您的自定义 css。
<span class="badge badge-primary" title="click to keep session alive" id="sessionTimeRemaining"
onclick="ajaxSessionRefresh()" style="display:none;">
<i class="badge badge-danger" id="sessionTimeRemainingBadge" style="float:left">30</i>
<small>Refresh</small>
<i class="glyphicon glyphicon-refresh"></i>
</span>
That is all about a simple auto logout implementation.
You can download working example from my github repository
Autologout using simple servlet example
Autologout using spring-security java configuration example
Autologout using spring-security xml configuration example
这就是一个简单的自动注销实现。您可以从我的 github 存储库下载工作示例
Autologout using simple servlet example
Autologout using spring-security java 配置示例
Autologout using spring-security xml 配置示例
Logic Explained
逻辑解释
Case 1: On Page load案例1:页面加载
这里的逻辑很简单,在页面加载时将间隔定时器设置为maxInactiveInterval。超时后重定向到登录页面。
Case 2: Keep track AJAX calls案例 2:跟踪 AJAX 调用
现在考虑 AJAX 请求,您可以使用 jquery 的 .ajaxStart() 或 .ajaxComplete() 回调,以便在触发任何 ajax 请求时您可以重置间隔。
Case 3: Tracking multi tab/window activity案例 3:跟踪多标签/窗口活动
进行标签间通信以同步每个标签的状态。在更改事件上使用 localStorage。
Limitations/Improvements required
1. If maximum allowed session is one, if session is taken from another system, AJAX request will fail. It needs to be handled to redirect to login page.
2. Use ajaxStart() instead of ajaxComplete() to have exact sync of idleTime values between server and browser.
需要的限制/改进
1. 如果允许的最大会话是一个,如果会话是从另一个系统获取的,AJAX 请求将失败。需要处理重定向到登录页面。
2. 使用 ajaxStart() 而不是 ajaxComplete() 在服务器和浏览器之间精确同步 idleTime 值。
Requirements
1. Jquery
需求
1. jQuery
Alternatives to current implementation compared
比较当前实现的替代方案
1. Setting Refresh header在 http 响应中设置刷新标头。(Not works for AJAX requests)(不适用于 AJAX 请求)
response.setHeader("Refresh", "60; URL=login.jsp");
- Setting meta refresh tagin HTML (Not works for AJAX requests)
- 在 HTML 中设置元刷新标记(不适用于 AJAX 请求)
<meta http-equiv="refresh" content="60; url=login.jsp">
- Configuring Activity checkerKeeps session alive by repeated AJAX request. Tracks idle time and makes logout request after timeout.
No doubt it is a good one with simple logic. But i want to just ink my observations.- Performance impactif 2 requests are made per minute to keep session alive and 50k active users. 100k requests per minute.
- Intertab communicationIf two tabs are open, one tab is receiving activity but other tab is not receiving activity, that tab fires logout request and invalidate session even though activity is present in other tab. (But can be handled)
- Force logout approachIt is a client is dominated over server to invalidate session.
- 配置活动检查器通过重复的 AJAX 请求使会话保持活动状态。跟踪空闲时间并在超时后发出注销请求。
毫无疑问,这是一本逻辑简单的好书。但我只想写下我的观察。- 如果每分钟发出 2 个请求以保持会话活动和 50k 活跃用户,则性能影响。每分钟 10 万个请求。
- Intertab 通信如果两个选项卡打开,一个选项卡正在接收活动,而另一个选项卡未接收活动,即使其他选项卡中存在活动,该选项卡也会触发注销请求并使会话无效。(但可以处理)
- 强制注销方法客户端控制服务器使会话无效。