自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

SpringSecurity系列之查看登錄詳情

開發(fā) 架構
Authentication 接口用來保存我們的登錄用戶信息,實際上,它是對主體(java.security.Principal)做了進一步的封裝。

 [[398075]]

上篇文章跟大家聊了如何使用更加優(yōu)雅的方式自定義 Spring Security 登錄邏輯,更加優(yōu)雅的方式可以有效避免掉自定義過濾器帶來的低效,建議大家一定閱讀一下,也可以順便理解 Spring Security 中的認證邏輯。

本文將在上文的基礎上,繼續(xù)和大家探討如何存儲登錄用戶詳細信息的問題。

1.Authentication

Authentication 這個接口前面和大家聊過多次,今天還要再來聊一聊。

Authentication 接口用來保存我們的登錄用戶信息,實際上,它是對主體(java.security.Principal)做了進一步的封裝。

我們來看下 Authentication 的一個定義:

  1. public interface Authentication extends Principal, Serializable { 
  2.  Collection<? extends GrantedAuthority> getAuthorities(); 
  3.  Object getCredentials(); 
  4.  Object getDetails(); 
  5.  Object getPrincipal(); 
  6.  boolean isAuthenticated(); 
  7.  void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException; 

接口的解釋如下:

  1. getAuthorities 方法用來獲取用戶的權限。
  2. getCredentials 方法用來獲取用戶憑證,一般來說就是密碼。
  3. getDetails 方法用來獲取用戶攜帶的詳細信息,可能是當前請求之類的東西。
  4. getPrincipal 方法用來獲取當前用戶,可能是一個用戶名,也可能是一個用戶對象。
  5. isAuthenticated 當前用戶是否認證成功。

這里有一個比較好玩的方法,叫做 getDetails。關于這個方法,源碼的解釋如下:

Stores additional details about the authentication request. These might be an IP address, certificate serial number etc.

從這段解釋中,我們可以看出,該方法實際上就是用來存儲有關身份認證的其他信息的,例如 IP 地址、證書信息等等。

實際上,在默認情況下,這里存儲的就是用戶登錄的 IP 地址和 sessionId。我們從源碼角度來看下。

2.源碼分析

松哥的 SpringSecurity 系列已經寫到第 12 篇了,看了前面的文章,相信大家已經明白用戶登錄必經的一個過濾器就是 UsernamePasswordAuthenticationFilter,在該類的 attemptAuthentication 方法中,對請求參數(shù)做提取,在 attemptAuthentication 方法中,會調用到一個方法,就是 setDetails。

我們一起來看下 setDetails 方法:

  1. protected void setDetails(HttpServletRequest request, 
  2.   UsernamePasswordAuthenticationToken authRequest) { 
  3.  authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); 

UsernamePasswordAuthenticationToken 是 Authentication 的具體實現(xiàn),所以這里實際上就是在設置 details,至于 details 的值,則是通過 authenticationDetailsSource 來構建的,我們來看下:

  1. public class WebAuthenticationDetailsSource implements 
  2.   AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> { 
  3.  public WebAuthenticationDetails buildDetails(HttpServletRequest context) { 
  4.   return new WebAuthenticationDetails(context); 
  5.  } 
  6. public class WebAuthenticationDetails implements Serializable { 
  7.  private final String remoteAddress; 
  8.  private final String sessionId; 
  9.  public WebAuthenticationDetails(HttpServletRequest request) { 
  10.   this.remoteAddress = request.getRemoteAddr(); 
  11.  
  12.   HttpSession session = request.getSession(false); 
  13.   this.sessionId = (session != null) ? session.getId() : null
  14.  } 
  15.     //省略其他方法 

默認通過 WebAuthenticationDetailsSource 來構建 WebAuthenticationDetails,并將結果設置到 Authentication 的 details 屬性中去。而 WebAuthenticationDetails 中定義的屬性,大家看一下基本上就明白,這就是保存了用戶登錄地址和 sessionId。

那么看到這里,大家基本上就明白了,用戶登錄的 IP 地址實際上我們可以直接從 WebAuthenticationDetails 中獲取到。

我舉一個簡單例子,例如我們登錄成功后,可以通過如下方式隨時隨地拿到用戶 IP:

  1. @Service 
  2. public class HelloService { 
  3.     public void hello() { 
  4.         Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 
  5.         WebAuthenticationDetails details = (WebAuthenticationDetails) authentication.getDetails(); 
  6.         System.out.println(details); 
  7.     } 

這個獲取過程之所以放在 service 來做,就是為了演示隨時隨地這個特性。然后我們在 controller 中調用該方法,當訪問接口時,可以看到如下日志:

  1. WebAuthenticationDetails@fffc7f0c: RemoteIpAddress: 127.0.0.1; SessionId: 303C7F254DF8B86667A2B20AA0667160 

可以看到,用戶的 IP 地址和 SessionId 都給出來了。這兩個屬性在 WebAuthenticationDetails 中都有對應的 get 方法,也可以單獨獲取屬性值。

3.定制

當然,WebAuthenticationDetails 也可以自己定制,因為默認它只提供了 IP 和 sessionid 兩個信息,如果我們想保存關于 Http 請求的更多信息,就可以通過自定義 WebAuthenticationDetails 來實現(xiàn)。

如果我們要定制 WebAuthenticationDetails,還要連同 WebAuthenticationDetailsSource 一起重新定義。

結合上篇文章的驗證碼登錄,我跟大家演示一個自定義 WebAuthenticationDetails 的例子。

上篇文章我們是在 MyAuthenticationProvider 類中進行驗證碼判斷的,回顧一下上篇文章的代碼:

  1. public class MyAuthenticationProvider extends DaoAuthenticationProvider { 
  2.  
  3.     @Override 
  4.     protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { 
  5.         HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); 
  6.         String code = req.getParameter("code"); 
  7.         String verify_code = (String) req.getSession().getAttribute("verify_code"); 
  8.         if (code == null || verify_code == null || !code.equals(verify_code)) { 
  9.             throw new AuthenticationServiceException("驗證碼錯誤"); 
  10.         } 
  11.         super.additionalAuthenticationChecks(userDetails, authentication); 
  12.     } 

不過這個驗證操作,我們也可以放在自定義的 WebAuthenticationDetails 中來做,我們定義如下兩個類:

  1. public class MyWebAuthenticationDetails extends WebAuthenticationDetails { 
  2.  
  3.     private boolean isPassed; 
  4.  
  5.     public MyWebAuthenticationDetails(HttpServletRequest req) { 
  6.         super(req); 
  7.         String code = req.getParameter("code"); 
  8.         String verify_code = (String) req.getSession().getAttribute("verify_code"); 
  9.         if (code != null && verify_code != null && code.equals(verify_code)) { 
  10.             isPassed = true
  11.         } 
  12.     } 
  13.  
  14.     public boolean isPassed() { 
  15.         return isPassed; 
  16.     } 
  17. @Component 
  18. public class MyWebAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest,MyWebAuthenticationDetails> { 
  19.     @Override 
  20.     public MyWebAuthenticationDetails buildDetails(HttpServletRequest context) { 
  21.         return new MyWebAuthenticationDetails(context); 
  22.     } 

首先我們定義 MyWebAuthenticationDetails,由于它的構造方法中,剛好就提供了 HttpServletRequest 對象,所以我們可以直接利用該對象進行驗證碼判斷,并將判斷結果交給 isPassed 變量保存。如果我們想擴展屬性,只需要在 MyWebAuthenticationDetails 中再去定義更多屬性,然后從 HttpServletRequest 中提取出來設置給對應的屬性即可,這樣,在登錄成功后就可以隨時隨地獲取這些屬性了。

最后在 MyWebAuthenticationDetailsSource 中構造 MyWebAuthenticationDetails 并返回。

定義完成后,接下來,我們就可以直接在 MyAuthenticationProvider 中進行調用了:

  1. public class MyAuthenticationProvider extends DaoAuthenticationProvider { 
  2.  
  3.     @Override 
  4.     protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { 
  5.         if (!((MyWebAuthenticationDetails) authentication.getDetails()).isPassed()) { 
  6.             throw new AuthenticationServiceException("驗證碼錯誤"); 
  7.         } 
  8.         super.additionalAuthenticationChecks(userDetails, authentication); 
  9.     } 

直接從 authentication 中獲取到 details 并調用 isPassed 方法,有問題就拋出異常即可。

最后的問題就是如何用自定義的 MyWebAuthenticationDetailsSource 代替系統(tǒng)默認的 WebAuthenticationDetailsSource,很簡單,我們只需要在 SecurityConfig 中稍作定義即可:

  1. @Autowired 
  2. MyWebAuthenticationDetailsSource myWebAuthenticationDetailsSource; 
  3. @Override 
  4. protected void configure(HttpSecurity http) throws Exception { 
  5.     http.authorizeRequests() 
  6.             ... 
  7.             .and() 
  8.             .formLogin() 
  9.             .authenticationDetailsSource(myWebAuthenticationDetailsSource) 
  10.             ... 

將 MyWebAuthenticationDetailsSource 注入到 SecurityConfig 中,并在 formLogin 中配置 authenticationDetailsSource 即可成功使用我們自定義的 WebAuthenticationDetails。

這樣自定義完成后,WebAuthenticationDetails 中原有的功能依然保留,也就是我們還可以利用老辦法繼續(xù)獲取用戶 IP 以及 sessionId 等信息,如下:

  1. @Service 
  2. public class HelloService { 
  3.     public void hello() { 
  4.         Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 
  5.         MyWebAuthenticationDetails details = (MyWebAuthenticationDetails) authentication.getDetails(); 
  6.         System.out.println(details); 
  7.     } 

這里類型強轉的時候,轉為 MyWebAuthenticationDetails 即可。

本文案例大家可以從 GitHub 上下載:https://github.com/lenve/spring-security-samples

本文轉載自微信公眾號「江南一點雨」,可以通過以下二維碼關注。轉載本文請聯(lián)系江南一點雨公眾號。

 

責任編輯:武曉燕 來源: 江南一點雨
相關推薦

2021-07-02 10:45:53

SpringBootCAS登錄

2021-07-06 11:42:05

數(shù)據(jù)庫SpringSecurCAS

2021-04-21 10:38:44

Spring Boot RememberMe安全

2021-05-12 10:39:51

SpringSecurity設備

2020-04-29 15:10:16

Linux命令進程

2009-03-10 18:10:12

LinuxUbuntu技巧

2021-05-20 10:47:24

防火墻SpringSecur

2021-02-07 09:22:42

Zabbix5.2拓撲圖運維

2011-04-29 10:58:11

SimpleFrame

2021-12-06 09:44:30

鴻蒙HarmonyOS應用

2023-01-06 08:18:44

2023-03-03 08:18:41

2021-07-07 21:40:46

Rust函數(shù)勸退

2021-06-09 08:53:34

設計模式策略模式工廠模式

2011-04-29 09:33:22

SimpleFrame

2012-11-14 11:09:14

OSSECactive-resp

2012-11-14 11:03:14

OSSEC文件檢查SYSCHECK

2017-06-15 13:29:12

AkkaSpark異步

2020-05-27 08:05:33

MybatisMapper接口

2012-08-22 10:18:03

PHP
點贊
收藏

51CTO技術棧公眾號