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

SpringSecurity中用户表单登录验证源码分析

SpringSecurity简单介绍

Spring Security所解决的问题就是安全访问控制,安全访问控制功能其实就是对所有进入系统的请求进行拦截,校验每个请求是否能够访问它所期望的资源。通过学习SpringMVC我们得知只有进入Controller的请求才会走拦截器(Interceptor),而过滤器可以过滤所有的请求,因此可以通过Filter来实现,SpringSecurity对Web资源的保护是就是靠一大串Filter实现的。

当初始化Spring Security时,会创建一个名为 SpringSecurityFilterChain 的Servlet过滤器,类型为org.springframework.security.web.FilterChainProxy,其实现了javax.servlet.Filter,外部的请求都会经过此类
在这里插入图片描述
FilterChainProxy 是一个代理,真正起作用的是FilterChainProxy中SecurityFilterChain所包含的各个Filter,同时这些Filter作为Bean被Spring管理,它们是Spring Security核心,各有各的职责,但他们并不直接处理用户的认证,也不直接处理用户的授权,而是把它们交给了身份认证管理器(AuthenticationManager)和访问决策管理器(AccessDecisionManager)进行处理,
通过上面的介绍我们知道AuthenticationManager接口的实现类里面维护了一个List<ManagerProvider>,ManagerProvider提供多种实现类,每个实现类可以有自己的身份认证方式,比如短信验证、邮箱验证、表单验证等,其中DaoAuthenticationProvier就是专门处理表单验证的
在这里插入图片描述

在这里插入图片描述

表单认证分析

在这里插入图片描述

  1. 用户提交用户名、密码被SecurityFilterChain中的 UsernamePasswordAuthenticationFilter 过滤器获取到,封装为请求Authentication,通常情况下是UsernamePasswordAuthenticationToken这个实现类。

    在这里插入图片描述

  2. 然后过滤器将Authentication提交至认证管理器(AuthenticationManager)进行认证
    在这里插入图片描述

  3. 认证成功后, AuthenticationManager 身份管理器返回一个被填充满了信息的(包括上面提到的权限信息,身份信息,细节信息,但密码通常会被移除) Authentication 实例。

  4. SecurityContextHolder 安全上下文容器将第3步填充了信息的 Authentication ,通过SecurityContextHolder.getContext().setAuthentication(…)方法,设置到其中。可以看出AuthenticationManager接口(认证管理器)是认证相关的核心接口,也是发起认证的出发点,它的实现类为ProviderManager。而Spring Security支持多种认证方式,因此ProviderManager维护着一个List 列表,存放多种认证方式,最终实际的认证工作是由AuthenticationProvider完成的。咱们知道web表单的对应的AuthenticationProvider实现类为DaoAuthenticationProvider,它的内部又维护着一个UserDetailsService负责UserDetails的获取。最终AuthenticationProvider将UserDetails填充至Authentication
    在这里插入图片描述

AuthenticationProvider接口:

在这里插入图片描述

AbstractUserDetailsAuthenticationProvider抽象类

在这里插入图片描述


	private String determineUsername(Authentication authentication) {
		return (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
	}

	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
				() -> this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",
						"Only UsernamePasswordAuthenticationToken is supported"));
		String username = determineUsername(authentication);
		boolean cacheWasUsed = true;
		UserDetails user = this.userCache.getUserFromCache(username);
		if (user == null) {
			cacheWasUsed = false;
			try {
				user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
			}
			........................
			try {
				this.preAuthenticationChecks.check(user);
				additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
		}
		...................
	}

在这里插入图片描述

DaoAuthenticationProvider

在这里插入图片描述

protected void additionalAuthenticationChecks(UserDetails userDetails,
			UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
		if (authentication.getCredentials() == null) {
			this.logger.debug("Failed to authenticate since no credentials provided");
			throw new BadCredentialsException(this.messages
					.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
		}
		String presentedPassword = authentication.getCredentials().toString();
		if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
			this.logger.debug("Failed to authenticate since password does not match stored value");
			throw new BadCredentialsException(this.messages
					.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
		}
	}
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException {
		prepareTimingAttackProtection();
		try {
			//执行ServiceImpl中重写了UserDetailsService的loadUserByUsername()
			UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
			if (loadedUser == null) {
				throw new InternalAuthenticationServiceException(
						"UserDetailsService returned null, which is an interface contract violation");
			}
			return loadedUser;
		}
}
protected UserDetailsService getUserDetailsService() {
		//获取到的实现了UserDetailsService的我们自定义的ServiceImpl对象
		return this.userDetailsService;
	}

自定义的ServiceImpl实现了UserDetailsService接口

@Service
public class UserServiceImpl implements UserService, UserDetailsService {
    @Autowired
    private UserBeanMapper userBeanMapper;


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据用户名从数据库获取密码
        UserBean user = userBeanMapper.selectByUserName(username);
        if(null==user){
            throw new UsernameNotFoundException("用户名不存在");
        }
        //根据用户id从数据库获取用户的权限
        List<String> pers = userBeanMapper.selectPermissionCodeByUserId(user.getUserId());
        //将获取到的用户权限一个个放进List<SimpleGrantedAuthority>中
        for(String per : pers){
            user.getPermission().add(new SimpleGrantedAuthority(per));
        }
        return user;
    }

在这里插入图片描述


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

相关文章:

  • jQuery位置方法
  • 【Cesium 编程第一篇】概述、环境搭建、界面介绍
  • 总结815
  • Android Textview Button 等基础组件学习
  • Java-红黑树的实现
  • Python 小型项目大全 76~81
  • 【数据结构】二叉树的概念及结构
  • 在线文章生成器-文章生成器在线生成
  • 文件小注意
  • 摩兽Pesgo plus首发爆卖,全网关注度破亿!中国潮玩跨骑电自浪潮已至?
  • 家装产业的数字化,正在成为越来越多人的新共识
  • 使用java实现斗地主小游戏
  • MATLAB 不同格式点云文件的读取与显示(1)
  • RCIE练习题2之BGP4+配置
  • 《Java设计模式学习》适配器模式
  • STL基础知识
  • 推荐几款程序员提升工作效率的必备工具
  • 【ChatGPT】预训练模型微调及其应用(ChatGLM-6B、duckduckgo_search、GPT在科研的应用等)
  • 23年5月高项学习笔记10 —— 采购管理
  • 双交叉注意学习用于细粒度视觉分类和目标重新识别
  • 重磅!阿里版本【ChatGPT】开放测评!
  • Thinkphp6.0服务系统
  • argparse参数总结(方便之后自己看)
  • 模板学堂|DataEase图表样式解析
  • 科技成果评价最新攻略,你确定不来看看?
  • Python实现Imagenet数据集的合并和拆分
  • 一篇文章让你搞懂TypeScript中的??和?:和?.和!.是什么意思
  • 风电的Weibull分布及光电的Beta分布组合研究(Matlab代码实现)
  • 开源后台管理系统解决方案 boot-admin 简介
  • 关于ChatGPT人工智能浅谈