zxpnet网站 zxpnet网站
首页
前端
后端服务器
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

zxpnet

一个爱学习的java开发攻城狮
首页
前端
后端服务器
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 大后端课程视频归档
  • 南航面试题
  • 并发编程

  • 性能调优

  • java8语法

  • lombok

  • 日志

  • 工具类

  • spring

  • mybatis

  • springboot

  • redis

  • zookeeper

  • springcloud

  • dubbo

  • netty

  • springsecurity

    • springsecurity基本使用及个性化登录配置
    • 微服务安全与实战

    • springsecurity认证流程分析
    • springsecurity过滤器链分析
    • springsecurity图形验证码
    • springsecurity记住我RememberMe功能
      • 记住我功能的基本原理
      • 记住我功能具体实现
      • 记住我功能SpringSecurity源码分析
      • 测试
    • springsecurity手机验证码登陆
    • springsecurity httpFirewall
    • springsecurity漏洞保护
    • Untitled
    • 跨域问题
    • Springsecurity Session会话管理
    • springsecurity控制授权
    • 权限模型
    • springseuciryt oauth2基础
    • 基于JWT实现SSO单点登录
    • springsocial第三方登陆
    • springsecurity退出登陆
    • Spring security 集成 JustAuth 实现第三方授权登录
    • Spring Boot+CAS 单点登录
    • Springsecurity常见错误
  • mq消息中间件

  • shiro

  • beetle

  • 模板引擎

  • jpa

  • 数据结构与算法

  • 数据库知识与设计

  • gradle

  • maven

  • bus

  • 定时任务

  • docker

  • centos

  • 加解密

  • biz业务

  • pigx项目

  • 开源项目

  • 品达通用权限项目-黑马

  • 货币交易项目coin-尚学堂

  • php

  • backend
  • springsecurity
shollin
2021-07-04
目录

springsecurity记住我RememberMe功能

  • 记住我功能的基本原理
  • 记住我功能具体实现
  • 记住我功能SpringSecurity源码分析
  • 测试

# 记住我功能的基本原理

img

记住我功能的基本原理

之前有讲过,当用户发起认证请求,会通过UsernamePasswordAuthenticationFilter,在认证成功之后,可以调用SpringSecurity提供的RememberMeService,它会生成一个Token并将它写入浏览器的Cookie中,同时这个它里面有一个TokenRepository,TokenRepository会将Token放入数据库中。 当下次浏览器再请求的时候,会经过RememberMeAuthenticationFiler,在这个filter里面会读取Cookie中的Token,然后去数据库中查找是否有相应的Token,然后再通过UserDetailsService获取用户的信息。

# 记住我功能具体实现

我们就按照它的基本原理来挨着顺序做相应的配置就可以了

@Autowired
private DataSource dataSource;

@Autowired
private UserDetailsService myUserDetailsService;

/**
 * 配置TokenRepository
 * @return
 */
@Bean
public PersistentTokenRepository persistentTokenRepository() {
    JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
    // 配置数据源
    jdbcTokenRepository.setDataSource(dataSource);
    // 第一次启动的时候自动建表(可以不用这句话,自己手动建表,源码中有语句的)
    // jdbcTokenRepository.setCreateTableOnStartup(true);
    return jdbcTokenRepository;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

这里注入了UserDetailsService,同时配置了一下TokenRepository,因为需要和数据库进行交互,所以DataSource是必须的,这里就不贴出配置相关的代码了。

接下来就是在config方法中做相应的配置了

 @Override
protected void configure(HttpSecurity http) throws Exception {
    http.formLogin()
            .rememberMe()                                   // 记住我相关配置
            .tokenRepository(persistentTokenRepository())   // 设置TokenRepository
            // 配置Cookie过期时间
            .tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())
            // 配置UserDetailsService
            .userDetailsService(myUserDetailsService);
    // 省略其他配置
}
1
2
3
4
5
6
7
8
9
10
11

# 记住我功能SpringSecurity源码分析

根据最开始的原理分析,我们可以知道一般是在认证成功之后,才会去做记住我的操作,所以我们可以看successfulAuthentication方法中的相关代码

// AbstractAuthenticationProcessingFilter.java
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
    SecurityContextHolder.getContext().setAuthentication(authResult);
    // 调用rememberMeServices,进行登录操作
    this.rememberMeServices.loginSuccess(request, response, authResult);
    if (this.eventPublisher != null) {
        this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
    }

    this.successHandler.onAuthenticationSuccess(request, response, authResult);
}

这里在认证成功之后,调用了RememberMeServices的loginSuccess方法,该方法会进一步调用onLoginSuccess:

protected void onLoginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) {
    String username= successfulAuthentication.getName();
    PersistentRememberMeToken persistentToken = new PersistentRememberMeToken(username, this.generateSeriesData(), this.generateTokenData(), new Date());

    try {
        // 进行TokenRepository操作
        this.tokenRepository.createNewToken(persistentToken);
        // 进行Cookie操作
        this.addCookie(persistentToken, request, response);
    } catch (Exception var7) {
        this.logger.error("Failed to save persistent token ", var7);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

这样相关的数据库操作和Token操作就完成了。

接下来看看下次登录的时候,是怎么进行记住我认证的。这里我们就直接看RememberMeAuthenticationFilter

image-20210708115927182

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest)req;
    HttpServletResponse response = (HttpServletResponse)res;
    // 1.先判断之前是否已经通过认证了
    if (SecurityContextHolder.getContext().getAuthentication() == null) {
        // 2.通过RememberMeServices进行登录 
        Authentication rememberMeAuth = this.rememberMeServices.autoLogin(request, response);
        if (rememberMeAuth != null) {
            try {
                rememberMeAuth = this.authenticationManager.authenticate(rememberMeAuth);
                SecurityContextHolder.getContext().setAuthentication(rememberMeAuth);
                this.onSuccessfulAuthentication(request, response, rememberMeAuth);
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("SecurityContextHolder populated with remember-me token: '" + SecurityContextHolder.getContext().getAuthentication() + "'");
                }

                if (this.eventPublisher != null) {
                    this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(SecurityContextHolder.getContext().getAuthentication(), this.getClass()));
                }

                if (this.successHandler != null) {
                    this.successHandler.onAuthenticationSuccess(request, response, rememberMeAuth);
                    return;
                }
            } catch (AuthenticationException var8) {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("SecurityContextHolder not populated with remember-me token, as AuthenticationManager rejected Authentication returned by RememberMeServices: '" + rememberMeAuth + "'; invalidating remember-me token", var8);
                }

                this.rememberMeServices.loginFail(request, response);
                this.onUnsuccessfulAuthentication(request, response, var8);
            }
        }

        chain.doFilter(request, response);
    } else {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("SecurityContextHolder not populated with remember-me token, as it already contained: '" + SecurityContextHolder.getContext().getAuthentication() + "'");
        }

        chain.doFilter(request, response);
    }

}

# 测试

关掉并重启应用后,访问的时候,没有跳到登陆页。

参考文章:

SpringBoot + SpringSecurity “记住我”功能实现及相关源码分析_whyalwaysmea-CSDN博客_springsecurity 记住我 (opens new window)

springsecurity图形验证码
springsecurity手机验证码登陆

← springsecurity图形验证码 springsecurity手机验证码登陆→

最近更新
01
国际象棋
09-15
02
成语
09-15
03
自然拼读
09-15
更多文章>
Theme by Vdoing | Copyright © 2019-2023 zxpnet | 粤ICP备14079330号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式