SpringSecurity权限访问流程(源码剖析)

ExceptionTranslationFilter 过滤器

该过滤器是用于处理异常的,不需要我们配置,对于前端提交的请求会直接放行,捕获后续抛出的异常并进行处理(例如:权限访问限制)。具体源码如下:

image-20221111205806671

FilterSecurityInterceptor 过滤器

FilterSecurityInterceptor 是过滤器链的最后一个过滤器,该过滤器是过滤器链的最后一个过滤器,根据资源权限配置来判断当前请求是否有权限访问对应的资源。如果访问受限会抛出相关异常,最终所抛出的异常会由前一个过滤器ExceptionTranslationFilter 进行捕获和处理。具体源码如下:

image-20221111210309165

需要注意,Spring Security 的过滤器链是配置在 SpringMVC 的核心组件DispatcherServlet 运行之前。也就是说,请求通过 Spring Security 的所有过滤器,不意味着能够正常访问资源,该请求还需要通过 SpringMVC 的拦截器链。

SpringSecurity 认证流程(源码剖析)

ps:硅谷老师的笔记(代码背景白色)

SpringSecurity 基本流程

上:对应教程视频:36-尚硅谷-SpringSecurity-源码剖析-认证流程详解上_哔哩哔哩_bilibili

image-20221111185833955

绿色部分是认证过滤器,需要我们自己配置,可以配置多个认证过滤器。认证过滤器可以使用 Spring Security 提供的认证过滤器,也可以自定义过滤器(例如:短信验证)。认证过滤器要在 **configure(HttpSecurity http)**方法中配置,没有配置不生效。下面会重点介绍以下三个过滤器:

UsernamePasswordAuthenticationFilter 过滤器:该过滤器会拦截前端提交的 POST 方式的登录表单请求,并进行身份认证。

ExceptionTranslationFilter 过滤器:该过滤器不需要我们配置,对于前端提交的请求会直接放行,捕获后续抛出的异常并进行处理(例如:权限访问限制)。

FilterSecurityInterceptor 过滤器:该过滤器是过滤器链的最后一个过滤器,根据资源权限配置来判断当前请求是否有权限访问对应的资源。如果访问受限会抛出相关异常,并由 ExceptionTranslationFilter 过滤器进行捕获和处理。

SpringSecurity 认证流程(源码剖析)

image-20221111185759068

前端的post请求都会被UsernamePasswordAuthenticationFilter这个过滤器给拦截,并进行身份的认证

(1) 到父类AbstractAuthenticationProcessingFilter

​ 第一步:过滤的方法doFilter,判断提交方式是否post提交

image-20221111175238982

​ 第二步:调用子类UsernamePasswordAuthenticationFilterattemptAuthentication方法进行身份认证,认证成功之后,把认证信息封装到Authentication对象里面

Authentication用于封装认证信息的类对象

​ 在post提交表单之后,得到表单提交过来的数据,然后这里调用子类里的attemptAuthentication方法去查数据库和进行验证,认证之后,那认证通过后的信息用Authentication进行封装

image-20221111175602673

第三步:session策略处理

image-20221111180301213

第四步:

1、认证失败抛出异常,执行认证失败的方法

认证成功后会进行封装,但认证失败会,会在catch里面调用unsuccessfulAuthentication方法,做认证失败处理,比如提示用户你没有权限

image-20221111180616578

ps:网友的笔记(代码背景白色)

image-20221111185611776

2、认证成功后,调用认证成功后的方法

image-20221111181109625

ps:上面第二步:调用子类UsernamePasswordAuthenticationFilterattemptAuthentication方法去查数据库和进行验证的源码剖析:

attemptAuthentication中的

第一步:判断前端是否是post提交

第二步:获取前端表单提交数据

image-20221111182257781

image-20221111185415885

image-20221111185333746

第三步:

使用获取数据,构造成对象(UsernamePasswordAuthenticationToken),标记为未认证的状态

