一、先从配置类入手,主要是@Bean了一个ShiroFilterFactoryBean:

@Data
@Configuration
@Slf4j
@EnableConfigurationProperties(ShiroProperties.class)
public class ShiroConfiguration { //.......这里省略了大部分跟主题无关的配置代码 /**
* 这里名字叫 shiroFilter,后续通过FilterRegistrationBean从spring容器中向servlet容器中注册filter
* @param securityManager
* @return
* @author
* @create
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean getShiroFilterFactoryBean (DefaultWebSecurityManager securityManager,
ShiroProperties sp,IUserService userService){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
//配置shiroFilter子过滤器
loadFilter(shiroFilterFactoryBean,getCasFilter(sp,userService),getJwtFilter(),getWeComFilter());
//配置拦截规则
loadShiroFilterChain(shiroFilterFactoryBean, sp);
return shiroFilterFactoryBean;
}   /**
* CAS过滤器
*/
@Bean
public CustomizedCasFilter getCasFilter(ShiroProperties sp, IUserService userService) {
CustomizedCasFilter casFilter = new CustomizedCasFilter();
//.......
return casFilter;
} /**
* JWT过滤器
*
* @return
* @author
* @create 2020年8月19日
*/
@Bean
public CustomizedJwtFilter getJwtFilter() {
CustomizedJwtFilter jwtFilter = new CustomizedJwtFilter();
return jwtFilter;
} /**
* 企业微信过滤器
* @param
* @return
*/
@Bean
public CustomizedWeComFilter getWeComFilter() {
CustomizedWeComFilter weComFilter = new CustomizedWeComFilter();
return weComFilter;
} /**
* 添加过滤器
* @param bean
* @param casFilter
* @param jwtFilter
*/
private void loadFilter(ShiroFilterFactoryBean bean,CasFilter casFilter,
CustomizedJwtFilter jwtFilter,
CustomizedWeComFilter weComFilter) {
Map<String, Filter> filters = new HashMap<>();
filters.put("cas", casFilter);
filters.put("jwt", jwtFilter);
filters.put("weCom",weComFilter);
bean.setFilters(filters);
} /**
* 加载shiroFilter权限控制规则
*
* @author
* @create
*/
private void loadShiroFilterChain (ShiroFilterFactoryBean shiroFilterFactoryBean, ShiroProperties sp){
//下面这些规则配置最好配置到配置文件中
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// shiro集成cas后,首先添加该规则
filterChainDefinitionMap.put("/api/**", "jwt");
filterChainDefinitionMap.put("/cas/**", "cas");
filterChainDefinitionMap.put("/wecom/**", "weCom");
filterChainDefinitionMap.put("/test/**", "anon");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
} /**
* 在spring容器处在某一声明周期时时,为shiro中的相关bean执行对应的bean生命周期方法
*
* @author
* @create
*/
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor () {
return new LifecycleBeanPostProcessor();
} .......
}

 

二、ShiroFilterFactoryBean(源码解析,代码中注释带序号的为代码执行顺序)-- 向Spring容器中注入了SpringShiroFilter:

