Spring Boot+Spring Security 实现自动登录功能(实战+源码分析)(1)

总结

至此,文章终于到了尾声。总结一下,我们谈论了简历制作过程中需要注意的以下三个部分,并分别给出了一些建议:

  1. 技术能力:先写岗位所需能力,再写加分能力,不要写无关能力;
  2. 项目经历:只写明星项目,描述遵循 STAR 法则;
  3. 简历印象:简历遵循三大原则:清晰,简短,必要,要有的放矢,不要海投;

以及最后为大家准备的福利时间:简历模板+Java面试题+热门技术系列教程视频

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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

需要这份系统化的资料的朋友,可以点击这里获取

@Override

protected void configure(HttpSecurity http) throws Exception {

http.authorizeRequests()

.anyRequest().authenticated()

.and()

.formLogin()

.and()

.rememberMe()

.and()

.csrf().disable();

}

大家看到,这里只需要添加一个 .rememberMe() 即可,自动登录功能就成功添加进来了。

接下来我们随意添加一个测试接口:

@RestController

public class HelloController {

@GetMapping(“/hello”)

public String hello() {

return “hello”;

}

}

重启项目,我们访问 hello 接口,此时会自动跳转到登录页面:

image

这个时候大家发现,默认的登录页面多了一个选项,就是记住我。我们输入用户名密码,并且勾选上记住我这个框,然后点击登录按钮执行登录操作:

image

可以看到,登录数据中,除了 username 和 password 之外,还有一个 remember-me,之所以给大家看这个,是想告诉大家,如果你你需要自定义登录页面,RememberMe 这个选项的 key 该怎么写。

登录成功之后,就会自动跳转到 hello 接口了。我们注意,系统访问 hello 接口的时候,携带的 cookie:

image

大家注意到,这里多了一个 remember-me,这就是这里实现的核心,关于这个 remember-me 我一会解释,我们先来测试效果。

接下来,我们关闭浏览器,再重新打开浏览器。正常情况下,浏览器关闭再重新打开,如果需要再次访问 hello 接口,就需要我们重新登录了。但是此时,我们再去访问 hello 接口,发现不用重新登录了,直接就能访问到,这就说明我们的 RememberMe 配置生效了(即下次自动登录功能生效了)。

2.原理分析

=========================================================================

按理说,浏览器关闭再重新打开,就要重新登录,现在竟然不用等了,那么这个功能到底是怎么实现的呢?

首先我们来分析一下 cookie 中多出来的这个 remember-me,这个值一看就是一个 Base64 转码后的字符串,我们可以使用网上的一些在线工具来解码,可以自己简单写两行代码来解码:

@Test

void contextLoads() throws UnsupportedEncodingException {

String s = new String(Base64.getDecoder().decode(“amF2YWJveToxNTg5MTA0MDU1MzczOjI1NzhmZmJjMjY0ODVjNTM0YTJlZjkyOWFjMmVmYzQ3”), “UTF-8”);

System.out.println("s = " + s);

}

执行这段代码,输出结果如下:

s = javaboy:1589104055373:2578ffbc26485c534a2ef929ac2efc47

可以看到,这段 Base64 字符串实际上用 : 隔开,分成了三部分:

  1. 第一段是用户名,这个无需质疑。

  2. 第二段看起来是一个时间戳,我们通过在线工具或者 Java 代码解析后发现,这是一个两周后的数据。

  3. 第三段我就不卖关子了,这是使用 MD5 散列函数算出来的值,他的明文格式是 username + “:” + tokenExpiryTime + “:” + password + “:” + key,最后的 key 是一个散列盐值,可以用来防止令牌被修改。

了解到 cookie 中 remember-me 的含义之后,那么我们对于记住我的登录流程也就很容易猜到了了。

在浏览器关闭后,并重新打开之后,用户再去访问 hello 接口,此时会携带着 cookie 中的 remember-me 到服务端,服务到拿到值之后,可以方便的计算出用户名和过期时间,再根据用户名查询到用户密码,然后通过 MD5 散列函数计算出散列值,再将计算出的散列值和浏览器传递来的散列值进行对比,就能确认这个令牌是否有效。

流程就是这么个流程,接下来我们通过分析源码来验证一下这个流程对不对。

3.源码分析

=========================================================================

接下来,我们通过源码来验证一下我们上面说的对不对。

这里主要从两个方面来介绍,一个是 remember-me 这个令牌生成的过程,另一个则是它解析的过程。

3.1 生成

=========================================================================

生成的核心处理方法在:TokenBasedRememberMeServices#onLoginSuccess:

@Override

