springsecurity 源码解读 之 RememberMeAuthenticationFilter

时间:2022-12-19 10:09:51

RememberMeAuthenticationFilter 的作用很简单,就是用于当session 过期后,系统自动通过读取cookie 让系统自动登录。

我们来看看Springsecurity的过滤器链条。

springsecurity 源码解读 之 RememberMeAuthenticationFilter

我们发现这个 RememberMeAuthenticationFilter  在 匿名构造器之前,这个是为什么呢?

还是从源码来分析:

if (SecurityContextHolder.getContext().getAuthentication() == null) {
Authentication rememberMeAuth = rememberMeServices.autoLogin(request, response); if (rememberMeAuth != null) {

代码中有这样的一行,当SecurityContext 中 Authentication 为空时,他就会调用 rememberMeServices 自动登录。

因此刚刚的问题也就好解释了,因为如果RememberMeAuthenticationFilter  没有实现自动登录,那么他的Authentication 还是为空,

这是可以创建一个匿名登录,如果先创建了匿名登录,那么这个 RememberMeAuthenticationFilter   的判断就不会为null,过滤器将失效。

我们看看rememberMeServices 的autoLogin 登录

我们贴出实现的代码:

public final Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
String rememberMeCookie = extractRememberMeCookie(request); if (rememberMeCookie == null) {
return null;
} logger.debug("Remember-me cookie detected"); if (rememberMeCookie.length() == 0) {
logger.debug("Cookie was empty");
cancelCookie(request, response);
return null;
} UserDetails user = null; try {
String[] cookieTokens = decodeCookie(rememberMeCookie);
user = processAutoLoginCookie(cookieTokens, request, response);
userDetailsChecker.check(user); logger.debug("Remember-me cookie accepted"); return createSuccessfulAuthentication(request, user);
} catch (CookieTheftException cte) {
cancelCookie(request, response);
throw cte;
} catch (UsernameNotFoundException noUser) {
logger.debug("Remember-me login was valid but corresponding user not found.", noUser);
} catch (InvalidCookieException invalidCookie) {
logger.debug("Invalid remember-me cookie: " + invalidCookie.getMessage());
} catch (AccountStatusException statusInvalid) {
logger.debug("Invalid UserDetails: " + statusInvalid.getMessage());
} catch (RememberMeAuthenticationException e) {
logger.debug(e.getMessage());
} cancelCookie(request, response);
return null;
}
extractRememberMeCookie这个方法判断 SPRING_SECURITY_REMEMBER_ME_COOKIE 这样的cookie,如果没有就直接返回了null。
processAutoLoginCookie:这个是处理cookie 并从cookie加载用户。

默认springsecurity 使用类 TokenBasedRememberMeServices 来解析 cookie。

实现代码如下:
protected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,
HttpServletResponse response) { if (cookieTokens.length != 3) {
throw new InvalidCookieException("Cookie token did not contain 3" +
" tokens, but contained '" + Arrays.asList(cookieTokens) + "'");
} long tokenExpiryTime; try {
tokenExpiryTime = new Long(cookieTokens[1]).longValue();
}
catch (NumberFormatException nfe) {
throw new InvalidCookieException("Cookie token[1] did not contain a valid number (contained '" +
cookieTokens[1] + "')");
} if (isTokenExpired(tokenExpiryTime)) {
throw new InvalidCookieException("Cookie token[1] has expired (expired on '"
+ new Date(tokenExpiryTime) + "'; current time is '" + new Date() + "')");
} // Check the user exists.
// Defer lookup until after expiry time checked, to possibly avoid expensive database call. UserDetails userDetails = getUserDetailsService().loadUserByUsername(cookieTokens[0]); // Check signature of token matches remaining details.
// Must do this after user lookup, as we need the DAO-derived password.
// If efficiency was a major issue, just add in a UserCache implementation,
// but recall that this method is usually only called once per HttpSession - if the token is valid,
// it will cause SecurityContextHolder population, whilst if invalid, will cause the cookie to be cancelled.
String expectedTokenSignature = makeTokenSignature(tokenExpiryTime, userDetails.getUsername(),
userDetails.getPassword()); if (!equals(expectedTokenSignature,cookieTokens[2])) {
throw new InvalidCookieException("Cookie token[2] contained signature '" + cookieTokens[2]
+ "' but expected '" + expectedTokenSignature + "'");
} return userDetails;
}   
 protected String makeTokenSignature(long tokenExpiryTime, String username, String password) {
String data = username + ":" + tokenExpiryTime + ":" + password + ":" + getKey();
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("No MD5 algorithm available!");
} return new String(Hex.encode(digest.digest(data.getBytes())));
}

这个代码就是加载用户,并验证cookie 是否有效。

因此我们在写入cookie 时,产生的cookie 过程如下:

String enPassword=EncryptUtil.hexToBase64(password);

        long tokenValiditySeconds = 1209600; // 14 days
