不廢話了,咱們來看今天的文章。java
1.Authentication
Authentication 這個接口前面和你們聊過屢次,今天還要再來聊一聊。另外你們要注意:突破高薪Java架構項目經驗永遠是核心,若是你沒有最新JAVA架構實戰教程及大廠30k+面試寶典,能夠去小編的Java架構學習.裙 :七吧傘吧零而衣零傘 (數字的諧音)轉換下能夠找到了,裏面不少新JAVA架構項目教程,還能夠跟老司機交流討教! 面試
Authentication 接口用來保存咱們的登陸用戶信息,實際上,它是對主體(java.security.Principal)作了進一步的封裝。bash
咱們來看下 Authentication 的一個定義:網絡
public interface Authentication extends Principal, Serializable { Collection<? extends GrantedAuthority> getAuthorities(); Object getCredentials(); Object getDetails(); Object getPrincipal(); boolean isAuthenticated(); void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException; } 複製代碼
接口的解釋以下:session
- getAuthorities 方法用來獲取用戶的權限。
- getCredentials 方法用來獲取用戶憑證,通常來講就是密碼。
- getDetails 方法用來獲取用戶攜帶的詳細信息,多是當前請求之類的東西。
- getPrincipal 方法用來獲取當前用戶,多是一個用戶名,也多是一個用戶對象。
- isAuthenticated 當前用戶是否定證成功。
這裏有一個比較好玩的方法,叫作 getDetails。關於這個方法,源碼的解釋以下:架構
Stores additional details about the authentication request. These might be an IP address, certificate serial number etc.ide
從這段解釋中,咱們能夠看出,該方法實際上就是用來存儲有關身份認證的其餘信息的,例如 IP 地址、證書信息等等。源碼分析
實際上,在默認狀況下,這裏存儲的就是用戶登陸的 IP 地址和 sessionId。咱們從源碼角度來看下。學習
2.源碼分析
鬆哥的 SpringSecurity 系列已經寫到第 12 篇了,看了前面的文章,相信你們已經明白用戶登陸必經的一個過濾器就是 UsernamePasswordAuthenticationFilter,在該類的 attemptAuthentication 方法中,對請求參數作提取,在 attemptAuthentication 方法中,會調用到一個方法,就是 setDetails。ui
咱們一塊兒來看下 setDetails 方法:
protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) { authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); } 複製代碼
UsernamePasswordAuthenticationToken 是 Authentication 的具體實現,因此這裏實際上就是在設置 details,至於 details 的值,則是經過 authenticationDetailsSource 來構建的,咱們來看下:
public class WebAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> { public WebAuthenticationDetails buildDetails(HttpServletRequest context) { return new WebAuthenticationDetails(context); } } public class WebAuthenticationDetails implements Serializable { private final String remoteAddress; private final String sessionId; public WebAuthenticationDetails(HttpServletRequest request) { this.remoteAddress = request.getRemoteAddr(); HttpSession session = request.getSession(false); this.sessionId = (session != null) ? session.getId() : null; } //省略其餘方法 } 複製代碼
默認經過 WebAuthenticationDetailsSource 來構建 WebAuthenticationDetails,並將結果設置到 Authentication 的 details 屬性中去。而 WebAuthenticationDetails 中定義的屬性,你們看一下基本上就明白,這就是保存了用戶登陸地址和 sessionId。
那麼看到這裏,你們基本上就明白了,用戶登陸的 IP 地址實際上咱們能夠直接從 WebAuthenticationDetails 中獲取到。
我舉一個簡單例子,例如咱們登陸成功後,能夠經過以下方式隨時隨地拿到用戶 IP:
@Service public class HelloService { public void hello() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); WebAuthenticationDetails details = (WebAuthenticationDetails) authentication.getDetails(); System.out.println(details); } } 複製代碼
這個獲取過程之因此放在 service 來作,就是爲了演示隨時隨地這個特性。而後咱們在 controller 中調用該方法,當訪問接口時,能夠看到以下日誌:
WebAuthenticationDetails@fffc7f0c: RemoteIpAddress: 127.0.0.1; SessionId: 303C7F254DF8B86667A2B20AA0667160
複製代碼
能夠看到,用戶的 IP 地址和 SessionId 都給出來了。這兩個屬性在 WebAuthenticationDetails 中都有對應的 get 方法,也能夠單獨獲取屬性值。
3.定製
固然,WebAuthenticationDetails 也能夠本身定製,由於默認它只提供了 IP 和 sessionid 兩個信息,若是咱們想保存關於 Http 請求的更多信息,就能夠經過自定義 WebAuthenticationDetails 來實現。
若是咱們要定製 WebAuthenticationDetails,還要連同 WebAuthenticationDetailsSource 一塊兒從新定義。
結合上篇文章的驗證碼登陸,我跟你們演示一個自定義 WebAuthenticationDetails 的例子。
上篇文章咱們是在 MyAuthenticationProvider 類中進行驗證碼判斷的,回顧一下上篇文章的代碼:
public class MyAuthenticationProvider extends DaoAuthenticationProvider { @Override protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String code = req.getParameter("code"); String verify_code = (String) req.getSession().getAttribute("verify_code"); if (code == null || verify_code == null || !code.equals(verify_code)) { throw new AuthenticationServiceException("驗證碼錯誤"); } super.additionalAuthenticationChecks(userDetails, authentication); } } 複製代碼
不過這個驗證操做,咱們也能夠放在自定義的 WebAuthenticationDetails 中來作,咱們定義以下兩個類:
public class MyWebAuthenticationDetails extends WebAuthenticationDetails { private boolean isPassed; public MyWebAuthenticationDetails(HttpServletRequest req) { super(req); String code = req.getParameter("code"); String verify_code = (String) req.getSession().getAttribute("verify_code"); if (code != null && verify_code != null && code.equals(verify_code)) { isPassed = true; } } public boolean isPassed() { return isPassed; } } @Component public class MyWebAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest,MyWebAuthenticationDetails> { @Override public MyWebAuthenticationDetails buildDetails(HttpServletRequest context) { return new MyWebAuthenticationDetails(context); } } 複製代碼
首先咱們定義 MyWebAuthenticationDetails,因爲它的構造方法中,恰好就提供了 HttpServletRequest 對象,因此咱們能夠直接利用該對象進行驗證碼判斷,並將判斷結果交給 isPassed 變量保存。若是咱們想擴展屬性,只須要在 MyWebAuthenticationDetails 中再去定義更多屬性,而後從 HttpServletRequest 中提取出來設置給對應的屬性便可,這樣,在登陸成功後就能夠隨時隨地獲取這些屬性了。
最後在 MyWebAuthenticationDetailsSource 中構造 MyWebAuthenticationDetails 並返回。
定義完成後,接下來,咱們就能夠直接在 MyAuthenticationProvider 中進行調用了:
public class MyAuthenticationProvider extends DaoAuthenticationProvider { @Override protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { if (!((MyWebAuthenticationDetails) authentication.getDetails()).isPassed()) { throw new AuthenticationServiceException("驗證碼錯誤"); } super.additionalAuthenticationChecks(userDetails, authentication); } } 複製代碼
直接從 authentication 中獲取到 details 並調用 isPassed 方法,有問題就拋出異常便可。
最後的問題就是如何用自定義的 MyWebAuthenticationDetailsSource 代替系統默認的 WebAuthenticationDetailsSource,很簡單,咱們只須要在 SecurityConfig 中稍做定義便可:
@Autowired MyWebAuthenticationDetailsSource myWebAuthenticationDetailsSource; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() ... .and() .formLogin() .authenticationDetailsSource(myWebAuthenticationDetailsSource) ... } 複製代碼
將 MyWebAuthenticationDetailsSource 注入到 SecurityConfig 中,並在 formLogin 中配置 authenticationDetailsSource 便可成功使用咱們自定義的 WebAuthenticationDetails。
這樣自定義完成後,WebAuthenticationDetails 中原有的功能依然保留,也就是咱們還能夠利用老辦法繼續獲取用戶 IP 以及 sessionId 等信息,以下:
@Service public class HelloService { public void hello() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); MyWebAuthenticationDetails details = (MyWebAuthenticationDetails) authentication.getDetails(); System.out.println(details); } } 複製代碼
這裏類型強轉的時候,轉爲 MyWebAuthenticationDetails 便可。
最後注意:突破高薪Java架構項目經驗永遠是核心,若是你沒有最新JAVA架構實戰教程及大廠30k+面試寶典,能夠去小編的Java架構學習.裙 :七吧傘吧零而衣零傘 (數字的諧音)轉換下能夠找到了,裏面不少新JAVA架構項目教程,還能夠跟老司機交流討教! 本文的文字及圖片來源於網絡加上本身的想法,僅供學習、交流使用,不具備任何商業用途,版權歸原做者全部,若有問題請及時聯繫咱們以做處理