简体   繁体   English

如何从Spring Security中的会话管理(超时/并发检查)中排除某些页面?

[英]How to exclude some page from session management (timeout/concurrency check) in Spring Security?

I added this code in spring-security.xml to enable session timeout check and concurrency check. 我在spring-security.xml添加了此代码,以启用会话超时检查和并发检查。

<sec:http>
    <sec:form-login login-page="/login" login-processing-url="/authentication"
                    default-target-url="/home" always-use-default-target="true"
                    authentication-failure-url="/login?error=true"
                    username-parameter="userid" password-parameter="password"/>
    <sec:logout logout-url="/logout" logout-success-url="/login" delete-cookies="JSESSIONID" invalidate-session="true" />

    <!-- User login (URL not View Name) -->
    <sec:intercept-url pattern="/login" access="permitAll" />

    <!-- User change password -->
    <sec:intercept-url pattern="/change_password" access="permitAll" />


    <sec:session-management invalid-session-url="/session_timeout">
        <sec:concurrency-control max-sessions="1" error-if-maximum-exceeded="false" expired-url="/session_conflict"/>
    </sec:session-management>

    <sec:headers>
        <sec:frame-options policy="SAMEORIGIN" />
    </sec:headers>
</sec:http>

but the question is, 但问题是,

  1. I need to exclude some page like login change_password from session check (timeout and concurrency). 我需要从会话检查(超时和并发)中排除一些页面,如login change_password

  2. If I have a page that accessible for both logged user or un-logged user. 如果我有一个可供登录用户或未登录用户访问的页面。 But I need to do session timeout and concurrency check only when the user logged in. 但是我只需要在用户登录时进行会话超时和并发检查。

How should I implement this? 我该如何实现呢?

Thanks very much. 非常感谢。

Update : I tested my original session="false" answer on one of my Spring Security login pages and it didn't work. 更新 :我在我的一个Spring Security登录页面上测试了我原来的session="false"答案,但它没有用。 See further below for a better solution. 请参阅下面的更多解决方案。


Original answer : 原始答案

Adding <%@page session="false"%> to the top of the JSP file should prevent a session from being started but this is unrelated to Spring Security. 将<%@ page session =“false”%>添加到JSP文件的顶部应该会阻止会话启动,但这与Spring Security无关。

There is some very minimal Oracle documentation at the URL below that says: 下面的URL中有一些非常小的Oracle文档说:

JSP Default Session Requests JSP默认会话请求

Generally speaking, servlets do not request an HTTP session by default. 一般来说,servlet默认不会请求HTTP会话。 However, JSP page implementation classes do request an HTTP session by default. 但是,JSP页面实现类默认情况下会请求HTTP会话。 You can override this by setting the session parameter to false in a JSP page directive, as follows: 您可以通过在JSP页面指令中将session参数设置为false来覆盖它,如下所示:

<%@ page ... session="false" % <%@ page ... session =“false”%

https://docs.oracle.com/cd/A87860_01/doc/java.817/a83726/basics3.htm#1007356 https://docs.oracle.com/cd/A87860_01/doc/java.817/a83726/basics3.htm#1007356


Updated answer : The problem may be related to Spring Security having CSRF enabled. 更新的答案 :问题可能与启用CSRF的Spring Security有关。 It is enabled by default in version 4.0 which is a good thing and not something you want to disable. 它在4.0版中默认启用,这是一件好事,而不是你想要禁用的东西。 In earlier versions it may have needed to be manually. 在早期版本中,可能需要手动操作。

CRSF tokens require a session so you need a solution for excluding the CSRF testing on just the login process. CRSF令牌需要一个会话,因此您需要一个解决方案来仅在登录过程中排除CSRF测试。

There is discussion of "Relaxing CSRF" in the Spring Security Reference. 在Spring Security Reference中讨论了“放松CSRF” They specifically mention SockJS but the principle is universal. 他们特别提到了SockJS,但原则是普遍的。 They suggest something like the line below for JavaConfig: 他们建议类似下面的JavaConfig行:

http.csrf().ignoringAntMatchers("/login")

The accepted answer by P.Peter for the SO question "CSRF token expires during login" has a similar solution that requires a little more effort. P.Peter对SO问题“CSRF令牌在登录期间到期”的接受答案有一个类似的解决方案,需要更多的努力。

You need to add a class that will implement Spring's RequestMatcher class and override it's matches() method: 你需要添加一个实现Spring的RequestMatcher类的类并覆盖它的matches()方法:

class CsrfSecurityRequestMatcher implements RequestMatcher {
    @Override
    public boolean matches(HttpServletRequest request) {
        return !request.getServletPath().equals("/login");
    }
}

