Spring Security 中如何快速查看登录用户 IP 地址等信息?

  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 方法:

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”);

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
img

最后

ActiveMQ消息中间件面试专题

  • 什么是ActiveMQ?
  • ActiveMQ服务器宕机怎么办?
  • 丢消息怎么办?
  • 持久化消息非常慢怎么办?
  • 消息的不均匀消费怎么办?
  • 死信队列怎么办?
  • ActiveMQ中的消息重发时间间隔和重发次数吗?

ActiveMQ消息中间件面试专题解析拓展:

BAT面试文档:ActiveMQ+redis+Spring+高并发多线程+JVM


redis面试专题及答案

  • 支持一致性哈希的客户端有哪些?
  • Redis与其他key-value存储有什么不同?
  • Redis的内存占用情况怎么样?
  • 都有哪些办法可以降低Redis的内存使用情况呢?
  • 查看Redis使用情况及状态信息用什么命令?
  • Redis的内存用完了会发生什么?
  • Redis是单线程的,如何提高多核CPU的利用率?

BAT面试文档:ActiveMQ+redis+Spring+高并发多线程+JVM


Spring面试专题及答案

  • 谈谈你对 Spring 的理解
  • Spring 有哪些优点?
  • Spring 中的设计模式
  • 怎样开启注解装配以及常用注解
  • 简单介绍下 Spring bean 的生命周期

Spring面试答案解析拓展

BAT面试文档:ActiveMQ+redis+Spring+高并发多线程+JVM


高并发多线程面试专题

  • 现在有线程 T1、T2 和 T3。你如何确保 T2 线程在 T1 之后执行,并且 T3 线程在 T2 之后执行?
  • Java 中新的 Lock 接口相对于同步代码块(synchronized block)有什么优势?如果让你实现一个高性能缓存,支持并发读取和单一写入,你如何保证数据完整性。
  • Java 中 wait 和 sleep 方法有什么区别?
  • 如何在 Java 中实现一个阻塞队列?
  • 如何在 Java 中编写代码解决生产者消费者问题?
  • 写一段死锁代码。你在 Java 中如何解决死锁?

高并发多线程面试解析与拓展

BAT面试文档:ActiveMQ+redis+Spring+高并发多线程+JVM


jvm面试专题与解析

  • JVM 由哪些部分组成?
  • JVM 内存划分?
  • Java 的内存模型?
  • 引用的分类?
  • GC什么时候开始?

JVM面试专题解析与拓展!

BAT面试文档:ActiveMQ+redis+Spring+高并发多线程+JVM

么区别?

  • 如何在 Java 中实现一个阻塞队列?
  • 如何在 Java 中编写代码解决生产者消费者问题?
  • 写一段死锁代码。你在 Java 中如何解决死锁?

高并发多线程面试解析与拓展

[外链图片转存中…(img-rsnDumWs-1711082231625)]


jvm面试专题与解析

  • JVM 由哪些部分组成?
  • JVM 内存划分?
  • Java 的内存模型?
  • 引用的分类?
  • GC什么时候开始?

JVM面试专题解析与拓展!

[外链图片转存中…(img-HvVGkW25-1711082231625)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 24
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值