public class ShiroFilterFactoryBean implements FactoryBean, BeanPostProcessor {
private static final transient Logger log = LoggerFactory.getLogger(ShiroFilterFactoryBean.class);
private SecurityManager securityManager;
//这里是我们向注入的多个filter 见配置类中的loadFilter()方法
private Map<String, Filter> filters = new LinkedHashMap();
private List<String> globalFilters = new ArrayList();
//见配置类中loadShiroFilterChain
private Map<String, String> filterChainDefinitionMap;
private String loginUrl;
private String successUrl;
private String unauthorizedUrl;
private AbstractShiroFilter instance; public ShiroFilterFactoryBean() {
this.globalFilters.add(DefaultFilter.invalidRequest.name());
this.filterChainDefinitionMap = new LinkedHashMap();
} public SecurityManager getSecurityManager() {
return this.securityManager;
} ...... public Map<String, Filter> getFilters() {
return this.filters;
} public Map<String, String> getFilterChainDefinitionMap() {
return this.filterChainDefinitionMap;
} ...... // 1、 这里会在spring容器加载的时候调用,向spring容器中注入getObject()返回的对象
public Object getObject() throws Exception {
if (this.instance == null) {
//2、调用createInstance()方法
this.instance = this.createInstance();
} return this.instance;
} public Class getObjectType() {
return ShiroFilterFactoryBean.SpringShiroFilter.class;
} public boolean isSingleton() {
return true;
} protected FilterChainManager createFilterChainManager() {
DefaultFilterChainManager manager = new DefaultFilterChainManager();
    
Map<String, Filter> defaultFilters = manager.getFilters();
Iterator var3 = defaultFilters.values().iterator(); while(var3.hasNext()) {
Filter filter = (Filter)var3.next();
this.applyGlobalPropertiesIfNecessary(filter);
}      //4、在这个地方获取了shiroFilter中的子过滤器,即我们在配置类中设置的loadFilter()
Map<String, Filter> filters = this.getFilters();
String name;
Filter filter;
if (!CollectionUtils.isEmpty(filters)) {        //5、这个地方迭代,并将子过滤器添加到了manager中
for(Iterator var10 = filters.entrySet().iterator(); var10.hasNext(); manager.addFilter(name, filter, false)) {
Entry<String, Filter> entry = (Entry)var10.next();
name = (String)entry.getKey();
filter = (Filter)entry.getValue();
this.applyGlobalPropertiesIfNecessary(filter);
if (filter instanceof Nameable) {
((Nameable)filter).setName(name);
}
}
} manager.setGlobalFilters(this.globalFilters);
Map<String, String> chains = this.getFilterChainDefinitionMap();
if (!CollectionUtils.isEmpty(chains)) {
Iterator var12 = chains.entrySet().iterator(); while(var12.hasNext()) {
Entry<String, String> entry = (Entry)var12.next();
String url = (String)entry.getKey();
String chainDefinition = (String)entry.getValue();
          //这里很重要 后面DefaultFilterChainManager filterChains属性在这里添加值
manager.createChain(url, chainDefinition);
}
}      //这里很重要 后面DefaultFilterChainManager filterChains属性在这里添加值
manager.createDefaultChain("/**");
return manager;
} protected AbstractShiroFilter createInstance() throws Exception {
log.debug("Creating Shiro Filter instance.");
SecurityManager securityManager = this.getSecurityManager();
String msg;
if (securityManager == null) {
msg = "SecurityManager property must be set.";
throw new BeanInitializationException(msg);
} else if (!(securityManager instanceof WebSecurityManager)) {
msg = "The security manager does not implement the WebSecurityManager interface.";
throw new BeanInitializationException(msg);
} else {    //3、调用createFilterChainManager()
FilterChainManager manager = this.createFilterChainManager();
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver(); //6、将manager注入到chainResolver中,此时的manager有子过滤器,因而chainResolver也是可以获取到子过滤器的
chainResolver.setFilterChainManager(manager);        //7、返回SpringShiroFilter对象,即向Spring容器中注入SpringShiroFilter bean对象(带chain)
return new ShiroFilterFactoryBean.SpringShiroFilter((WebSecurityManager)securityManager, chainResolver);
}
} //.........删除无关代码 private void applyGlobalPropertiesIfNecessary(Filter filter) {
this.applyLoginUrlIfNecessary(filter);
this.applySuccessUrlIfNecessary(filter);
this.applyUnauthorizedUrlIfNecessary(filter);
} public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Filter) {
log.debug("Found filter chain candidate filter '{}'", beanName);
Filter filter = (Filter)bean;
this.applyGlobalPropertiesIfNecessary(filter);
this.getFilters().put(beanName, filter);
} else {
log.trace("Ignoring non-Filter bean '{}'", beanName);
} return bean;
} public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
} private static final class SpringShiroFilter extends AbstractShiroFilter {
protected SpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
if (webSecurityManager == null) {
throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
} else {
this.setSecurityManager(webSecurityManager);
if (resolver != null) {
this.setFilterChainResolver(resolver);
} }
}
}
}

三、FilterRegistrationBean:上面两步已经将BeanName为shiroFilter的SpringShiroFilter注入到了Spring容器中,现在来说一下是如何从Spring容器中注册到Servlet容器中的。

一般我们在Spring中配置Filter是这样写的:

