SpringSecurity系列之查看登录详情

架构 2023-07-05 17:29:38
34阅读

 

上一篇文章跟大伙儿聊了怎么使用更为雅致的方法自定 Spring Security 登陆逻辑性,更为雅致的方法能够 合理防止掉自定过滤装置产生的低效能,提议大伙儿一定阅读文章一下,还可以顺带了解 Spring Security 中的验证逻辑性。

文中将在上文的基本上,再次和大伙儿讨论怎样储存登陆客户详细资料的难题。

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 方式 中,对要求主要参数做获取,在 attemptAuthentication 方式 中,会启用到一个方式 ,便是 setDetails。

大家一起来看下 setDetails 方式 :

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

UsernamePasswordAuthenticationToken 是 Authentication 的实际完成,因此 这儿事实上便是在设定 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 2个信息内容,如果我们想储存有关 Http 要求的其他信息,就可以根据自定 WebAuthenticationDetails 来完成。

假如我们要订制 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 中做,大家界定以下2个类:

 
  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 替代系统软件默认设置的 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 中华有的作用仍然保存,也就是大家还能够运用老办法再次获得客户 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

the end
免责声明:本文不代表本站的观点和立场,如有侵权请联系本站删除!本站仅提供信息存储空间服务。