Spring Security 中如何快速查看登陆 IP地址等信息?一招搞定

以前跟你们聊了如何使用更加优雅的方式自定义 Spring Security 登陆逻辑,更加优雅的方式能够有效避免掉自定义过滤器带来的低效,建议你们必定阅读一下,也能够顺便理解 Spring Security 中的认证逻辑。

不废话了,咱们来看今天的文章。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

  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.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架构项目教程,还能够跟老司机交流讨教! 本文的文字及图片来源于网络加上本身的想法,仅供学习、交流使用,不具备任何商业用途,版权归原做者全部,若有问题请及时联系咱们以做处理

相关文章
相关标签/搜索