自动登录的实现
当我们在登录某一个网站的时候,我们会使用账号密码进行登录,但是我们不想每一次关闭浏览器,我们不想每一次重新输入账号和密码,贴合用户的登录体验,给用户带来方便,我们使用自动登录功能,将登录信息保存在浏览器的cookie中,这样用户在下次访问的时候,自动实现校验并新建登录状态。但是这样也会给用户带来信息泄露的危险。
自动登录流程
用户选择记住我登录成功后,会在服务端生成一个Cookie返回到浏览器,Cookie名字默认是remember-me,值是token
当用户重新访问的时候,会先检查remember-me的cookie对应的token值,如果token值存在则检查token值中的username、序列号和token值是否和服务端生成的相同,相同则通过。并且系统还会重新生成一个新的token,序列化series保持不变,同时删除旧的cookie,重新生成一个新的cookie保存在客户端
如果cookie不存在,或者username、序列化series和token和服务端的不一样,将重新返回到登录页面。
因为cookie有被盗用的可能,所以如果应用安全性比较高,那么就不要使用remember-me功能。
散列解密方案
SpringSecurity实现自动登录功能非常简单
修改login.html
我们需要在login.html中添加记住我功能,html实现如下
<!--suppress ALL--> <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>springboot葵花宝典登录页面</title> <!-- Tell the browser to be responsive to screen width --> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>springboot葵花宝典登录页面</h1> <form th:action="@{/login/form}" action="index.html" method="post"> <span>用户名称</span><input type="text" name="username" /> <br> <span>用户密码</span><input type="password" name="password" /> <br> <div > <input name="code" type="text" class="form-control" placeholder="验证码"> <img onclick="this.src='/code/image?'+Math.random()" src="/code/image" alt="验证码" /> </div> <br> <span>记住我</span><input type="checkbox" name="remember-me" > <br> <div th:if="${param.error}"> <span th:text="${session.SPRING_SECURITY_LAST_EXCEPTION.message}" style="color:#ff0000">用户名或 密码错误</span> </div> <input type="submit" value="登陆"> </form> </body> </html>
修改结果后登录页面展示如下:
修改LearnSrpingSecurity
在configure(HttpSecurity http)方法中添加记住我的配置,代码实现如下
测试
重新启动程序,使用浏览器访问http://localhost:8888/login/page,然后输入账号密码进行登录,结果如下
从图上我们可以看出来,勾选“Remember me”可选框(简写为Remember-me),按照正常的流程登录,并在 开发者工具中查看浏览器cookie,可以看到除JSESSIONID外多了一个值。
从图上我们可以看出来,勾选“Remember me”可选框(简写为Remember-me),按照正常的流程登录,并在 开发者工具中查看浏览器cookie,可以看到除JSESSIONID外多了一个值。
cookie个性化配置
在实际的开发过程中,我们还可以根据需求做一些个性化的设置,如下:
.rememberMe() .rememberMeParameter("remember-me-new") .rememberMeCookieName("remember-me-cookie")
通过rememberMeParameter设置from表单“自动登录”勾选框的参数名称。如果这里改了,from表单中checkbox的name属性要对应的更改。如果不设置默认是remember-me。
rememberMeCookieName设置了保存在浏览器端的cookie的名称,如果不设置默认也是remember-me。如下图中查看浏览器的cookie。
持久化令牌方案
持久化令牌方案是交互方式上和散列加密方案一致,都是用户勾选remember-me
后,将生成的令牌发送到用户的客户端,用户在下次进行访问的时候会读取该令牌,不同的是会将数据保存到数据库中。
添加数据库依赖
我们在项目中的pom.xml中添加数据库依赖
<!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!--mybatis-plus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.1</version> </dependency>
application.yml中添加数据库配置
spring: thymeleaf: cache: false datasource: url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=utf-8&useSSL=false username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver
使用 JdbcTokenRepository 实现类
在LearnSrpingSecurity配置类中注入DataSource
和JdbcTokenRepositoryImpl
,实现如下
@Autowired private DataSource dataSource; @Bean public JdbcTokenRepositoryImpl jdbcTokenRepository(){ JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl(); jdbcTokenRepository.setDataSource(dataSource); jdbcTokenRepository.setCreateTableOnStartup(true); return jdbcTokenRepository; }
安全配置 LearnSrpingSecurity#confifigure(HttpSecurity http)
在LearnSrpingSecurity的confifigure(HttpSecurity http)方法中添加如下配置
.and() .rememberMe() .userDetailsService(customUserDetailsService) .tokenRepository(jdbcTokenRepository()) .tokenValiditySeconds(60) .rememberMeParameter("remember-me-test") .rememberMeCookieName("remember-me-cookie")
测试效果
重启项目后,数据库会自动创建表 persistent_logins
访问首页跳转到登录页面,登录成功后,查看浏览器 cookies,保存 token 值,在数据库中也有数据。
再重启,Session会清除。再次访问首页,不会跳转到登录页,因为上次已经记录了。这时浏览器Cookie中保存的token从数据库查找用户名,然后进行自动登录。
报错
Caused by: java.sql.SQLSyntaxErrorException: Table ‘persistent_logins’ already exists
解决方法:
将JdbcTokenRepositoryImpl方法中jdbcTokenRepository.setCreateTableOnStartup(true)注释掉
@Bean public JdbcTokenRepositoryImpl jdbcTokenRepository(){ JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl(); jdbcTokenRepository.setDataSource(dataSource); //jdbcTokenRepository.setCreateTableOnStartup(true); return jdbcTokenRepository; }
退出系统
其实使用Spring Security进行logout非常简单,只需要在spring Security配置类配置项上加上这样一行代码:http.logout()。
修改index.html
我们在index.html中添加退出系统功能,实现如下
<!--suppress ALL--> <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8" /> <title>springboot葵花宝典管理系统</title> </head> <body> <a th:href="@{/logout}" href="login.html" > <span>退出系统</span> </a> <h1>springboot葵花宝典管理系统</h1> <br> <a href="/syslog">日志管理</a> <br> <a href="/sysuser">用户管理</a> </body>
测试
重新启动项目后,登录成功后展示页面如下,退出登录后,我们发现remember-me-cookie已经删除。虽然我们简简单单的实现了logout功能,是不是还不足够放心?我们下面就来看一下Spring Security默认在logout过程中帮我们做了哪些动作。
当前session失效,即:logout的核心需求,session失效就是访问权限的回收。
删除当前用户的 remember-me“记住我”功能信息
clear清除当前的 SecurityContext
重定向到登录页面,loginPage配置项指定的页面
个性化配置
虽然Spring Security默认使用了/logout作为退出处理请求路径,登录页面作为退出之后的跳转页面。这符合绝大多数的应用的开发逻辑,但有的时候我们需要一些个性化设置,如下
.and() .logout() .logoutUrl("/logout") .logoutSuccessUrl("/login/page") .deleteCookies("JSESSIONID")
指定logoutUrl配置修改默认的logout路径,同时html退出按钮的请求url也要修改
指定logoutSuccessUrl配置,指定退出之后的跳转页面
使用deleteCookies删除指定的cookie,参数为cookie的名称
如果您觉得本文不错,欢迎关注,点赞,收藏支持,您的关注是我坚持的动力!