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

【Shiro】Shiro 的学习教程(四)之 SpringBoot 集成 Shiro 原理

目录

  • 1、第一阶段:启动服务,构建类的功能
  • 2、第二阶段:正式请求

1、第一阶段:启动服务,构建类的功能

查看 Shiro 配置类 ShiroConfiguration

@Configuration
public class ShiroConfiguration {
    // 创建 shiroFilter,负责拦截所有请求
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //给filter设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        //配置系统受限资源
        //配置系统公共资源
        Map<String,String> map = new HashMap<>();
        // authc 请求这个资源需要认证和授权
        map.put("/login", "anon");
        map.put("/register", "anon");
        map.put("/user/register", "anon");
        map.put("/user/login", "anon");
        map.put("/index", "authc");
        //默认认证界面路径
        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }
}

配置了 ShiroFilterFactoryBean

查看 ShiroFilterFactoryBean实现了 FactoryBean 接口

public class ShiroFilterFactoryBean implements FactoryBean, BeanPostProcessor {

	private SecurityManager securityManager;
	// 用于自定义的 filter
    private Map<String, Filter> filters = new LinkedHashMap();
    // 用于自定义的过滤器链
    private Map<String, String> filterChainDefinitionMap = new LinkedHashMap();
    // 设置全局的 url
    private String loginUrl;
    private String successUrl;
    private String unauthorizedUrl;
    // 生成的实例
    private AbstractShiroFilter instance;
    
	// ...
	
	public Object getObject() throws Exception {
        if (this.instance == null) {
            this.instance = this.createInstance();
        }
        return this.instance;
    }
	
	protected AbstractShiroFilter createInstance() throws Exception {
        log.debug("Creating Shiro Filter instance.");
        FilterChainManager manager = this.createFilterChainManager();
        PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
        chainResolver.setFilterChainManager(manager);
        return new ShiroFilterFactoryBean.SpringShiroFilter((WebSecurityManager)securityManager, chainResolver);
    }
    
    // ...
}
  • Map<String, Filter> filterskey 是过滤器名称,value 是 Filter
  • Map<String, String> filterChainDefinitionMapkey 是 uri,value 是过滤器名称

Spring 容器会自动检测到 FactoryBean 接口,并调用其 getObject() 方法获取实际的 bean 对象,所以,最终会调用 createInstance() 方法。

createInstance() 方法:创建 FilterChainManager 对象、PathMatchingFilterChainResolverPathMatchingFilterChainResolver 设置了 FilterChainManager)对象,再创建了内部类 SpringShiroFilter 对象

  1. this.createFilterChainManager():创建 FilterChainManager
    • 创建 DefaultFilterChainManager 实例,并将默认过滤器、自定义过滤器存放到属性 Map<String, Filter> filters
    • 并给所有的过滤器设置 urlloginUrlsuccessUrlunauthorizedUrl
    • 构造过滤器链,并存放到属性 Map<String, NamedFilterList> filterChains,其中,key 为 uri,value 为 NamedFilterList (extends List<Filter>);并将 uri 存放在 PathMatchingFilter 类的 Map<String, Object> appliedPaths 属性

createFilterChainManager() 方法:

在这里插入图片描述

①:new DefaultFilterChainManager():管理 过滤器 filters(包括默认过滤器 + 自定义过滤器)、过滤器链

在这里插入图片描述
在构造器中,将默认的过滤器添加到 filters 属性中

默认过滤器 DefaultFilter:枚举类

public enum DefaultFilter {
    anon(AnonymousFilter.class),
    authc(FormAuthenticationFilter.class),
    authcBasic(BasicHttpAuthenticationFilter.class),
    authcBearer(BearerHttpAuthenticationFilter.class),
    logout(LogoutFilter.class),
    noSessionCreation(NoSessionCreationFilter.class),
    perms(PermissionsAuthorizationFilter.class),
    port(PortFilter.class),
    rest(HttpMethodPermissionFilter.class),
    roles(RolesAuthorizationFilter.class),
    ssl(SslFilter.class),
    user(UserFilter.class);
}

②:applyGlobalPropertiesIfNecessary(filter):设置 loginUrlsuccessUrlunauthorizedUrl

在这里插入图片描述

applyLoginUrlIfNecessary() 方法为例:

在这里插入图片描述
在这里插入图片描述

如果 ShiroFilterFactoryBean#loginUrl 有值且 AccessControlFilter#loginUrl 的值为默认值(/login.jsp),那么就会给 AccessControlFilter#loginUrl 设置值

③:处理自定义的过滤器 filter:将自定义过滤器添加到 DefaultFilterChainManager

在这里插入图片描述

④:getFilterChainDefinitionMap() & createChain():构造过滤器链

createChain() 方法:

在这里插入图片描述

注意下面这行代码:

String[] filterTokens = this.splitChainDefinition(chainDefinition);

将一个字符串可以转化为字符串数组,就可猜测是不是可以配置多个过滤器 map.put("/login", "anon, authc");

对此数组进行 for 循环:

String[] nameConfigPair = this.toNameConfigPair(token);
this.addToChain(chainName, nameConfigPair[0], nameConfigPair[1]);

又将一个字符串 token 转化为了一个 字符串数组,且通过 "["toNameConfigPair 内部实现)将数组分为了两部分

所以,最终可以配置成:

map.put("/login", "authc, roles[admin,user], perms[file:edit]");

