使用Spring Security控制会话

1.概述

在本文中,咱们将说明Spring Security如何容许咱们控制HTTP会话。此控件的范围从会话超时到启用并发会话和其余高级安全配置。html

2.会话什么时候建立?

咱们能够准确控制会话什么时候建立以及Spring Security如何与之交互:git

  • always - 若是一个会话尚不存在,将始终建立一个会话
  • ifRequired - 仅在须要时建立会话(默认)
  • never - 框架永远不会建立会话自己,但若是它已经存在,它将使用一个
  • stateless - Spring Security不会建立或使用任何会话
<http create-session="ifRequired">...</http>
Java配置:
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
}

解此配置仅控制Spring Security的功能很是重要 - 而不是整个应用程序。若是咱们不指示Spring Security,可能没法建立会话,但咱们的应用程序可能会!github

默认状况下,Spring Security会在须要时建立会话 - 这是“ifRequired”web

对于更无状态的应用程序,“never”选项将确保Spring Security自己不会建立任何会话;可是,若是应用程序建立了一个,那么Spring Security将使用它。spring

最后,最严格的会话建立选项 - “stateless” - 保证应用程序根本不会建立任何会话编程

这是在Spring 3.1中引入的,它将有效地跳过部分Spring Security过滤器链。主要是会话相关的部分,如HttpSessionSecurityContextRepository,SessionManagementFilter,RequestCacheFilter浏览器

这些更严格的控制机制直接暗示不使用cookie,因此每一个请求都须要从新进行身份验证。这种无状态架构适用于REST API及其无状态约束。它们也适用于基本和摘要式身份验证等身份验证机制。安全

3. Under The Hood

在执行身份验证过程以前,Spring Security将运行一个负责在请求之间存储安全上下文的过滤器-SecurityContextPersistenceFilter。上下文将根据策略存储 - 默认状况下为HttpSessionSecurityContextRepository - 它使用HTTP会话做为存储。对于strict create-session =“stateless”属性,此策略将替换为另外一个 - NullSecurityContextRepository - 而且不会建立或使用会话来保留上下文。cookie

4.并发会话控制

当已通过身份验证的用户尝试再次进行身份验证时,应用程序能够经过如下几种方式之一处理该事件。它可使用户的活动会话无效,并使用新会话再次对用户进行身份验证,或者容许两个会话同时存在session

启用并发会话控制支持的第一步是在web.xml中添加如下侦听器:

<listener>
    <listener-class>
      org.springframework.security.web.session.HttpSessionEventPublisher
    </listener-class>
</listener>

或者将其定义为Bean - 以下所示:

@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
    return new HttpSessionEventPublisher();
}

这对于确保在销毁会话时通知Spring Security会话注册表是相当重要。

要为同一用户启用容许多个并发会话的方案,应在XML配置中使用<session-management>元素:

<http ...>
    <session-management>
        <concurrency-control max-sessions="2" />
    </session-management>
</http>

或者,经过Java配置:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.sessionManagement().maximumSessions(2)
}

5.会话超时

会话超时后,若是用户发送的会话ID已过时,则会将其重定向到可经过命名空间配置的URL:

<session-management>
    <concurrency-control expired-url="/sessionExpired.html" ... />
</session-management>

一样,若是用户发送的会话ID未过时但彻底无效,则它们也会被重定向到可配置的URL:

<session-management invalid-session-url="/invalidSession.html">
    ...
</session-management>

相应的Java配置:

http.sessionManagement()
  .expiredUrl("/sessionExpired.html")
  .invalidSessionUrl("/invalidSession.html");

6.防止使用URL参数进行会话跟踪

在URL中公开会话信息的安全风险愈来愈大(从2007年的第7位到2013年在OWASP排行榜前10位的第2位)。

从Spring 3.0开始,如今能够经过在<http>命名空间中设置disable-url-rewriting =“true”来禁用将jsessionid附加到URL的URL重写逻辑。

或者,从Servlet 3.0开始,也能够在web.xml中配置会话跟踪机制

<session-config>
     <tracking-mode>COOKIE</tracking-mode>
</session-config>

编程方式

servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE));

这将选择存储JSESSIONID的位置 - 在cookie或URL参数中

7. Spring Security的会话固定保护

该框架经过配置在用户已有会话的状况但尝试再次进行身份验证时,提供了针对典型会话固定攻击的保护:

<session-management session-fixation-protection="migrateSession"> ...

相应的Java配置:

http.sessionManagement()
  .sessionFixation().migrateSession()

默认状况下,Spring Security启用了此保护(“migrateSession”) - 在身份验证时,会建立一个新的HTTP会话,旧的会话将失效,旧会话的属性将被复制

若是这不是所需的行为,则可使用其余两个选项:

  • 设置“none”时,原始会话不会失效
  • 设置“newSession”时,将建立一个干净的会话,而不会复制旧会话中的任何属性

8.安全会话Cookie

接下来,咱们将讨论如何保护会话cookie。 咱们可使用httpOnly和secure标签来保护咱们的会话cookie

  • httpOnly:若是为true,那么浏览器脚本将没法访问cookie
  • secure:若是为true,则cookie将仅经过HTTPS链接发送

咱们能够在web.xml中为会话cookie设置这些标志:

<session-config>
    <session-timeout>1</session-timeout>
    <cookie-config>
        <http-only>true</http-only>
        <secure>true</secure>
    </cookie-config>
</session-config>

从Java servlet 3开始,此配置选项可用。默认状况下,http-only为true且secure为false

咱们来看看相应的Java配置:

public class MainWebAppInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext sc) throws ServletException {
        // ...
        sc.getSessionCookieConfig().setHttpOnly(true);        
        sc.getSessionCookieConfig().setSecure(true);        
    }
}

若是咱们使用Spring Boot,咱们能够在application.properties中设置这些标志:

server.servlet.session.cookie.http-only=true
server.servlet.session.cookie.secure=true

最后,咱们还可使用Filter手动实现此目的:

public class SessionFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        Cookie[] allCookies = req.getCookies();
        if (allCookies != null) {
            Cookie session = 
              Arrays.stream(allCookies).filter(x -> x.getName().equals("JSESSIONID"))
                    .findFirst().orElse(null);
 
            if (session != null) {
                session.setHttpOnly(true);
                session.setSecure(true);
                res.addCookie(session);
            }
        }
        chain.doFilter(req, res);
    }
}

9.Session使用

9.1。 Session Scoped Beans

只需在web-Context中,使用@Scope注释声明的bean:

@Component
@Scope("session")
public class Foo { .. }

或者使用XML:

<bean id="foo" scope="session"/>

而后,bean能够简单地注入另外一个bean:

@Autowired
private Foo theFoo;

Spring会将新bean绑定到HTTP Session的生命周期。

9.2。将会话注入控制器

原始HTTP会话也能够直接注入Controller方法:

@RequestMapping(..)
public void fooMethod(HttpSession session) {
    session.addAttribute(Constants.FOO, new Foo();
    //...
    Foo foo = (Foo) session.getAttribute(Constants.Foo);
}

9.3。获取会话

当前的HTTP Session也能够经过原始Servlet API以编程方式得到:

ServletRequestAttributes attr = (ServletRequestAttributes) 
    RequestContextHolder.currentRequestAttributes();
HttpSession session= attr.getRequest().getSession(true); // true == allow create

10.总结

在本文中,咱们讨论了使用Spring Security管理Sessions。此外,Spring Reference包含一个很是好的会话管理常见问题解答

与往常同样,本文中提供的代码能够在Github上获得。这是一个基于Maven的项目,所以它应该很容易导入和运行。

原文出处:https://www.cnblogs.com/xjknight/p/10897849.html

相关文章
相关标签/搜索