日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
SpringSecurity系列之查看登錄詳情

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

網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、成都小程序開發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了平山免費(fèi)建站歡迎大家使用!

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

1.Authentication

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

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

我們來看下 Authentication 的一個(gè)定義:

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

接口的解釋如下:

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

這里有一個(gè)比較好玩的方法,叫做 getDetails。關(guān)于這個(gè)方法,源碼的解釋如下:

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

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

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

2.源碼分析

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

我們一起來看下 setDetails 方法:

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

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

 
 
 
 
  1. public class WebAuthenticationDetailsSource implements 
  2.   AuthenticationDetailsSource { 
  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.     //省略其他方法 

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

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

我舉一個(gè)簡單例子,例如我們登錄成功后,可以通過如下方式隨時(shí)隨地拿到用戶 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.     } 

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

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

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

3.定制

當(dāng)然,WebAuthenticationDetails 也可以自己定制,因?yàn)槟J(rèn)它只提供了 IP 和 sessionid 兩個(gè)信息,如果我們想保存關(guān)于 Http 請求的更多信息,就可以通過自定義 WebAuthenticationDetails 來實(shí)現(xiàn)。

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

結(jié)合上篇文章的驗(yàn)證碼登錄,我跟大家演示一個(gè)自定義 WebAuthenticationDetails 的例子。

上篇文章我們是在 MyAuthenticationProvider 類中進(jìn)行驗(yàn)證碼判斷的,回顧一下上篇文章的代碼:

 
 
 
 
  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("驗(yàn)證碼錯(cuò)誤"); 
  10.         } 
  11.         super.additionalAuthenticationChecks(userDetails, authentication); 
  12.     } 

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

 
 
 
 
  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 { 
  19.     @Override 
  20.     public MyWebAuthenticationDetails buildDetails(HttpServletRequest context) { 
  21.         return new MyWebAuthenticationDetails(context); 
  22.     } 

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

最后在 MyWebAuthenticationDetailsSource 中構(gòu)造 MyWebAuthenticationDetails 并返回。

定義完成后,接下來,我們就可以直接在 MyAuthenticationProvider 中進(jìn)行調(diào)用了:

 
 
 
 
  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("驗(yàn)證碼錯(cuò)誤"); 
  7.         } 
  8.         super.additionalAuthenticationChecks(userDetails, authentication); 
  9.     } 

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

最后的問題就是如何用自定義的 MyWebAuthenticationDetailsSource 代替系統(tǒng)默認(rèn)的 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.     } 

這里類型強(qiáng)轉(zhuǎn)的時(shí)候,轉(zhuǎn)為 MyWebAuthenticationDetails 即可。

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

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


當(dāng)前題目:SpringSecurity系列之查看登錄詳情
標(biāo)題URL:http://www.dlmjj.cn/article/cciiehp.html