addToChain() 方法:生成带有 name(过滤 uri)的过滤器链 NamedFilterList,并将过滤器添加其中(过滤 uri 和过滤器是一对多关系)

在这里插入图片描述

1、applyChainConfig() 方法:存放所有的过滤 uri

在这里插入图片描述

1-1、PathMatchingFilter#processPathConfig() 方法:

  • 属性
    • PatternMatcher pathMatcher:用于请求 uri 和已配置的 uri 进行配置
    • Map<String, Object> appliedPaths: 存放所有的过滤 uri

在这里插入图片描述

2、ensureChain(chainName) 方法:通过 uri 获取过滤器链,如果没有,则创建 SimpleNamedFilterList,并将此过滤器链放入 filterChains 属性中,返回;如果通过 uri 获取到过滤器链,则直接返回

在这里插入图片描述

2-1、SimpleNamedFilterList:其实就是一个有名字 name 的 List<Filter>

  • name:uri
  • List<Filter>:配置的过滤器

一个 uri 可以对应多个过滤器,所以,Filter 是一个 List 集合

在这里插入图片描述

  1. new PathMatchingFilterChainResolver():创建 PathMatchingFilterChainResolver,负责路径和过滤器链的解析、匹配,根据 url 找到过滤器链

2、第二阶段:正式请求

任何请求都会先经过 Shiro 先过滤,直到成功才会执行 web 本身的过滤器

一个请求过来时,先到达 AbstractShiroFilter#executeChain()方法,去根据 request 解析出来的 url 找到对应的过滤链,然后执行过滤器链。

1、executeChain() 方法如下:

在这里插入图片描述

1-1、getExecutionChain() 方法:这里的 resolver 就是 PathMatchingFilterChainResolver,根据 uri 获取过滤器链 ProxiedFilterChain

在这里插入图片描述

1-2、getChain() 方法:从属性 filterChains 循环匹配请求 uri,若匹配成功,则调用 filterChainManager.proxy();否则,返回 null

在这里插入图片描述

1-2-1、pathMatches() 方法:

在这里插入图片描述

1-2-1-1、AntPathMatcher#matches():使用 AntPathMatcher 进行匹配

在这里插入图片描述

doMatch():匹配 uri 和 requestUri,考虑到通配符 **

在这里插入图片描述

2、chain.doFilter(request, response);:执行过滤器链

在这里插入图片描述
在这里插入图片描述

过滤器类结构图:

在这里插入图片描述

最终调用 AdviceFilter#doFilterInternal() 方法:preHandle() 方法决定过滤器链是否执行

在这里插入图片描述

通过属性 appliedPaths 循环判断是否与当前请求的 uri 相匹配,如果通过,直接返回 true,否则,进入 onPreHandle() 方法判断:

在这里插入图片描述

onPreHandle() 方法供子类去重写:

在这里插入图片描述

AnonymousFilter:匿名访问。

在这里插入图片描述
任何对象访问都一直返回 true,表明任何用 AnonymousFilter 过滤的请求都不需要验证。因为它一直返回 true

AccessControlFilter:需要身份验证的过滤器

在这里插入图片描述

isAccessAllowed()onAccessDenied() 方法决定

  • isAccessAllowed():决定了当前请求的subject是否允许访问
  • onAccessDenied():在被拒绝访问时处理。AccessControlFilter 类有很多子类重载了该方法。以 FormAuthenticationFilter 类的onAccessDenied() 方法为例

在这里插入图片描述
如果是登陆请求,则执行登陆操作;否则,保存请求链接跳转到登陆请求界面

executeLogin() 方法:创建 token,调用 Subject#login() 方法

在这里插入图片描述
总结流程:

在这里插入图片描述


http://www.kler.cn/a/301348.html

相关文章:

  • 【Nginx从入门到精通】03 、安装部署-让虚拟机可以联网
  • Python蓝桥杯刷题1
  • java设计模式之 - 适配器模式
  • PySpark——Python与大数据
  • 传奇996_23——杀怪掉落,自动捡取,捡取动画
  • 华为开源自研AI框架昇思MindSpore应用案例:人体关键点检测模型Lite-HRNet
  • 海外云手机是否适合运营TikTok?
  • Kubernetes部署(haproxy+keepalived)高可用环境和办公网络打通
  • Java 21的Preferences API的笔记
  • 分布式中间件-几个常用的消息中间件
  • redis基本数据结构-hash
  • 数据分析-11-时间序列分析的概念任务和主要方法
  • 第R2周:LSTM-火灾温度预测
  • C语言——希尔排序
  • Qt什么时候触发paintEvent事件
  • 【论文笔记】NDT: Neural Data Transformers (NBDT, 2022)
  • 一些深度学习相关指令
  • 【Qt】按钮样式--按钮内部布局(调整按钮文本和图标放置在任意位置)
  • 上海亚商投顾:沪指探底回升 华为产业链午后爆发
  • 【深度学习讲解笔记】第1章-机器学习基础(3)
  • Oracle Data Guard:Oracle数据库的高可用性和灾难恢复解决方案
  • 最近试用了FunHPC-AI宝箱-ComfyUI-Plus,使用了dreamshaperXL全能模型,生成了几张国风图,效果真的让人惊叹!
  • 安装MongoDB
  • 企业数字化转型、建设和升级面临的主要难题和解决之道(2)
  • 【机器学习】模型性能与可解释性的矛盾以及如何提高可解释性和模型性能
  • Java设计模式之命令模式介绍和案例示范