public void onLoginSuccess(HttpServletRequest request, HttpServletResponse response,

Authentication successfulAuthentication) {

String username = retrieveUserName(successfulAuthentication);

String password = retrievePassword(successfulAuthentication);

if (!StringUtils.hasLength(password)) {

UserDetails user = getUserDetailsService().loadUserByUsername(username);

password = user.getPassword();

}

int tokenLifetime = calculateLoginLifetime(request, successfulAuthentication);

long expiryTime = System.currentTimeMillis();

expiryTime += 1000L * (tokenLifetime < 0 ? TWO_WEEKS_S : tokenLifetime);

String signatureValue = makeTokenSignature(expiryTime, username, password);

setCookie(new String[] { username, Long.toString(expiryTime), signatureValue },

tokenLifetime, request, response);

}

protected String makeTokenSignature(long tokenExpiryTime, String username,

String password) {

String data = username + “:” + tokenExpiryTime + “:” + password + “:” + getKey();

MessageDigest digest;

digest = MessageDigest.getInstance(“MD5”);

return new String(Hex.encode(digest.digest(data.getBytes())));

}

这段方法的逻辑其实很好理解:

  1. 首先从登录成功的 Authentication 中提取出用户名/密码。

  2. 由于登录成功之后,密码可能被擦除了,所以,如果一开始没有拿到密码,就再从 UserDetailsService 中重新加载用户并重新获取密码。

  3. 再接下来去获取令牌的有效期,令牌有效期默认就是两周。

  4. 再接下来调用 makeTokenSignature 方法去计算散列值,实际上就是根据 username、令牌有效期以及 password、key 一起计算一个散列值。如果我们没有自己去设置这个 key,默认是在 RememberMeConfigurer#getKey 方法中进行设置的,它的值是一个 UUID 字符串。

  5. 最后,将用户名、令牌有效期以及计算得到的散列值放入 Cookie 中。

关于第四点,我这里再说一下。

由于我们自己没有设置 key,key 默认值是一个 UUID 字符串,这样会带来一个问题,就是如果服务端重启,这个 key 会变,这样就导致之前派发出去的所有 remember-me 自动登录令牌失效,所以,我们可以指定这个 key。指定方式如下:

@Override

protected void configure(HttpSecurity http) throws Exception {

http.authorizeRequests()

.anyRequest().authenticated()

.and()

.formLogin()

.and()

.rememberMe()

.key(“javaboy”)

.and()

.csrf().disable();

}

如果自己配置了 key,「即使服务端重启,即使浏览器打开再关闭」,也依然能够访问到 hello 接口。

这是 remember-me 令牌生成的过程。至于是如何走到 onLoginSuccess 方法的,大家可以参考松哥之前的文章:松哥手把手带你捋一遍 Spring Security 登录流程。这里可以给大家稍微提醒一下思路:

AbstractAuthenticationProcessingFilter#doFilter -> AbstractAuthenticationProcessingFilter#successfulAuthentication -> AbstractRememberMeServices#loginSuccess -> TokenBasedRememberMeServices#onLoginSuccess。

3.2 解析

复习的面试资料

这些面试全部出自大厂面试真题和面试合集当中,小编已经为大家整理完毕(PDF版)

  • 第一部分:Java基础-中级-高级

image

  • 第二部分:开源框架(SSM:Spring+SpringMVC+MyBatis)

image

  • 第三部分:性能调优(JVM+MySQL+Tomcat)

image

  • 第四部分:分布式(限流:ZK+Nginx;缓存:Redis+MongoDB+Memcached;通讯:MQ+kafka)

image

  • 第五部分:微服务(SpringBoot+SpringCloud+Dubbo)

image

  • 第六部分:其他:并发编程+设计模式+数据结构与算法+网络

image

进阶学习笔记pdf

  • Java架构进阶之架构筑基篇(Java基础+并发编程+JVM+MySQL+Tomcat+网络+数据结构与算法

image

  • Java架构进阶之开源框架篇(设计模式+Spring+SpringMVC+MyBatis

image

image

image

  • Java架构进阶之分布式架构篇 (限流(ZK/Nginx)+缓存(Redis/MongoDB/Memcached)+通讯(MQ/kafka)

image

image

image

  • Java架构进阶之微服务架构篇(RPC+SpringBoot+SpringCloud+Dubbo+K8s)

image

image

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

需要这份系统化的资料的朋友,可以点击这里获取

X-1715464648130)]

  • Java架构进阶之微服务架构篇(RPC+SpringBoot+SpringCloud+Dubbo+K8s)

[外链图片转存中…(img-vaYU6BPP-1715464648130)]

[外链图片转存中…(img-xJn6aizi-1715464648130)]

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

需要这份系统化的资料的朋友,可以点击这里获取

  • 23
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值