当前位置: 首页 > article >正文

SpringSecurity5|12.实现RememberMe 及 实现原理分析

security/day08

这个功能大家还熟悉么?我们在登录网站的时候,除了让你输入用户名和密码,还会有个勾选框:
记住我!!!不是让大家记住我哈。

在这里插入图片描述

值得一提的是,Spring Security 也提供了这个功能,我们今天就来体验一把。

代码实战

其实想要开启rememberMe功能,其实很简单,只需要简单的修改下配置即可:

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests().anyRequest().authenticated()
                .and()
                .formLogin()
                .permitAll()
                .and()
                .rememberMe()
                .and()
                .csrf().disable();
        return http.build();
    }

实现思路

  • 当用户登录网站后,并且勾选rememberMe
  • 服务端认证通过,则把用户信息进行算法加密,加密完成后,通过cookie,让浏览器把cookie保存在本地
  • 当浏览器关闭后重新打开网站,浏览器会把cookie带给服务端
  • 服务端校验cookie确定用户身份,进而自动登录

源码分析

首先我们找切入点,我们前面讲过当引入一个新功能的时候,必定会引入一个configurer,rememberMe 也不例外:点击.rememberMe()进入源码
在这里插入图片描述

看到了RememberMeConfigurer,我们点进去看下主要是看init和configure方法

init

	public void init(H http) throws Exception {
		validateInput();
		String key = getKey();
		RememberMeServices rememberMeServices = getRememberMeServices(http, key);
		http.setSharedObject(RememberMeServices.class, rememberMeServices);
		LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
		if (logoutConfigurer != null && this.logoutHandler != null) {
			logoutConfigurer.addLogoutHandler(this.logoutHandler);
		}
		RememberMeAuthenticationProvider authenticationProvider = new RememberMeAuthenticationProvider(key);
		authenticationProvider = postProcess(authenticationProvider);
		http.authenticationProvider(authenticationProvider);
		initDefaultLoginFilter(http);
	}
  • getKey() 这个就是我们刚开始设置的key,如果不配置,每次系统都会重启,导致需要重新登录
  • getRememberMeServices() 用来实现自动登录、处理登录成功和登录失败的逻辑,主要有两个继承
    • PersistentTokenBasedRememberMeServices 持久化令牌到数据库
    • TokenBasedRememberMeServices
  • http.authenticationProvider(authenticationProvider);创建一个认证器放到providerList里面
  • initDefaultLoginFilter(http); 在自定生成登录页面加上rememberMe 选框

configure

	public void configure(H http) {
		RememberMeAuthenticationFilter rememberMeFilter = new RememberMeAuthenticationFilter(
				http.getSharedObject(AuthenticationManager.class), this.rememberMeServices);
		if (this.authenticationSuccessHandler != null) {
			rememberMeFilter.setAuthenticationSuccessHandler(this.authenticationSuccessHandler);
		}
		rememberMeFilter = postProcess(rememberMeFilter);
		http.addFilter(rememberMeFilter);
	}
  • 创建了一个过滤器:RememberMeAuthenticationFilter
  • postProcess(rememberMeFilter); 注入IOC容器
  • 在http中加入rememberMe过滤器

看完了RememberMeConfigurer的两个核心方法之后,我们现在知道容器中已经有了RememberMeAuthenticationFilter和RememberMeAuthenticationProvider
现在分别来看下

RememberMeAuthenticationFilter

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		Authentication rememberMeAuth = this.rememberMeServices.autoLogin(request, response);
		if (rememberMeAuth != null) {
			// Attempt authenticaton via AuthenticationManager
			try {
				rememberMeAuth = this.authenticationManager.authenticate(rememberMeAuth);
				// Store to SecurityContextHolder
				SecurityContext context = SecurityContextHolder.createEmptyContext();
				context.setAuthentication(rememberMeAuth);
				SecurityContextHolder.setContext(context);
				onSuccessfulAuthentication(request, response, rememberMeAuth);
			   this.securityContextRepository.saveContext(context, request, response);
				if (this.successHandler != null) {
					this.successHandler.onAuthenticationSuccess(request, response, rememberMeAuth);
					return;
				}
			}
			catch (AuthenticationException ex) {
				this.rememberMeServices.loginFail(request, response);
				onUnsuccessfulAuthentication(request, response, ex);
			}
		}
		chain.doFilter(request, response);
	}
  • 调用autoLogin方法
    • 从cookie提取用户身份,在调用loadUserByUsername获取用户详细信息
    • 校验用户身份是否合法(根据不同的令牌存储方式进行不同校验)
    • 如果校验通过,则返回Authentication(RememberMeAuthenticationToken)
  • authenticate
    • 如果autoLogin成功,则和前面普通登录一样进行认证
    • 因为这里生成的是RememberMeAuthenticationToken,则最终会被RememberMeAuthenticationProvider处理
  • 如果认证返回则进行对应的响应处理

RememberMeAuthenticationProvider

	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		if (!supports(authentication.getClass())) {
			return null;
		}
		if (this.key.hashCode() != ((RememberMeAuthenticationToken) authentication).getKeyHash()) {
			throw new BadCredentialsException(this.messages.getMessage("RememberMeAuthenticationProvider.incorrectKey",
					"The presented RememberMeAuthenticationToken does not contain the expected key"));
		}
		return authentication;
	}

这里的逻辑只做了一步,校验key的hashCode是否一致,如果一致,则校验通过


http://www.kler.cn/news/136220.html

相关文章:

  • 《向量数据库指南》——text-embedding-3-large与Mlivus Cloud打造语义搜索新纪元
  • Redis多级缓存
  • Java-图书管理系统
  • 深入剖析 C 与 C++ 动态内存管理之术
  • 使用 docker 的方式部署 NFS server 提供文件共享能力
  • 什么是域名?什么是泛域名?
  • 力扣labuladong一刷day13天双指针8道链表题
  • Uniapp中的事件处理:uni.emit和uni.on/uni.once
  • “非旺玖原装的PL2303,.........“解决办法
  • 线上发生锁表怎么办 show processlist命令详解
  • 基于 Junit 的接口自动化测试框架实现!
  • Avoiding Row-by-Row Processing 避免逐行处理
  • 【C++】类与对象(上)
  • 场景交互与场景漫游-对象选取(8-2)
  • webshell免杀之传参方式
  • CDN加速在目前网络安全里的重要性
  • PPT幻灯片里的图片,批量提取
  • [PHP]关联和操作MySQL数据库然后将数据库部署到ECS
  • centos7安装WordPress
  • 使用树莓派学习Linux系统编程的 --- 库编程(面试重点)
  • vue和uni-app的递归组件排坑
  • 【框架整合】Redis限流方案
  • 切面Aspect + 策略模式实现待办提醒功能
  • redis-持久化
  • 代码随想录算法训练营Day36 —— 738.单调递增的数字
  • Android App 启动流程学习