您当前的位置: 首页 >  spring

Charge8

暂无认证

  • 2浏览

    0关注

    447博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Spring Security实现记住我功能&源码分析

Charge8 发布时间:2022-01-12 01:02:53 ,浏览量:2

Spring Security 实现“记住我”功能,即自动登录功能有两种方式:

  • 将 token写入到浏览器的 Cookie中
  • 将 token持久化到数据库
一、将 token写入到浏览器的 Cookie中 1、代码实现

1.1 后端 Spring Security默认是没有开启“记住我”功能,我们在 Spring Security配置类中开启它即可。

// 开启记住我功能
.rememberMe()
.key("rememberMeKey") // 默认 key为UUID,我们自定义 key
.tokenValiditySeconds(60) //设置token的过期时间,默认2周

注意:key 默认值是一个 UUID 字符串,如果服务端重启,这个 key 会变,这样会导致之前所有 remember-me 自动登录令牌失效,所以,我们一般都指定 key值。

1.2 前端 前端需要注意:

  • 记住我的字段名称 默认是 remember-me
  • remember-me的值必须是true | on | yes | "1"

这些字段名可以自定义,我们使用默认值就行。

1.3 测试 测试一下,登录认证通过后,关掉浏览器,再次打开页面,remember-me功能生效了,就这么简单。 在这里插入图片描述 我们将 remember-me的值,通过 Base64 转码后的字符串,得到: 在这里插入图片描述 可以看到,cookie 中 remember-me 的使用用 : 隔开,分成了三部分:

  1. 用户名。
  2. 时间戳,即 token的过期时间。
  3. 是使用 MD5 散列函数算出来的值,它的明文格式是 username + “:” + tokenExpiryTime + “:” + password + “:” + key,最后的 key 是一个散列盐值,可以用来防治令牌被修改。
2、源码分析

前面分析登录认证流程时,认证成功就会调用“记住我”功能。

  • Spring Security登录认证源码分析:https://blog.csdn.net/qq_42402854/article/details/122295175

查看 UsernamePasswordAuthenticationFilter的父类 AbstractAuthenticationProcessingFilter过滤器的 doFIlter方法中,认证做了两个分支,

  • 成功执行 successfulAuthentication,
  • 失败执行 unsuccessfulAuthentication。

在 successfulAuthentication内部,将用户认证信息存储到了 SecurityContext中,并调用了 loginSuccess方法,这就是“记住我”功能的核心方法。 在这里插入图片描述

2.1 token的生成

查看 AbstractRememberMeServices类的 loginSuccess方法

	private String parameter = "remember-me";    

	public final void loginSuccess(HttpServletRequest request, HttpServletResponse response,
                                   Authentication successfulAuthentication) {
		// 判断是否勾选记住我
		// 注意:这里this.parameter点进去是上面的private String parameter = "remember-me";
        if (!this.rememberMeRequested(request, this.parameter)) {
            this.logger.debug("Remember-me login not requested.");
        } else {
			//若勾选就调用onLoginSuccess方法
            this.onLoginSuccess(request, response, successfulAuthentication);
        }
    }

1)“记住我”表单属性的名称和值

        protected boolean rememberMeRequested(HttpServletRequest request, String parameter) {
            if (this.alwaysRemember) {
                return true;
            } else {
                // "remember-me"属性名默认为"remember-me"
                String paramValue = request.getParameter(parameter);
                // 这属性值可以为:true,on,yes,1。
                if (paramValue != null && (paramValue.equalsIgnoreCase("true") ||
                        paramValue.equalsIgnoreCase("on") || paramValue.equalsIgnoreCase("yes") ||
                        paramValue.equals("1"))) {
                    //满足上面条件才能返回true
                    return true;
                } else {
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("Did not send remember-me cookie (principal did not set
                                parameter '" + parameter + "')");
                    }
                    return false;
                }
            }
        }

2)查看 TokenBasedRememberMeServices类的 onLoginSuccess方法。 在这里插入图片描述

public void onLoginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) {
        //1.获取用户信息
        String username = this.retrieveUserName(successfulAuthentication);
        String password = this.retrievePassword(successfulAuthentication);
        if (!StringUtils.hasLength(username)) {
            this.logger.debug("Unable to retrieve username");
        } else {
            if (!StringUtils.hasLength(password)) {
                UserDetails user = this.getUserDetailsService().loadUserByUsername(username);
                password = user.getPassword();
                if (!StringUtils.hasLength(password)) {
                    this.logger.debug("Unable to obtain password for user: " + username);
                    return;
                }
            }
			//2.获取token的有效期
            int tokenLifetime = this.calculateLoginLifetime(request, successfulAuthentication);
            long expiryTime = System.currentTimeMillis();
            expiryTime += 1000L * (long)(tokenLifetime             
关注
打赏
1664721914
查看更多评论
0.0444s