Spring Security 中的 RememberMe 登录,so easy!

news2025/7/7 23:34:06

1. RememberMe简介

RememberMe 这个功能非常常见,图 6-1 所示就是 QQ 邮箱登录时的“记住我”选项。

提到 RememberMe,一些初学者往往会有一些误解,认为 RememberMe 功能就是把用户名/密码用 Cookie 保存在浏览器中,下次登录时不用再次输入用户名/密码。这个理解显然是不对的。

我们这里所说的 RememberMe 是一种服务器端的行为。传统的登录方式基于  Session 会话,一旦用户关闭浏览器重新打开,就要再次登录,这样太过于烦琐。如果能有一种机制,让用户关闭并重新打开浏览器之后,还能继续保持认证状态,就会方便很多,RememberMe 就是为了解决这一需求而生的。

具体的实现思路就是通过 Cookie 来记录当前用户身份。当用户登录成功之后,会通过一定的算法,将用户信息、时间戳等进行加密,加密完成后,通过响应头带回前端存储在 Cookie 中,当浏览器关闭之后重新打开,如果再次访问该网站,会自动将 Cookie 中的信息发送给服务器,服务器对 Cookie 中的信息进行校验分析,进而确定出用户的身份,Cookie 中所保存的用户信息也是有时效的,例如三天、一周等。敏锐的读者可能已经发现这种方式是存在安全隐患的。所谓鱼与熊掌不可兼得,要想使用便利,就要牺牲一定的安全性,不过在本章中,我们将会介绍通过持久化令牌以及二次校验来降低使用 RememberMe 所带来的安全风险。

2. RememberMe基本用法

我们先来看一种最简单的用法。

首先创建一个 Spring Boot 工程,引入 spring-boot-starter-security 依赖。工程创建成功后,添加一个 HelloController 并创建一个测试接口,代码如下:

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }
}

然后创建 SecurityConfig 配置文件:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) 
                                                                 throws Exception {
        auth.inMemoryAuthentication()
                .withUser("javaboy")
                .password("123")
                .roles("admin");
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .and()
                .rememberMe()
                .key("javaboy")
                .and()
                .csrf().disable();
    }
}

这里我们主要是调用了 HttpSecurity 中的 rememberMe 方法并配置了一个 key,该方法最终会向过滤器链中添加 RememberMeAuthenticationFilter 过滤器。

配置完成后,启动项目,当我们访问 /hello 接口时,会自动重定向到登录页面,如图 6-2 所示。

可以看到,此时的默认登录页面多了一个 RememberMe 选项,勾选上 RememberMe,登录成功之后,我们就可以访问 /hello 接口了。访问完成后,关闭浏览器再重新打开,此时不需要登录就可以直接访问 /hello 接口;同时,如果关闭掉服务端重新打开,再去访问 /hello接口,发现此时也不需要登录了。

那么这一切是怎么实现的呢?打开浏览器控制台,我们来分析整个登录过程。

首先,当我们单击登录按钮时,多了一个请求参数 remember-me,如图6-3所示。

很明显,remember-me 参数就是用来告诉服务端是否开启 RememberMe 功能,如果开发者自定义登录页面,那么默认情况下,是否开启 RememberMe 的参数就是 remember-me。

当请求成功后,在响应头中多出了一个 Set-Cookie,如图 6-4 所示。

在响应头中给出了一个 remember-me 字符串。以后所有请求的请求头 Cookie 字段,都会自动携带上这个令牌,服务端利用该令牌可以校验用户身份是否合法。

大致的流程就是这样,但是大家发现这种方式安全隐患很大,一旦 remember-me 令牌泄漏,恶意用户就可以拿着这个令牌去随意访问系统资源。持久化令牌和二次校验可以在一定程度上降低该问题带来的风险。

3. 持久化令牌

使用持久化令牌实现 RememberMe 的体验和使用普通令牌的登录体验是一样的,不同的是服务端所做的事情变了。

持久化令牌在普通令牌的基础上,新增了 series 和 token 两个校验参数,当使用用户名/密码的方式登录时,series 才会自动更新;而一旦有了新的会话,token 就会重新生成。所以,如果令牌被人盗用,一旦对方基于 RememberMe 登录成功后,就会生成新的 token,你自己的登录令牌就会失效,这样就能及时发现账户泄漏并作出处理,比如清除自动登录令牌、通知用户账户泄漏等。

Spring Security中对于持久化令牌提供了两种实现:

  • JdbcTokenRepositoryImpl

  • InMemoryTokenRepositoryImpl

前者是基于 JdbcTemplate 来操作数据库,后者则是操作存储在内存中的数据。由于 InMemoryTokenRepositoryImpl 的使用场景很少,因此这里主要介绍基于 JdbcTokenRepositoryImpl 的配置。

首先创建一个 security06 数据库,然后我们需要一张表来记录令牌信息,创建表的 SQL 脚本在在 JdbcTokenRepositoryImpl 类中的 CREATE_TABLE_SQL 变量上已经定义好了,代码如下:

