【权限管理】Spring Security 执行流程
转自:https://blog.csdn.net/weixin_37689658/article/details/92752890
1、基本配置使用
(1)创建配置类
创建一个配置类SecurityConfig继承自WebSecurityConfigurerAdapter,重写里面的configure(HttpSecurity http)这个方法,配置好需要认证的登录url,以及提交表单的url,这里除了登录url不需要认证之外,其他的url都需要认证才能访问,并且formLogin表名这是一个表单提交,loginprocessingUrl中是设置的提交登录表单的url。
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- .formLogin()
- .loginPage("/authentication/require") //指定没有认证时跳转到的认证url
- .loginProcessingUrl("/authentication/form") //提交登录表单的url
- .and()
- .authorizeRequests()
- .antMatchers("/authentication/require").permitAll()
- .anyRequest()
- .authenticated()
- .and()
- .csrf().disable();
- }
(2)获取认证信息
我们需要继承一个UserDetailSevice接口并且加入到容器中,实现loadUserByUsername方法,里面的逻辑通常是从数据库查找出对应用户名的密码然后构造一个UserDetail对象,spring security会根据返回的这个带有正确用户信息的对象和前台传过来的用户名密码进行比对来判断是否认证通过。
- /**
- * 表单登录的时候会调用loadUserByUsername来验证前端传过来的账号密码是否正确
- */
- @Component
- public class MyUserDetailService implements UserDetailsService {
- private Logger logger = LoggerFactory.getLogger(MyUserDetailService.class);
- @Autowired
- private PasswordEncoder passwordEncoder;
- @Override
- public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
- logger.info("登录用户名===========" + username);
- //这里需要去数据库查询用户的账号密码来比对是否正确,以及账号是否过期等等
- String password = passwordEncoder.encode("123456");
- logger.info("数据库密码是==============" + password);
- return new User(username, password,
- true,
- true,
- true,
- true,
- AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
- }
- }
这样配置好的话,比如说我们访问一个一个http://localhost:8080/user的接口,那么spring security 发现配置中这个接口是需要拦截的,并且当前的请求还没有通过认证,就会重定向到loginPage设置的这个接口或者页面中去。当我们调用loginProcessingUrl这个接口去提交表单的时候,如果通过了认证,那么就会重定向到原来我们想要访问的接口中去了,如果认证不成功,那么就出现认证失败等信息。
那么对于这个过程,spring security内部的流程到底是怎样的尼?我们先通过一张图来看。
我们可以看到在spring security内部其实是通过一个过滤器链来实现认证流程的,比如说这里的UsernamePasswordAuthenticationFilter就是拦截我们通过表单提交接口提交的用户名和密码,如果是Basic提交的话,就会被BasicAuthenticationFilter拦截,最后的橙色FilterSecurityInterceptor是首先判断我们当前请求的url是否需要认证,如果需要认证,那么就看当前请求是否已经认证,是的话就放行到我们要访问的接口,否则重定向到认证页面。
2、认证流程
这里已表单提交为例,当我们提交表单时,UsernamePasswordAuthenticationFilter首先会拦截请求,而UsernamePasswordAuthenticationFilter是继承于AbstractAuthenticationProcessingFilter的,在这个抽象类中已经定义好了doFilter的方法,而里面有一个attemptAuthentication方法是由子类实现的。所以当提交表单时spring security会发现这个一个表单提交,然后就调用了UsernamePasswordAuthenticationFilter的doFilter方法
然后我们来看 UsernamePasswordAuthenticationFilter里面重写的这个方法
然后我们可以看到首先是创建了一个UsernamePasswordAuthenticationToken对象,把用户名和密码传进去,再调用了getAuthenticationManager().authentication()方法,并把 UsernamePasswordAuthenticationToken对象传进去,那么这个AuthenticationManager是什么尼,其实它是一个接口
我们这里要看的是它的实现类ProviderManager,进去找到它的authenticate方法
其实这个ProviderManager就是管理所有的Provider对象的,通过for循环遍历找到适合的provider对象来调用其authenticate方法,那么provider对象又是什么东西?其实它也是一个接口
可以看到它的实现类有很多,我们这里主要来看DaoAuthenticationProvider这个实现类,但是在这个类中并没有找到authenticate方法,那么我们来它的父类AbstractUserDetailAuthenticationProvider里面来看下
里面调用了一个子类实现的方法,我们看这个子类的这个方法l
里面拿到我们之前加入到容器中的userDetailService然后调用loadUserByUsername方法拿到我们包含数据库用户信息的UserDetail对象,并且捕获用户名找不到等异常,所以这个方法就是来获取我们之前定义的userDetailService返回的UserDetail。
回到其父类中去,父类拿到了UserDetail对象,对这个对象进行了一系列的判断
首先我们来看下前置判断
里面就是调用了UserDetail对象一些实现方法进行判断,比如说账户是否锁定,账户是否能用,账户是否过期的判断,判断通过后来到了密码判断
在判断了密码是否正确之后,后置判断就是来判断密码是否过期了,
前置判断判断账户异常问题,然后到密码判断,最后到密码是否过期判断,因为你登录先看你账户是否有异常才能,有异常的话就不进行密码判断了,密码正确也要看密码是够过期 ,所以这三个操作合乎常理。
判断都没问题之后,最后返回了一个authentication对象,这里把用户名,密码,权限都传到UsernamePasswordAuthenticationToken中去,因为所有判断都没问题,这些信息都是正确的了。
3.认证结果如何在多个线程中共享?
首先,我们还是先来看一张图
当请求过来的时候都会最先经过一个叫SecurityContextPersistenceFilter的过滤器,这个过滤器的作用就是在请求经过的时候检查session中是否有认证的authentication对象,如果有的话就把它放进一个叫securityContext ,然后再交给securityContextHolder处理
看一下securityContext
它是一个接口,由子类SecurityContextImpl实现,里面就是一个authentication属性,所以它的作用就是包装了authentication
接下来再看一下securityContextHolder
里面的getContext,clearContext方法都是使用的一个叫 SecurityContextHolderStrategy对象,点进去看一下这个对象
我们发现它又是一个接口,我们看它的实现类ThreadLocalSecurityContextHolderStrategy
里面只有一个ThreadLocal对象,放的是SecurityContext,我们知道ThreadLocal有线程隔离的作用,每一个对象在ThreadLocal中都是线程级别的。
接着我们回到securityContextpersistenceFilter中去看里面的doFilter方法
有一个loadContext方法是加载出来SecurityContext对象的,那么它是怎么加载的?点进去
它是一个接口,看它的实现类HttpSessionSecurityContextRepository
所以上面请求经过securityContextpersistenceFilter就是先从判断session是否有SecurityContext,有的话就放进当前线程中
那么响应的时候做什么?这里直接给出
那么认证成功后,是谁把认证对象放在放在当前请求中的?答案就是FilterSecurityInterceptor,FilterSecurityInterceptor会判断当前请求的url是否要拦截,要拦截的话如果当前用户没有经过认证,那么就跳转到认证页面,如果认证了就直接放行,并且把认证信息放在当前线程中。
总的流程:一个请求过来时通过各个过滤器,最后通过FilterSecurityInterceptor来判断这个请求url是否是不需要验证的,如果是
就直接访问到我们的接口api,如果不是的话,再判断当前请求线程中是否有authentication的认证对象,如果有就放行,如果没有就返回登录页面(比如这里我们设置的是登录表单的方式),来到登录页面输入账号密码登录后就会来到 UsernamePasswordAuthenticationFilter,经过一系列的操作,最后验证成功就会把认证对象authentication放进securityContext中,然后FilterSecurityInterceptor判断到当前请求线程中这个认证对象就放行,返回的时候最后会通过securityContextpersistenceFilter,判断当前线程是否有securityContext,如果有就放进session,那么下次再请求这个url的时候会首先通过securityContextpersistenceFilter这个过滤器,判断session中是否有securityContextduxiiang,如果有就放进当前请求线程中,然后最后经过FilterSecurityInterceptor时再判断当前请求线程是否有认证对象,由于最前面经过securityContextpersistenceFilter,已经从session中把认证对象放进了当前请求线程中,所以FilterSecurityInterceptor会直接放行,这样就访问到我们的接口api。
【权限管理】Spring Security 执行流程的更多相关文章
- 03 spring security执行流程分析
spring security主要是依赖一系列的Filter来实现权限验证的,责任链设计模式是跑不了的.下面简单记录一下spring操作这些Filter的过程. 1. WebSecurityConfi ...
- 一文读懂Spring MVC执行流程
说到Spring MVC执行流程,网上有很多这方面的文章介绍,但是都不太详细,作为一个初学者去读会有许多不理解的地方,今天这篇文章记录一下我学习Spring MVC的心得体会 话不多说,先上图: ...
- Java——一文读懂Spring MVC执行流程
说到Spring MVC执行流程,网上有很多这方面的文章介绍,但是都不太详细,作为一个初学者去读会有许多不理解的地方,今天这篇文章记录一下我学习Spring MVC的心得体会 话不多说,先上图: Sp ...
- spring boot:spring security用mysql实现动态权限管理(spring boot 2.3.3)
一,动态权限管理的优点和缺点 1,优点: 因为控制权限的数据保存在了mysql或其他存储系统中, 可以动态修改权限控制,无需改动代码和重启应用, 权限变更时灵活方便 2,缺点: 权限的设置需要保存在 ...
- spring boot:spring security用mysql数据库实现RBAC权限管理(spring boot 2.3.1)
一,用数据库实现权限管理要注意哪些环节? 1,需要生成spring security中user类的派生类,用来保存用户id和昵称等信息, 避免页面上显示用户昵称时需要查数据库 2,如果需要在页面上显示 ...
- 权限开发 spring security 3.0.7 序列1 数据库脚本
spring security 3 细粒度权限控制第一篇,关于权限的初始化测试数据库脚本. 空间脚本: drop user FrameworkTest cascade; drop tablespa ...
- 使用IDEA搭建一个Spring + AOP (权限管理 ) + Spring MVC + Mybatis的Web项目 (零配置文件)
前言: 除了mybatis 不是零配置,有些还是有xml的配置文件在里面的. 注解是Spring的一个构建的一个重要手段,减少写配置文件,下面解释一下一些要用到的注解: @Configuration ...
- Spring Security认证流程分析--练气后期
写在前面 在前一篇文章中,我们介绍了如何配置spring security的自定义认证页面,以及前后端分离场景下如何获取spring security的CSRF Token.在这一篇文章中我们将来分析 ...
- spring security执行流程图
今天看到非常多人转载了这篇文章,这里备注一下.原文来自CSDN我的博客. 近期在研究spring security的配置,研究了一个星期了,在官网看了下.仅仅配置出来了简单的登录,但不知如何从数据库读 ...
随机推荐
- C语言:Unicode字符集
Unicode 也称为统一码.万国码:看名字就知道,Unicode 希望统一所有国家的字符编码.Unicode 于 1994 年正式公布第一个版本,现在的规模可以容纳 100 多万个符号,是一个很大的 ...
- matlab——线性规划
@ 目录 前言 一.基本概念 二.matlab实现 1.常用函数 2.常见变形 参考书目 前言 线性规划是数学规划中的一个重要分支,常用于解决如何利用现有资源来安排生产,以取得最大经济效益的问题.本文 ...
- JPA用法中字段起名规范
前两天在学习Springboot使用JPA 来操作数据库时,碰到一个问题,最终发现了JPA写法中表字段名称要写规范. 记录下来提醒自己. CityEntity是一个City的实体类. 1 @Table ...
- aria2+uget+chrome
1.安装aira2 sudo apt install aria2 2.安装及配置uget sudo apt install uget 编辑 -> 设置: 分类 -> 属性: 3.chrom ...
- 小程序框架WePY 从入门到放弃踩坑合集
小程序框架WePY 从入门到放弃踩坑合集 一点点介绍WePY 因为小程序的语法设计略迷, 所以x1 模块化起来并不方便, 所以x2 各厂就出了不少的框架用以方便小程序的开发, 腾讯看到别人家都出了框架 ...
- Dubbo的优雅下线原理分析
文/朱季谦 Dubbo如何实现优雅下线? 这个问题困扰了我一阵,既然有优雅下线这种说法,那么,是否有非优雅下线的说法呢? 这,还真有. 可以从linux进程关闭说起,其实,我们经常使用到杀进程的指令背 ...
- SpringCloud学习之【NACOS实现服务的注册与发现】
根据nacos官方的介绍,Nacos 致力于帮助您发现.配置和管理微服务.Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现.服务配置.服务元数据及流量管理. 具有服务发现和服务健康监 ...
- 前端基础EL表达式(八)
一.什么是EL表达式? 1.什么是EL表达式? EL(Expression Language) 是为了使JSP写起来更加简单.表达式语言的灵感来自于 ECMAScript 和 XPath 表达式语言, ...
- selenium 鼠标,键盘操作
1.鼠标操作 导包:from selenium.webdriver.common.action_chains import ActionChains 1.context_click() ...
- 流畅的python学习1
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Mon Apr 20 21:08:08 202 ...