把请求一些属性信息设置到该对象里面( this.setDetails(request, authRequest); )

调用authenticate方法进行身份认证(调用我们自己编写的userDetailsService里面的方法进行查数据过程或认证过程)

image-20221111182712378

image-20221111190458033

(3)查看 UsernamePasswordAuthenticationToken 的构建过程

image-20221111185138507

下:对应教程视频:37-尚硅谷-SpringSecurity-源码剖析-认证流程详解下_哔哩哔哩_bilibili

(4)查看ProviderManager源码,认证实现

上述过程中,UsernamePasswordAuthenticationFilter 过滤器的attemptAuthentication() 方法的身份认证过程将未认证的 Authentication 对象传入ProviderManager 类的 authenticate() 方法进行身份认证。

ProviderManager 是 AuthenticationManager 接口的实现类,该接口是认证相关的核心接口,也是认证的入口。在实际开发中,我们可能有多种不同的认证方式,例如:用户名+密码、邮箱+密码、手机号+验证码等,而这些认证方式的入口始终只有一个,那就是AuthenticationManager。在该接口的常用实现类 ProviderManager 内部会维护一个**List**列表,存放多种认证方式,实际上这是委托者模式(Delegate)的应用。每种认证方式对应着一个 AuthenticationProvider,AuthenticationManager 根据认证方式的不同(根据传入的 Authentication 类型判断)委托对应的 AuthenticationProvider 进行用户认证。

image-20221111203614625

image-20221111203630089

image-20221111203653228

image-20221111203710806

上述认证成功之后的(6)过程,调用 CredentialsContainer 接口定义的eraseCredentials() 方法去除敏感信息。查看

UsernamePasswordAuthenticationToken 实现的 eraseCredentials() 方法,该方法实现在其父类中:

image-20221111203846591

上述过程就是认证流程的最核心部分,接下来重新回到

UsernamePasswordAuthenticationFilter 过滤器的 doFilter() 方法,查看认证成功/失败的处理:

image-20221111203923720

查看successfulAuthentication() 和 unsuccessfulAuthentication() 方法源码:

image-20221111204011293

image-20221111204101980

———————————————————————————————————————————————————————————————————————

活跃性-定位死锁 (如何查看、定位死锁)

参考视频教程:04.067-活跃性-定位死锁_哔哩哔哩_bilibili

方式1 :jstack工具

通过终端命令:jstack 进程号 (图上面写错了,不是进程的端口号,是进程号)

img点击并拖拽以移动编辑通过上面的运行后可以看到下面的 线程信息:

img

方式1的jstack工具可以查看死锁的位置(哪一行),然后让我们去修改相应的代码

方式2:jconsole工具

imgimg

img

点击之后img

(rabbitmq的高级特性)MQ集群

对应的教程视频:

高级篇Day5-04-MQ集群_哔哩哔哩_bilibili

1.集群分类

img

因为比较推荐使用仲裁队列,所以 普通集群和镜像集群就不做笔记的记录了

2.普通集群

3.镜像集群

3.仲裁队列

仲裁队列:仲裁队列是3.8版本以后才有的新功能,用来替代镜像队列,具备下列特征:

•与镜像队列一样,都是主从模式,支持主从数据同步

•使用非常简单,没有复杂的配置

•主从同步基于Raft协议,强一致

搭建(非常简单,不需要任何配置,直接在控制台添加一个队列就行):

在任意控制台添加一个队列,一定要选择队列类型为Quorum类型

img

img

img

img

(rabbitmq的高级特性)死信交换机

对应的教程视频:

高级篇Day5-02-死信交换机_哔哩哔哩_bilibili

1.初始死信交换机

img

总结:

什么样的消息会成为死信?

•消息被消费者reject或者返回nack

•消息超时未消费

•队列满了

如何给队列绑定死信交换机?

•给队列设置dead-letter-exchange属性,指定一个交换机

•给队列设置dead-letter-routing-key属性,设置死信交换机与死信队列的RoutingKey