public static final String CREATE_TABLE_SQL = "create table persistent_logins 
(username varchar(64) not null, series varchar(64) primary key, " 
+ "token varchar(64) not null, last_used timestamp not null)";

我们直接将变量中定义的 SQL 脚本拷贝出来到数据库中执行,生成一张 persistent_logins 表用来记录令牌信息。persistent_logins 表一共就四个字段:username 表示登录用户名、series 表示生成的 series 字符串、token 表示生成的 token 字符串、last_used 则表示上次使用时间。

接下来,在项目中引入 JdbcTemplate 依赖和 MySQL 数据库驱动依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

然后在 application.properties 中配置数据库连接信息:

spring.datasource.url=jdbc:mysql:///security06?useUnicode=true&characterEncod
ing=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123

最后修改 SecurityConfig:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    DataSource dataSource;
    @Bean
    JdbcTokenRepositoryImpl jdbcTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = 
                                                   new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        return jdbcTokenRepository;
    }
    @Bean
    PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) 
                                                                  throws Exception {
        auth.inMemoryAuthentication()
                .withUser("javaboy")
                .password("123")
                .roles("admin");
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .and()
                .rememberMe()
                .tokenRepository(jdbcTokenRepository())
                .and()
                .csrf().disable();
    }
}

在配置中我们提供了一个 JdbcTokenRepositoryImpl 实例,并为其配置了数据源,最后在配置 RememberMe 时通过 tokenRepository 方法指定 JdbcTokenRepositoryImpl 实例。

配置完成后,启动项目并进行登录测试。登录成功后,我们发现数据库表中多了一条记录,如图6-5所示。

此时如果关闭浏览器重新打开,再去访问 /hello 接口,访问时并不需要登录,但是访问成功之后,数据库中的 token 字段会发生变化。同时,如果服务端重启之后,浏览器再去访问 /hello 接口,依然不需要登录,但是 token 字段也会更新,因为这两种情况中都有新会话的建立,所以 token 会更新,而 series 则不会更新。当然,如果用户注销登录,则数据库中和该用户相关的登录记录会自动清除。

可以看到,持久化令牌比前面的普通令牌安全系数提高了不少,但是依然存在风险。安全问题和用户的使用便捷性就像一个悖论,想要用户使用方便,不可避免地要牺牲一点安全性。对于开发者而言,要做的就是如何将系统存在的安全风险降到最低。

那么怎么办呢?二次校验可以帮助我们进一步降低风险。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/37040.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

11.25总结

目录 一.做题总结 1.约瑟夫问题 2.最近请求次数 4.选择题订正 二.最长字符串 三.最长数组问题 一.做题总结 1.约瑟夫问题 我的思路就是先遍历按队员编号添加按顺序初始化每个人的编号. 就建立循环,终止条件就是大小为1 进入每次叫号的循环.让编号不为1的全部移出去 因…

尚硅谷axios笔记——入门学习

这篇笔记不包括源码分析&#xff0c;呜呜感觉时间不够用了还得留时间准备教资面试 axios理解和使用 json-server服务与搭建 终端安装npm i -g json-server 启动服务 json-server --watch db.json 启动在当前文档前 数据去db.json找 查看id为2的页面在http://localhost:3000.…

Linux下调试方法汇总

一、Linux驱动调试方法 Linux驱动调试主要有以下几种方法&#xff1a; 1、利用printk。 2、查看OOP消息。 3、利用strace。 4、利用内核内置的hacking选项。 5、利用ioctl方法。 6、利用/proc 文件系统。 7、使用kgdb。 1.1、printk 这是驱动开发中最朴实无华&#xff0c;同时…

使用 Lambda 表达式的正确姿势,写得太好了叭

​ 编辑切换为居中 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; Lambda 表达式非常方便&#xff0c;在项目中一般在 stream 编程中用得比较多。 List<Student> studentList gen(); Map<String, Student> map studentList .stream() .…

Redis缓存的几个名词,缓存命中、缓存穿透、缓存雪崩、缓存击穿

本文为转载整理&#xff1a;原文地址为【https://zhuanlan.zhihu.com/p/422627839】 1. 缓存命中 缓存命中(cache hit)&#xff1a;当应用程序或软件请求数据时&#xff0c;会首先发生缓存命中。首先&#xff0c;中央处理单元&#xff08;CPU&#xff09;在其最近的内存位置&…

MySQL之BufferPool

文章目录前言一、BufferPool是什么二、BufferPool的组成2.1 结构图2.2 如何读取缓冲页三、BufferPool在MySQL中的作用前言 本文会讲述BufferPool是什么&#xff0c;BufferPool的组成&#xff0c;在mysql中的作用 一、BufferPool是什么 在mysql中是用来缓存磁盘中的页&#xff…

央企招聘:中储粮集团2023公开招聘公告(校招+社招,共700人)

中国储备粮管理集团有限公司2023年度招聘包括校园招聘和社会招聘&#xff0c;计划招募优秀人才700余人。 中国储备粮管理集团有限公司是经国务院批准组建的涉及国家安全和国民经济命脉的国有大型重要骨干企业&#xff0c;受国务院委托具体负责中央储备粮油棉的经营管理&#xf…