If you need more complex matching logic you can add Pattern and RegexRequestMatcher fields in your custom RequestMatcher class and use them in your matches() method. 如果您需要更复杂的匹配逻辑,可以在自定义RequestMatcher类中添加Pattern和RegexRequestMatcher字段,并在matches()方法中使用它们。

Then you will need to add a reference to the new class in your Spring Security configuration. 然后,您需要在Spring Security配置中添加对新类的引用。 In XML it would be something like this: 在XML中它将是这样的:

<http>
    <csrf request-matcher-ref="csrfSecurityRequestMatcher"/>
</http>

In JavaConfig it would be something like this: 在JavaConfig中它将是这样的:

http.csrf().requireCsrfProtectionMatcher(new CsrfSecurityRequestMatcher());

I'm unsure what security ramifications there may be to disabling CSRF testing on the login page so you may want to look into that. 我不确定在登录页面上禁用CSRF测试可能会产生什么样的安全后果,所以你可能想要研究一下。

If you decide that disabling CSRF for your login page is unacceptable then you can use an AJAX keepalive solution that prevents your login page from expiring but that is a bit hackish for my taste. 如果您认为禁用登录页面的CSRF是不可接受的,那么您可以使用AJAX keepalive解决方案来阻止您的登录页面过期,但这有点像我的口味。

Session is created regardless of spring, security, etc. Mechanism behind session is fully automatic, you do not need to change or do anything. 无论弹簧,安全性如何,都会创建会话。会话后面的机制是全自动的,您无需更改或执行任何操作。 Instead of digging into Spring details, you can use plain javax.servlet API and accomplish what you need. 您可以使用普通的javax.servlet API来完成所需的工作,而不是深入了解Spring细节。

And what you need to do is simple: differentiate between anonymous session and authenticated user sessions with the help of session-bound custom object. 您需要做的很简单:在会话绑定自定义对象的帮助下区分匿名会话和经过身份验证的用户会话。

Session can hold objects, and server maintains objects bound to specific session (normally via session cookie jsessionid). 会话可以保存对象,服务器维护绑定到特定会话的对象(通常通过会话cookie jsessionid)。 Example of such a object is following code: 这样的对象的示例如下:

public class SessionUsr implements java.io.Serializable {
    private static final long serialVersionUID = 6034793247940424913L;

    // do not make this fields nonfinal. This object is in session as serialized stream and
    // setting the fields does not refresh the object in session. You must replace it

    protected final boolean isAnonymous;
    protected final String userName; 

    public SessionUsr(boolean isAnonymous, String userName) {
        super();
        this.isAnonymous = isAnonymous;
        this.userName = userName;
    }
    public boolean isAnonymous() {
        return this.isAnonymous;
    }
    public String getUserName() {
        return this.userName;
    }

    @Override
    public String toString() {
        return "SessionUsr [isAnonymous=" + this.isAnonymous + ", userName=" + this.userName + "]";
    }
    @Override
    public int hashCode() {
        int result = 17;
        result = 31 * result + (null == getUserName() ? 0 : getUserName().hashCode());
        result = 31 * result + (!isAnonymous() ? 0 : 1);
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof SessionUsr)) {
            return false;
        }

        final SessionUsr comparation= (SessionUsr) obj;
        if (comparation.getUserName().equals(this.userName))
            return true;

        return false;
    }

}

Session listener to track events 会话侦听器跟踪事件

import java.util.logging.Logger;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class SessionListener implements HttpSessionListener {

    public static final Logger log = Logger.getLogger(SessionListener.class.getCanonicalName());

    /**
     * Method is called after new client connects   
     * @see javax.servlet.http.HttpSessionListener#sessionCreated(javax.servlet.http.HttpSessionEvent)
     */
    @Override
    public void sessionCreated(HttpSessionEvent arg0) {
        log.info("SESSION CREATED with id " + arg0.getSession().getId());
        final SessionUsr authenticatedUser  = new SessionUsr(false, "anonymous");
        arg0.getSession().setAttribute("_USR", authenticatedUser);
    }

    /** 
     * Invalidation or timeout definined in web.xml (session-timeout).
     * @see javax.servlet.http.HttpSessionListener#sessionDestroyed(javax.servlet.http.HttpSessionEvent)
     */
    @Override
    public void sessionDestroyed(HttpSessionEvent arg0) {
        log.info("SESSION DESTROYED, INVALIDATED " + arg0.getSession().getId());
    }

}

In your authorization and authentication code, after user is successfully validated, you replace session attribute with the code 在授权和身份验证代码中,成功验证用户后,将session属性替换为代码

final SessionUsr authorizated = new SessionUsr("John Kennedy", true);
getSession().setAttribute("_USR", authorizated);

And you can retrieve this user anywhere with : 您可以在任何地方检索此用户:

final SessionUsr authenticatedUser  = (SessionUsr) getSession().getAttribute("_USR");

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM