spring scurity session管理

前言

会话固定攻击:利用服务器会话不变机制,借他人之手获取认证和受权,而后冒充他人。举例以下:java

1.A先打开一个网站 "http://unsafe",而后服务器会回复他一个session id。比session id=mjg4qid0wioq, Mallory把这个id记下了;
2.A给B发送一个电子邮件,他伪装是银行在宣传本身的新业务,例如,我行推出了一项新服务,率先体验请点击:"http://unsafe/?sessionId=mjg4qid0wioq",sessionId后面是A本身的session id; 
3.Alice被吸引了,点击了"http://unsafe/?sessionId=mjg4qid0wioq",像往常同样,输入了本身的账号和口令从而登陆到银行网站;
4.由于服务器的session id不改变,如今A点击"http://unsafe/?SID=mjg4qid0wioq"后,他就拥有了B的身份。能够随心所欲了。

图例:
clipboard.pngspring

session fixation防御

spring security默认是开通session fixation防御的,若是想显示开通,能够经过如下代码实现:浏览器

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

spring 防御的原理为,每当用户认证事后,就会从新生成一个新的session,并抛弃旧的session,下图是spring security的防御原理:
图片描述
从上图中可知,SessionManagementFilter负责检查一个用户是不是新认证的用户,若是是则会调用接口SessionAuthenticationStrategy进行处理,SessionAuthenticationStrategy的实现类SessionFixationProtectionStrategy会为用户建立一个新的session 同时丢弃旧的session。服务器

spring security session的几个方法选项:
none() 关闭spring security的session防御功能,spring不会配置SessionManagementFilter类;
migrateSession() 用户认证以后,会从新建立一个新的session,而且将旧session中的属性,迁移到新的session中;
newSession()用户认证以后,会新建立一个session,可是不会将旧的session中的属性,迁移到新的session中session

单用户并发session控制

确保单个用户的单个帐号,只有一个活跃的session,这也是一个常见的需求,先看下在Security.java中的代码配置:并发

http.sessionManagement().maximumSessions(1)

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

并发控制的原理
Spring security使用SessionRegistry来维护一个列表,列表记录了每个活跃的session以及与之相关的认证用户,当session的生命周期发生变化时,容器会产生一个HttpSessionEventPublisher事件,SessionRegistry捕获到HttpSessionEventPublisher事件,并实时地更新列表。当用户访问一个受保护的站点时,SessionManagerFilter会经过SessionRegistry检查用户的激活session是否存在,若不存在,则将SessionRegistry中保存的激活session置为无效。另外,ConcurrentSessionFilter会识别session是否过时,若session过时则更新SessionRegistry。session的过时事件多是由服务器产生,也多是由ConcurrentSessionControlStrategy强制设置为过时。原理图以下所示:
图片描述app

配置session过时重定位地址
当spring security检查到session过时后,若未作任何配置,spring security会返回一个用户不友好的页面,所以咱们一般须要设置一个地址,当spring security检查到session过时后,将请求重定位到咱们的地址上,设置代码以下所示:网站

http.sessionManagement().expiredUrl("/login");

阻止认证而不是强制退出

当一个用户已经认证过了,在另一个地方从新进行登陆认证,spring security能够阻止其再次登陆认证,从而保持原来的会话可用性;具体的代码设置以下所示:this

http.sessionManager().maximumSession(1).maxSessionsPreventsLogin(true);

当时上述代码还存在一个问题,当用户登录后,没有退出直接关闭浏览器,则再次打开浏览器时,此时浏览器的session若被删除的话,用户只能等到服务器的session过时后,才能再次登陆。spa

一些常见问题

1.当采用传统的UserDetails认证登陆时,若UserDetails的equals方法和hashcode方法没有进行有效的实现,可能会致使,同一个用户屡次登陆,可是不会触发登陆退出事件,这是由于SessionRegistry是使用内存map来存储UserDetails的,对UserDetails的比较会调用其自身的equal方法;
2.当用户会话持久化到磁盘后,应用服务器重启时,会读取磁盘上的会话,这时已经使用有效会话登陆的用户,应该是登陆状态,但此时sessionRegistry的内存map是空,所以spring security会报告用户未登陆,为解决该问题,有两种方法,一是自定义sessionRegistry的实现,并在容器中禁用会话持久化功能,二是必须实现容器特定的方式,以确保在启动时将持久化会话填充到内存映射中。
3.spring security较低的版本并无在session的并发控制中实现‘记住我功能’;
4.session并发控制的缺省实现并不能用于集群环境中,所以其缺省实现是将session记录在内存map中,服务器1中记录的信息和服务器2记录的信息并不相同;

典型的业务场景

如今咱们有这样一个业务场景,我容许用户屡次登陆,同时登陆的用户能够查看相同的帐号在不一样的地方的登陆session,而且对这些session进行管理,好比查看,删除等操做;如何使用spring security实现上述功能?
It is easy!

http.sessionManagement().maximumSession(-1).sessionRegistry(sessionRegistryImp()).expiredUrl("login")

@Bean 
public SessionRegistry sessionRegistryImp(){
    return new SessionRegistryImp();
}

接下来,咱们就能够将SessionRegistry实例注入到Controller里面,经过SessionRegistry获取与当前认证用户相关的全部session,示例代码以下所示:

@GetMapping("/user/sessions/")
public String sessions(Authentication authentication,Model model){
    List<SessionInformation> list=sessionRegistry.getAllSessions(authentication.getPrincipal(),false);
}

认证信息和HttpSession的关系

1.在每个request的开始,SecurityContextPersistenceFilter负责经过SecurityContextRepository获取SecurityContext的实现;并将其设置到SecurityContextHolder中,随后能够在controller中经过SecurityContextHolder访问SecurityContext;
2.在请求结束,SecurityContextPersistenceFilter会从SecurityContextHolder中取出SecurityContext并将其保存到SecurityContextRepository中

The question that now arises is how is this related to HttpSession? This is all tied together by the default SecurityContextRepository implementation, which uses HttpSession。

SecurityContextRepository的缺省实现HttpSessionSecurityContextRepository,使用HttpSession检索和保存SecurityContext的实现,spring security除了只提供了一个SecurityContextRepository的实现即为HttpSessionSecurityContextRepository,可是咱们能够定义本身的SecurityContextRepository实现。

控制HttpSession的建立

咱们能够经过http.create-session(params)方法来控制HttpSession的建立,params的参数值以下图所示:
图片描述

参数对HttpSession的控制并不全面,它只控制了HttpSession建立的一个子集,为了跟踪HttpSession的建立,咱们可使用DebugFilter来调试spring security,它会输出session的建立信息,固然DebugFilter不只能调试Session,还能调试其余的信息。

相关文章
相关标签/搜索