SSM毕设项目 - 基于SSM的房屋出租出售系统(含源码+论文)

文章目录1 项目简介2 实现效果2.1 界面展示3 设计方案3.1 概述3.2 系统流程3.2.1 系统开发流程3.2.2 操作流程3.3 系统结构设计4 项目获取1 项目简介 Hi&#xff0c;各位同学好呀&#xff0c;这里是M学姐&#xff01; 今天向大家分享一个今年(2022)最新完成的毕业设计项目作品…

【学习笔记31】JavaScript冒泡排序和选择排序

笔记首发 一、冒泡排序 &#xff08;一&#xff09;核心原理 循环遍历数组&#xff0c;当前单元和下一个单元进行数据比较按照从小到大排序&#xff0c;应该是当前单元小于下一个单元&#xff0c;如果当前单元大于下一个单元&#xff0c;将交换两个单元存储的数据一次循环结束…

44、Map

一、基本介绍&#xff1a; 1、Map接口实现类的特点&#xff3b;很实用&#xff3d; 注意&#xff1a;这里讲的是JDK8的Map接口特点 1&#xff09;Map与Collection并列存在。用于保存具有映射关系的数据&#xff1a;Key-Value 2&#xff09;Map 中的 key和value 可以是任何引…

5个超好用的视频素材网站,视频剪辑必备。

推荐五个高质量视频素材网站&#xff0c;免费、可商用&#xff0c;赶紧收藏起来&#xff01;1、菜鸟图库 https://www.sucai999.com/video.html?vNTYwNDUx网站素材非常丰富&#xff0c;有平面、UI、电商、办公、视频、音频等相关素材&#xff0c;视频素材质量很高&#xff0c;…

SpringBoot2.x系列教程31--SpringBoot中的缓存实现方案介绍

前言 作为一个程序员&#xff0c;我们不仅仅要把项目的功能实现出来&#xff0c;还要追求这个功能的高效和健壮&#xff0c;我们得想办法对项目的功能进行各种优化和性能的提升。其中缓存就是对程序性能进行显著提升的一个有效手段&#xff0c;那么在SpringBoot中对缓存有哪些…

我的数学学习回忆录——一个数学爱好者的反思(一)

早点关注我&#xff0c;精彩不迷路&#xff01;我是一个热爱数学20余年的数学爱好者&#xff0c;曾奉数学为宇宙的真理和一切行动的指南的极客。从小对数字世界好奇和敏感&#xff0c;玩各种数学思维游戏&#xff0c;然后在高考应试和竞赛中扎实训练数学基础&#xff0c;逐渐把…

[附源码]java毕业设计中华美食网站

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

docker安装filebeat 进行日志收集

1.介绍 filebeat和beats的关系 首先filebeat是Beats中的一员。   Beats在是一个轻量级日志采集器&#xff0c;其实Beats家族有6个成员&#xff0c;早期的ELK架构中使用Logstash收集、解析日志&#xff0c;但是Logstash对内存、cpu、io等资源消耗比较高。相比Logstash&#x…

matlab学习笔记(五)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 matlab学习笔记&#xff08;五&#xff09;一、绘制下列连续时间信号的波形图二、绘制下列离散时间信号的波形图三.已知信号f(t)的波形如下图所示&#xff0c;请用MATLAB绘出…

ES filter查询 高亮查询 聚合查询

filter查询 query&#xff0c;根据你的查询条件&#xff0c;去计算文档的匹配度得到一个分数&#xff0c;并且根据分数进行排序&#xff0c;不会做缓存的。 filter&#xff0c;根据你的查询条件去查询文档&#xff0c;不去计算分数&#xff0c;而且filter会对经常被过滤的数据进…

C++中的多态(上)

&#x1f9f8;&#x1f9f8;&#x1f9f8;各位大佬大家好&#xff0c;我是猪皮兄弟&#x1f9f8;&#x1f9f8;&#x1f9f8; 文章目录一、多态的概念二、虚函数三、破坏多态条件的现象1.破坏多态条件一&#xff0c;虚函数重写/覆盖2.破坏多态条件二四、 多态的两个条件不满足…

数据库审核工具SQLE部署及使用

点击上方蓝字关注我SQLE&#xff08; https://opensource.actionsky.com/sqle/ &#xff09;是由上海爱可生信息技术股份有限公司 开发并开源&#xff0c;支持多场景审核&#xff0c;支持标准化上线流程&#xff0c;原生支持 MySQL 审核且数据库类型可扩展的 SQL 审核工具。我们…

PC_磁盘HDD_SSD

文章目录磁盘存储器组成磁盘驱动器磁盘控制器盘片platter存储区域磁盘结构磁道track扇区sector&#x1f388;/块Block&#x1f386;磁头(Head)圆柱面cylinder磁记录原理磁盘性能指标记录密度磁盘的容量非格式化容量格式化容量数据传输率磁盘转速旋转周期T例平均存取时间纯读/写…