2.TTL

img

imgimg

img 总结:

消息超时的两种方式是?

  • 给队列设置ttl属性,进入队列后超过ttl时间的消息变为死信

  • 给消息设置ttl属性,队列接收到消息超过ttl时间后变为死信

•两者共存时,以时间短的ttl为准

如何实现发送一个消息20秒后消费者才收到消息?

  • 给消息的目标队列指定死信交换机

  • 消费者监听与死信交换机绑定的队列

  • 发送消息时给消息设置ttl为20秒

3.延迟队列

img

延迟队列插件

因为延迟队列的需求非常多,所以RabbitMQ的官方也推出了一个插件,原生支持延迟队列效果。

详细安装过程略~~~(我也没装,只是做下笔记,具体怎么装看教程视频吧或是自行查询资料)

img

imgimg

img

img

总结:

延迟队列插件的使用步骤包括哪些?

•声明一个交换机,添加delayed属性为true

•发送消息时,添加x-delay头,值为超时时间

rabbitmq的高级特性

MQ的一些常见问题

  • **消息可靠性问题:**如何确保发送的消息至少被消费一次
  • **延迟消息问题:**如何实现消息的延迟投递
  • **消息堆积问题:**如何解决数百万消息堆积,无法及时消费的问题
  • **高可用问题:**如何避免单点的MQ故障而导致的不可用问题

一、消息可靠性

(78条消息) (rabbitmq的高级特性)消息可靠性_其然乐衣的博客-CSDN博客

二、死信交换机

(18条消息) (rabbitmq的高级特性)死信交换机_其然乐衣的博客-CSDN博客

三、惰性队列

(19条消息) (rabbitmq的高级特性)惰性队列_其然乐衣的博客-CSDN博客

四、MQ集群

(19条消息) (rabbitmq的高级特性)MQ集群_其然乐衣的博客-CSDN博客

(多级缓存)缓存同步

对应的教程视频:

高级篇Day4-04-缓存同步_哔哩哔哩_bilibili

一、数据同步策略

缓存数据同步的常见方式有三种:

设置有效期:给缓存设置有效期,到期后自动删除。再次查询时更新

•优势:简单、方便

•缺点:时效性差,缓存过期之前可能不一致

•场景:更新频率较低,时效性要求低的业务

同步双写:在修改数据库的同时,直接修改缓存

•优势:时效性强,缓存与数据库强一致

•缺点:有代码侵入,耦合度高;

•场景:对一致性、时效性要求较高的缓存数据

•**异步通知:**修改数据库时发送事件通知,相关服务监听到通知后修改缓存数据

•优势:低耦合,可以同时通知多个缓存服务

•缺点:时效性一般,可能存在中间不一致状态

•场景:时效性要求一般,有多个服务需要同步

img

img

二、安装和配置Canal

img

img

下面我们就开启mysql的主从同步机制,让Canal来模拟salve

1.开启MySQL主从

Canal是基于MySQL的主从同步功能,因此必须先开启MySQL的主从功能才可以。

这里以之前用Docker运行的mysql为例:

1.1.开启binlog

打开mysql容器挂载的日志文件,我的在/tmp/mysql/conf目录:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7I8WMYyf-1662205186188)(assets/image-20210813153241537.png)]

修改文件:

1
vi /tmp/mysql/conf/my.cnf

点击并拖拽以移动

添加内容:

1
2
log-bin=/var/lib/mysql/mysql-bin
binlog-do-db=heima

点击并拖拽以移动

配置解读:

  • log-bin=/var/lib/mysql/mysql-bin:设置binary log文件的存放地址和文件名,叫做mysql-bin
  • binlog-do-db=heima:指定对哪个database记录binary log events,这里记录heima这个库

最终效果:

1
2
3
4
5
6
7
[mysqld]
skip-name-resolve
character_set_server=utf8
datadir=/var/lib/mysql
server-id=1000
log-bin=/var/lib/mysql/mysql-bin
binlog-do-db=heima

点击并拖拽以移动

1.2.设置用户权限

接下来添加一个仅用于数据同步的账户,出于安全考虑,这里仅提供对heima这个库的操作权限。

1
2
3
create user canal@'%' IDENTIFIED by 'canal';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT,SUPER ON *.* TO 'canal'@'%' identified by 'canal';
FLUSH PRIVILEGES;

点击并拖拽以移动

重启mysql容器即可

1
docker restart mysql

点击并拖拽以移动

测试设置是否成功:在mysql控制台,或者Navicat中,输入命令:

1
show master status;

2.安装Canal

2.1.创建网络

我们需要创建一个网络,将MySQL、Canal、MQ放到同一个Docker网络中:

1
docker network create heima

点击并拖拽以移动

让mysql加入这个网络:

1
docker network connect heima mysql

点击并拖拽以移动

2.3.安装Canal

课前资料中提供了canal的镜像压缩包:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FvuASqcG-1662205186198)(assets/image-20210813161804292.png)]

大家可以上传到虚拟机,然后通过命令导入:

1
docker load -i canal.tar

点击并拖拽以移动

然后运行命令创建Canal容器:

1
2
3
4
5
6
7
8
9
10
11
docker run -p 11111:11111 --name canal \
-e canal.destinations=heima \
-e canal.instance.master.address=mysql:3306 \
-e canal.instance.dbUsername=canal \
-e canal.instance.dbPassword=canal \
-e canal.instance.connectionCharset=UTF-8 \
-e canal.instance.tsdb.enable=true \
-e canal.instance.gtidon=false \
-e canal.instance.filter.regex=heima\\..* \
--network heima \
-d canal/canal-server:v1.1.5

点击并拖拽以移动

说明:

  • -p 11111:11111:这是canal的默认监听端口
  • -e canal.instance.master.address=mysql:3306:数据库地址和端口,如果不知道mysql容器地址,可以通过docker inspect 容器id来查看
  • -e canal.instance.dbUsername=canal:数据库用户名
  • -e canal.instance.dbPassword=canal :数据库密码
  • -e canal.instance.filter.regex=:要监听的表名称

表名称监听支持的语法:

1
2
3
4
5
6
7
8
mysql 数据解析关注的表,Perl正则表达式.
多个正则之间以逗号(,)分隔,转义符需要双斜杠(\\)
常见例子:
1. 所有表:.* or .*\\..*
2. canal schema下所有表: canal\\..*
3. canal下的以canal打头的表:canal\\.canal.*
4. canal schema下的一张表:canal.test1
5. 多个规则组合使用然后以逗号隔开:canal\\..*,mysql.test1,mysql.test2

点击并拖拽以移动

三、监听Canal

img

Canal提供了各种语言的客户端,当Canal监听到binlog变化时,会通知Canal的客户端。不过这里我们会使用GitHub上的第三方开源的canal-starter。地址:GitHub - NormanGyllenhaal/canal-client: spring boot canal starter 易用的canal 客户端 canal client

img

img

img

(多级缓存)多级缓存

对应的教程视频: 高级篇Day4-03-多级缓存_哔哩哔哩_bilibili

一、安装OpenResty

OpenResty® 是一个基于 Nginx的高性能 Web 平台,用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。具备下列特点:

•具备Nginx的完整功能

•基于Lua语言进行扩展,集成了大量精良的 Lua 库、第三方模块

•允许使用Lua自定义业务逻辑、自定义库

官方网站: OpenResty® - 中文官方站

img

(74条消息) 安装OpenResty_其然乐衣的博客-CSDN博客

二、OpenResty快速入门

img

img

img

三、请求参数处理

img

img

四、查询Tomcat

img

img

img

img

img

img

img

img

五、Redis缓存预热

img

img

img

六、查询Redis缓存

img

img

img

七、Nginx本地缓存

img

img

img

img