long tokenExpiryTime = System.currentTimeMillis() + (tokenValiditySeconds * 1000);
String signatureValue = makeTokenSignature(tokenExpiryTime,username,enPassword);
String tokenValue = username + ":" + tokenExpiryTime + ":" + signatureValue;
String tokenValueBase64 = new String(Base64.encodeBase64(tokenValue.getBytes())); CookieUtil.addCookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,
tokenValueBase64,60 * 60 * 24 * 365, true, request, response);
 
														
		

springsecurity 源码解读 之 RememberMeAuthenticationFilter的更多相关文章

  1. springsecurity 源码解读之 SecurityContext

    在springsecurity 中,我们一般可以通过代码: SecurityContext securityContext = SecurityContextHolder.getContext(); ...

  2. springsecurity 源码解读之 AnonymousAuthenticationFilter

    我们知道springsecutity 是通过一系列的 过滤器实现的,我们可以看看这系列的过滤器到底长成什么样子呢? 一堆过滤器,这个过滤器的设计设计上是 责任链设计模式. 这里我们可以看到有一个 An ...

  3. SDWebImage源码解读之SDWebImageDownloaderOperation

    第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...

  4. SDWebImage源码解读 之 NSData+ImageContentType

    第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...

  5. SDWebImage源码解读 之 UIImage+GIF

    第二篇 前言 本篇是和GIF相关的一个UIImage的分类.主要提供了三个方法: + (UIImage *)sd_animatedGIFNamed:(NSString *)name ----- 根据名 ...

  6. SDWebImage源码解读 之 SDWebImageCompat

    第三篇 前言 本篇主要解读SDWebImage的配置文件.正如compat的定义,该配置文件主要是兼容Apple的其他设备.也许我们真实的开发平台只有一个,但考虑各个平台的兼容性,对于框架有着很重要的 ...

  7. SDWebImage源码解读_之SDWebImageDecoder

    第四篇 前言 首先,我们要弄明白一个问题? 为什么要对UIImage进行解码呢?难道不能直接使用吗? 其实不解码也是可以使用的,假如说我们通过imageNamed:来加载image,系统默认会在主线程 ...

  8. SDWebImage源码解读之SDWebImageCache(上)

    第五篇 前言 本篇主要讲解图片缓存类的知识,虽然只涉及了图片方面的缓存的设计,但思想同样适用于别的方面的设计.在架构上来说,缓存算是存储设计的一部分.我们把各种不同的存储内容按照功能进行切割后,图片缓 ...

  9. SDWebImage源码解读之SDWebImageCache(下)

    第六篇 前言 我们在SDWebImageCache(上)中了解了这个缓存类大概的功能是什么?那么接下来就要看看这些功能是如何实现的? 再次强调,不管是图片的缓存还是其他各种不同形式的缓存,在原理上都极 ...

随机推荐

  1. Oracle存储过程获取YYYY-MM-DD的时间格式

    环境:Oracle 10g,11g 问题重现:PL/SQL中命令窗口下,发现存储过程得到的时间格式不符合预期要求. SQL> select sysdate from dual; SYSDATE ...

  2. C#的注释和快速开启工具的命令

    1.注释的方法 1)sqlserver中,单行注释:——   多行注释:/****/ 2)C#中,单行注释://    多行注释:/****/ 3)C#中多行注释的快捷方式:启用ctrl+E+C ,撤 ...

  3. 详述Linux ftp命令的使用方法

    转自:http://os.51cto.com/art/201003/186325.htm ftp服务器在网上较为常见,Linux ftp命令的功能是用命令的方式来控制在本地机和远程机之间传送文件,这里 ...

  4. IT公司100题-17-第一个只出现一次的字符

    问题描述: 在一个字符串中找到第一个只出现一次的字符.例如输入asdertrtdsaf,输出e.   分析: 最简单的方法是直接遍历,时间复杂度为O(n^2). 进一步思考: 字符串中的字符,只有25 ...

  5. 开发中/listfile.jsp(11,31) quote symbol expected 这个错误

    可能是因为11行33列,少了一个引号.

  6. Android 开发笔记___初级控件之实战__计算器

    功能简单,实现并不难,对于初学者可以总和了解初级控件的基本使用. 用到的知识点如下: 线性布局 LinearLayout:整体界面是从上往下的,因此需要垂直方向的linearlayout:下面每行四个 ...

  7. 在centos7上使用最简单的方法把php脚本做成服务,随开机启动运行

    1.准备文件:coffeetest.service # copy to /usr/lib/systemd/system # systemctl enable coffeetest.service [U ...

  8. 『编程题全队』Beta 阶段冲刺博客五

    1.提供当天站立式会议照片一张 2.每个人的工作 (有work item 的ID) (1) 昨天已完成的工作 孙志威: 1.为新建提醒框添加了正则匹配限制 2.添加了新建Reminder的功能 3.初 ...

  9. JS IE 打开本地exe程序

    例: try{ //新建一个ActiveXObject对象 var exe = new ActiveXObject("wscript.shell"); var exePath = ...

  10. python redis 的基本操作指令

    #!/usr/bin/env python # -*- coding: utf-8 -*- ''' redis基本命令和基本用法详解 1.redis连接 2.redis连接池 3.redis基本命令 ...