spring-security 实现记住我,免登录 - Hash-Based Token

时间 2019/1/10 17:24:47 加载中...

介绍

先贴一个官方介绍:
https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#remember-me

“记住我”,这个功能有两种实现形式,一种是将token保存在Hash中,即 Simple Hash-Based Token Approach。
一种是将token保存在数据库中,即 Persistent Token Approach。

因 Simple Hash-Based Token Approach 存在安全隐患,因此并不推荐。但这里仍然介绍下其实现。

当用户登录成功后,服务器则会向浏览器发送一个cookie。此cookie的生成方式为

  1. base64(username + ":" + expirationTime + ":" +
  2. md5Hex(username + ":" + expirationTime + ":" password + ":" + key))

key: 防止修改令牌的私钥

cookie会有一个过期时间,在没有过期之前,用户名、密码和key都不会改变,所以如果有人劫持到cookie,则能够
登录你的帐号,你只有改变密码,这个cookie才会失效。

相关文章
spring-security 实现记住我,免登录 - Persistent Token

练习

使用IDEA新建Maven项目

修改pom.xml,引入Spring Boot

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <groupId>com.sqber</groupId>
  7. <artifactId>RememberMeHashBased</artifactId>
  8. <version>1.0-SNAPSHOT</version>
  9. <parent>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-parent</artifactId>
  12. <version>2.0.1.RELEASE</version>
  13. </parent>
  14. <properties>
  15. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  16. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  17. <java.version>1.8</java.version>
  18. </properties>
  19. <dependencies>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter-web</artifactId>
  23. </dependency>
  24. <dependency>
  25. <groupId>org.springframework.boot</groupId>
  26. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  27. </dependency>
  28. <dependency>
  29. <groupId>org.springframework.boot</groupId>
  30. <artifactId>spring-boot-starter-security</artifactId>
  31. </dependency>
  32. <dependency>
  33. <groupId>org.thymeleaf.extras</groupId>
  34. <artifactId>thymeleaf-extras-springsecurity4</artifactId>
  35. </dependency>
  36. </dependencies>
  37. </project>

创建控制器和视图

我们创建一个简单的 HomeController

  1. package com.sqber.hashbased.controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.GetMapping;
  4. @Controller
  5. public class HomeController {
  6. @GetMapping("/")
  7. public String home() {
  8. return "home/index";
  9. }
  10. @GetMapping("/login")
  11. public String login() {
  12. return "home/login";
  13. }
  14. }

一个首页,一个登录页

  1. <!DOCTYPE html>
  2. <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
  3. xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
  4. <head>
  5. <title>Security with Spring Boot</title>
  6. </head>
  7. <body>
  8. <h1>首页</h1>
  9. </body>
  10. </html>
  1. <!DOCTYPE html>
  2. <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <title>登录</title>
  5. </head>
  6. <body>
  7. <div th:if="${param.error}">
  8. <h1 style="color:red">用户名或密码错误</h1>
  9. </div>
  10. <div th:if="${param.logout}">
  11. <h1 style="color:blue">退出登录</h1>
  12. </div>
  13. <form th:action="@{/login}" method="post">
  14. <div>用户名: <input type="text" name="username"/> </div>
  15. <div>密码: <input type="password" name="password"/> </div>
  16. <div>记住我: <input type="checkbox" name="remember-me" /> </div>
  17. <div><input type="submit" value="登录"/></div>
  18. </form>
  19. </body>
  20. </html>

Security 配置

我们只配置一个 user 用户,且密码也为 user

  1. package com.sqber.hashbased.config;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
  6. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  7. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  8. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  9. @Configuration
  10. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  11. @Override
  12. protected void configure(HttpSecurity http) throws Exception {
  13. http.authorizeRequests()
  14. .antMatchers("/resources/**")
  15. .permitAll()
  16. .anyRequest()
  17. .authenticated()
  18. .and()
  19. .formLogin()
  20. .loginPage("/login")
  21. .permitAll()
  22. .and()
  23. .rememberMe()
  24. .key("uniqueAndSecret")
  25. .rememberMeCookieName("remember-me")
  26. .tokenValiditySeconds(24 * 60 * 60) //1天
  27. .and()
  28. .logout()
  29. .deleteCookies("JSESSIONID")
  30. .permitAll();
  31. }
  32. @Bean
  33. public BCryptPasswordEncoder passwordEncoder() {
  34. return new BCryptPasswordEncoder();
  35. }
  36. @Autowired
  37. public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
  38. auth.inMemoryAuthentication().withUser("user").password(new BCryptPasswordEncoder().encode("user")).roles("USER");
  39. }
  40. }

注意配置 BCryptPasswordEncoder,否则会报错:There is no PasswordEncoder mapped for the id “null”

查看效果

在浏览器中打开 localhost:8080 ,则会跳转到 login 页面。 输入 user,user 后,并选择 记住我 ,登录。
我们会看到两个 cookie

删除掉 JSESSIONID 这个cookie后,重新刷新,不会跳转到登录页。

Demo下载

完。

版权说明
作者:SQBER
文章来源:http://blog.sqber.com/articles/spring-security-remember-me-hash-based.html
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。