會話固定攻擊:利用服務器會話不變機制,借他人之手獲取認證和受權,而後冒充他人。舉例以下: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的身份。能夠隨心所欲了。
圖例:
spring
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,這也是一個常見的需求,先看下在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); }
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實現。
咱們能夠經過http.create-session(params)方法來控制HttpSession的建立,params的參數值以下圖所示:
參數對HttpSession的控制並不全面,它只控制了HttpSession建立的一個子集,爲了跟蹤HttpSession的建立,咱們可使用DebugFilter來調試spring security,它會輸出session的建立信息,固然DebugFilter不只能調試Session,還能調試其餘的信息。