@Configuration
public class FilterConfig {
/**
* 注册DelegatingFilterProxy(Shiro)
*
* @return
* @author
* @create 2020年8月19日
*/
@Bean
public FilterRegistrationBean shiroFilterRegistrationBean () {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
// 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
filterRegistration.addInitParameter("targetFilterLifecycle", "true");
filterRegistration.setEnabled(true);
filterRegistration.addUrlPatterns("/*");
return filterRegistration;
}
}

他具体的工作原理是什么?

1、Spring容器在启动时会调用org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext的selfInitialize(ServletContext servletContext)方法。
    private void selfInitialize(ServletContext servletContext) throws ServletException {
this.prepareWebApplicationContext(servletContext);
this.registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(this.getBeanFactory(), servletContext);
Iterator var2 = this.getServletContextInitializerBeans().iterator(); while(var2.hasNext()) {
ServletContextInitializer beans = (ServletContextInitializer)var2.next();
beans.onStartup(servletContext);
} }

2、bean.onStartup(servletContext)调用 RegistrationBean.onStartup()方法,该方法又调用抽象方法register()。

 public final void onStartup(ServletContext servletContext) throws ServletException {
String description = this.getDescription();
if (!this.isEnabled()) {
logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
} else {
this.register(description, servletContext);
}
} protected abstract void register(String description, ServletContext servletContext);

3、register()方法由RegistrationBean的子类DynamicRegistrationBean实现,并调用由子类AbstractFilterRegistrationBean实现的addRegistration()方法。

  protected final void register(String description, ServletContext servletContext) {
D registration = this.addRegistration(description, servletContext);
if (registration == null) {
logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
} else {
this.configure(registration);
}
} protected abstract D addRegistration(String description, ServletContext servletContext);

4、AbstractFilterRegistrationBean的addRegistration()方法,向ServletContext中注册了一个过滤器。而这个过滤器是子类提供的,也就是我们在Spring中注入的FilterRegistrationBean提供的,他的getFilter()方法

返回的就是我们的shiroFilter 。

AbstractFilterRegistrationBean:

   protected Dynamic addRegistration(String description, ServletContext servletContext) {
//调用子类实现的抽象方法
Filter filter = this.getFilter();
//向servletContext中注册Filter
return servletContext.addFilter(this.getOrDeduceName(filter), filter);
}
//这个方法由子类实现
public abstract T getFilter();

FilterRegistrationBean:

public class FilterRegistrationBean<T extends Filter> extends AbstractFilterRegistrationBean<T> {
private T filter; public FilterRegistrationBean() {
super(new ServletRegistrationBean[0]);
} public FilterRegistrationBean(T filter, ServletRegistrationBean<?>... servletRegistrationBeans) {
super(servletRegistrationBeans);
Assert.notNull(filter, "Filter must not be null");
this.filter = filter;
} public T getFilter() {
return this.filter;
} public void setFilter(T filter) {
Assert.notNull(filter, "Filter must not be null");
this.filter = filter;
}
}

至此,shiroFilter从Spring容器中注册到了Servlet容器中了。

SpringBoot整合shiro系列-SpingBoot是如何将shiroFilter注册到servlet容器中的的更多相关文章

  1. SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - 自定义Servlet、Filter、Listener是如何注册到Tomcat容器中的?(SpringBoot实现SpringMvc的原理)

    上一篇我们讲了SpringBoot中Tomcat的启动过程,本篇我们接着讲在SpringBoot中如何向Tomcat中添加Servlet.Filter.Listener 自定义Servlet.Filt ...

  2. 补习系列(6)- springboot 整合 shiro 一指禅

    目标 了解ApacheShiro是什么,能做什么: 通过QuickStart 代码领会 Shiro的关键概念: 能基于SpringBoot 整合Shiro 实现URL安全访问: 掌握基于注解的方法,以 ...

  3. SpringBoot系列十二:SpringBoot整合 Shiro

    声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅. 1.概念:SpringBoot 整合 Shiro 2.具体内容 Shiro 是现在最为流行的权限认证开发框架,与它起名的只有最初 ...

  4. SpringBoot整合Shiro+MD5+Salt+Redis实现认证和动态权限管理(上)----筑基中期

    写在前面 通过前几篇文章的学习,我们从大体上了解了shiro关于认证和授权方面的应用.在接下来的文章当中,我将通过一个demo,带领大家搭建一个SpringBoot整合Shiro的一个项目开发脚手架, ...

  5. SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建

    SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建 技术栈 : SpringBoot + shiro + jpa + freemark ,因为篇幅原因,这里只 ...

  6. 转:30分钟了解Springboot整合Shiro

    引自:30分钟了解Springboot整合Shiro 前言:06年7月的某日,不才创作了一篇题为<30分钟学会如何使用Shiro>的文章.不在意之间居然斩获了22万的阅读量,许多人因此加了 ...

  7. springboot整合Shiro功能案例

    Shiro 核心功能案例讲解 基于SpringBoot 有源码 从实战中学习Shiro的用法.本章使用SpringBoot快速搭建项目.整合SiteMesh框架布局页面.整合Shiro框架实现用身份认 ...

  8. SpringBoot整合Shiro实现权限控制,验证码

    本文介绍 SpringBoot 整合 shiro,相对于 Spring Security 而言,shiro 更加简单,没有那么复杂. 目前我的需求是一个博客系统,有用户和管理员两种角色.一个用户可能有 ...

  9. SpringBoot 整合Shiro 一指禅

    目标 了解ApacheShiro是什么,能做什么: 通过QuickStart 代码领会 Shiro的关键概念: 能基于SpringBoot 整合Shiro 实现URL安全访问: 掌握基于注解的方法,以 ...

随机推荐

  1. java连接sql server--关于登录验证及对数据库增删改查应用

    一:步骤## 1.sql server建立数据库和相关表 2.建立数据源  (1).打开控制面板找到管理,打开ODBC选项或直接搜索数据源  (2).打开数据源配置后点击添加,选择sql server ...

  2. 「HTML+CSS」--自定义加载动画【007】

    前言 Hello!小伙伴! 首先非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出- 哈哈 自我介绍一下 昵称:海轰 标签:程序猿一只|C++选手|学生 简介:因C语言结识编程,随后转入计算机 ...

  3. Apache Hudi 0.8.0版本重磅发布

    1. 重点特性 1.1 Flink集成 自从Hudi 0.7.0版本支持Flink写入后,Hudi社区又进一步完善了Flink和Hudi的集成.包括重新设计性能更好.扩展性更好.基于Flink状态索引 ...

  4. 【剑指offer】10:矩形覆盖

    题目描述: 我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形.请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? 解题思路: ①方法一 对于这种题没有思路怎么办?可以先从最 ...

  5. Mediapipe 在RK3399PRO上的初探(一)(编译、运行CPU和GPU Demo, RK OpenglES 填坑,编译bazel)

    PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明   本文作为本人csdn blog的主站的备份.(Bl ...

  6. Kubernetes部署metrics-server提示健康检测报错500,简单解决方式

    为什么写? 最近有项目要用到HPA(Horizontal Pod Autoscaler)依赖了k8s的 metrics 指标才能做出自动缩扩容的动作,我这边用官方GitHub v0.4.2版本启动不起 ...

  7. redhat 7.6 部署禅道 yum [Errno 14] curl#37 - "Couldn't open file /mnt/repodata/repomd.

    记个流水账 redhat 7.6 上部署 禅道.  禅道官网下载 http://dl.cnezsoft.com/zentao/9.8.3/ZenTaoPMS.9.8.3.zbox_64.tar.gz ...

  8. Go-22-方法

    方法 Go语言同时有函数和方法,方法的本质是函数,但是方法和函数又有所不同. 函数(function)是一段具有独立功能的代码,可以被反复多次调用,从而实现代码复用. 方法(method)是一个类的行 ...

  9. HUAWEI AppGallery Connect 正式发布移动端App,随时随地掌握应用动态

    华为应用市场AppGallery Connect应用一站式服务平台正式发布移动端App,帮助您随时随地查看应用信息,获取运营分析数据,接收重要消息通知,快速回复用户评论等,提升应用的运营管理效率,更便 ...

  10. 基于.Net Core 5.0 Worker Service 的 Quart 服务

    前言 看过我之前博客的人应该都知道,我负责了相当久的部门数据同步相关的工作.其中的艰辛不赘述了. 随着需求的越来越复杂,最近windows的计划任务已经越发的不能满足我了,而且计划任务毕竟太弱智,总是 ...