IT小栈

  • 主页
  • Java基础
  • RocketMQ
  • Kafka
  • Redis
  • Shiro
  • Spring
  • Spring Boot
  • Spring Cloud
  • 资料链接
  • 关于
所有文章 友链

IT小栈

  • 主页
  • Java基础
  • RocketMQ
  • Kafka
  • Redis
  • Shiro
  • Spring
  • Spring Boot
  • Spring Cloud
  • 资料链接
  • 关于

Shiro工作原理

2019-07-28

本节我们接着上一章节分析下Shiro的工作原理,初始化过程及如何与Spring进行整合使用的。

1、DelegatingFilterProxy

上一章节我们配置了一些参数,我们知道web应用启动时,首先需要找到web.xml用来初始化配置信息比如Welcome页面、servlet、servlet-mapping、filter、listener、启动加载级别等。我们在里面配置了过滤器shiroFilter我们发现其过滤器类是org.springframework.web.filter.DelegatingFilterProxy,这是spring提供的DelegatingFilterProxy作为通用Filter代理类,用这个类的好处是可以通过spring容器来管理filter的生命周期,还有就是,可以通过spring注入的形式,来代理一个filter执行。

1
2
3
4
5
<!— shiro的过滤器,DelegatingFilterProxy 通过代理模式将spring容器中的bean和filter关联-->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

查看org.springframework.web.filter.DelegatingFilterProxy类中的源码

这个代理过滤器DelegatingFilterProxy继承自GenericFilterBean,间接的实现了javax.servlet.Filter接口。既然是Filter,我们只需要关注两个方法即可,一个是init方法,在过滤器被tomcat初始化的时候被调用;另一个就是doFilter方法,tomcat会将客户端的请求交给拦截器的doFilter方法处理。

1.1、init()

init方法需要看它的父类GenericFilterBean,因为父类GenericFilterBean实现了Filter接口的init方法,而子类DelegatingFilterProxy并没有重写该方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public final void init(FilterConfig filterConfig) throws ServletException {
Assert.notNull(filterConfig, "FilterConfig must not be null");
if (this.logger.isDebugEnabled()) {
this.logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");
}

this.filterConfig = filterConfig;
try {
PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
} catch (BeansException ex) {
String msg = "Failed to set bean properties on filter '" + filterConfig.getFilterName() + "': "
+ ex.getMessage();
this.logger.error(msg, ex);
throw new NestedServletException(msg, ex);
}

initFilterBean();

if (this.logger.isDebugEnabled())
this.logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");
}

执行了DelegatingFilterProxy中的方法initFilterBean()

1
2
3
4
5
6
7
8
9
10
11
12
protected void initFilterBean() throws ServletException {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
if (this.targetBeanName == null) {
this.targetBeanName = getFilterName();
}
WebApplicationContext wac = findWebApplicationContext();
if (wac != null)
this.delegate = initDelegate(wac);
}
}
}

上一节中介绍了创建DelegatingFilterProxy时候注入了targetBeanName属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!— shiro的过滤器,DelegatingFilterProxy 通过代理模式将spring容器中的bean和filter关联-->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
<!— 设置true由servlet容器控制filter的生命周期 -->
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
<!— 设置spring容器filter的bean id 如果不设置则找与filter-name一致的bean-->
<init-param>
<param-name>targetBeanName</param-name>
<param-value>shiroFilter</param-value>
</init-param>
</filter>

1.2、doFilter()

分析完init()方法我们分析下doFilter()

我们发现最终执行的确是org.apache.shiro.spring.web.ShiroFilterFactoryBean入口类,原来DelegatingFilterProxy代理的是ShiroFilterFactoryBean类,下面我们分析下

2、ShiroFilterFactoryBean

1
public class ShiroFilterFactoryBean implements FactoryBean, BeanPostProcessor

ShiroFilterFactoryBean工厂类,ShiroFilterFactoryBean并没有实现Filter接口啊,所以它并不是一个过滤器,但是DelegatingFilterProxy不是要求被代理的对象是必须是一个Filter吗?仔细看,ShiroFilterFactoryBean实现了FactoryBean接口,也就是说,我们关心的是getObject方法返回的对象是不是一个Filter。

1
2
3
4
5
6
public Object getObject() throws Exception {
if (instance == null) {
instance = createInstance();
}
return instance;
}

我们可以看出其核心方法是createInstance(),该方法实现了几个功能

2.1、FilterChainManager过滤器管理类

2.1.1、属性讲解

查看其源码,FilterChainManager是一个接口,实际的实现类DefaultFilterChainManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class DefaultFilterChainManager implements FilterChainManager {

private static transient final Logger log = LoggerFactory.getLogger(DefaultFilterChainManager.class);
private FilterConfig filterConfig;
private Map<String, Filter> filters; //pool of filters available for creating chains
private Map<String, NamedFilterList> filterChains; //key: chain name, value: chain

public DefaultFilterChainManager() {
this.filters = new LinkedHashMap<String, Filter>();
this.filterChains = new LinkedHashMap<String, NamedFilterList>();
addDefaultFilters(false);
}

public DefaultFilterChainManager(FilterConfig filterConfig) {
this.filters = new LinkedHashMap<String, Filter>();
this.filterChains = new LinkedHashMap<String, NamedFilterList>();
setFilterConfig(filterConfig);
addDefaultFilters(true);
}
//省略代码....
}

1、filters:管理全部过滤器,包括默认的关于身份验证和权限验证的过滤器,这些过滤器分为两组,一组是认证过滤器,有anon,authcBasic,auchc,user,一组是授权过滤器,有perms,roles,ssl,rest,port。同时也包含在xml里filters配置的自定义过滤器。在其它地方使用时都是从过滤器管理类里filters里拿的。且过滤器是单例的,整个Shiro框架只维护每种类型过滤器的单例。

2、filterChains:过滤链。它是我们重点关注的东西,是一个Map对象,其中key就是我们请求的url,value是一个NamedFilterList对象,里面存放的是与url对应的一系列过滤器。这后面会详细讲解

2.1.2、创建过程

1、读取filters配置的自定义过滤器,将它们纳入到过滤器管理器里。

2、applyGlobalPropertiesIfNecessary(filter);方法是将配置的loginUrl,successUrl,unauthorizedUrl属性设置到不同过滤器里。其中loginUrl赋值到所有继承自AccessControlFilter的过滤器里,successUrl赋值到所有继承自AuthenticationFilter的过滤器里,unauthorizedUrl赋值到所有继承自AuthorizationFilter的过滤器里。

当然其实过滤器可以自己设置loginUrl,successUrl,unauthorizedUrl,自定义赋值的也覆盖全局指定的。

3.最后读取filterChainDefinitions配置,根据配置设置每个url对应的过滤链,filterChains保存这些配置,它是一个Map集合,key就是url,value就是过滤器组成的NamedFilterList集合。其实当请求过来时,解析出请求路径,会从filterChains里找到url对应的过滤链,按过滤器的策略一个一个执行下去。 同时会调用PathMatchingFilter的processPathConfig()方法做些赋值操作。下面会专门讲从PathMatchingFilter开始工作的过程。

4.一步一步分析下来过滤器管理器里的过滤链filterChains如下

1
2
3
4
5
6
/login.jsp=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@6ed97422,
/login=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@592dbd8,
/logout=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@1141badb,
/authenticated=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@2d1b876e,
/views/**=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@31106774,
/**=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@14cd7d10

可以看到每个url对应一个SimpleNamedFilterList对象,SimpleNamedFilterList是个List子类对象,保存的是过滤器集

1
2
3
4
5
6
public class SimpleNamedFilterList implements NamedFilterList {

private String name;
private List<Filter> backingList;
//省略代码...
}

2.2、PathMatchingFilterChainResolver

我们继续看将过滤器管理类设置到PathMatchingFilterChainResolver类里,该类负责路径和过滤器链的解析与匹配。根据url找到过滤器链。这个类实现了FilterChainResolver接口,来看FilterChainResolver接口。

1
2
3
public interface FilterChainResolver {
FilterChain getChain(ServletRequest var1, ServletResponse var2, FilterChain var3);
}

在Shiro中,确定一个请求会经过哪些过滤器是通过FilterChainResolver接口来定义的,FilterChainResolver只有一个getChain方法,就是用于确定请求到底要经过哪些过滤器,然后将这些过滤器封装成一个FilterChain对象。PathMatchingFilterChainResolver是Shiro提供的FilterChainResolver的一个实现类,PathMatchingFilterChainResolver是根据请求路径进行匹配过滤器。

接着看PathMatchingFilterChainResolver的源码,重点关注getChain方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
FilterChainManager filterChainManager = getFilterChainManager();
if (!filterChainManager.hasChains()) {
return null;
}
String requestURI = getPathWithinApplication(request);
for (String pathPattern : filterChainManager.getChainNames()) {
if (pathMatches(pathPattern, requestURI)) {
if (log.isTraceEnabled()) {
log.trace("Matched path pattern [" + pathPattern + "] for requestURI [" + requestURI + "]. " +
"Utilizing corresponding filter chain...");
}
return filterChainManager.proxy(originalChain, pathPattern);
}
}
return null;
}

第2行代码是获取一个FilterChainManager对象,上面我们分析过,我们接着看从filterChainManager中获取过滤器的URL和当前的请求的URL进行比对,获取当前的URL对应的执行器链。

1
2
3
4
5
6
7
8
public FilterChain proxy(FilterChain original, String chainName) {
NamedFilterList configured = getChain(chainName);
if (configured == null) {
String msg = "There is no configured chain under the name/key [" + chainName + "].";
throw new IllegalArgumentException(msg);
}
return configured.proxy(original);
}

从FilterChainManager对象的filterChains中获取其name对应的filter的集合,SimpleNamedFilterList.proxy()方法

1
2
3
public FilterChain proxy(FilterChain orig) {
return new ProxiedFilterChain(orig, this);
}

创建了ProxiedFilterChain对象,是一个FilterChain的代理执行器链。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class ProxiedFilterChain implements FilterChain {

private static final Logger log = LoggerFactory.getLogger(ProxiedFilterChain.class);

private FilterChain orig;
private List<Filter> filters;
private int index = 0;

public ProxiedFilterChain(FilterChain orig, List<Filter> filters) {
if (orig == null) {
throw new NullPointerException("original FilterChain cannot be null.");
}
this.orig = orig;
this.filters = filters;
this.index = 0;
}

public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
if (this.filters == null || this.filters.size() == this.index) {
//we've reached the end of the wrapped chain, so invoke the original one:
if (log.isTraceEnabled()) {
log.trace("Invoking original filter chain.");
}
this.orig.doFilter(request, response);
} else {
if (log.isTraceEnabled()) {
log.trace("Invoking wrapped filter at index [" + this.index + "]");
}
this.filters.get(this.index++).doFilter(request, response, this);
}
}
}

过滤器执行的时候执行doFilter()

2.3、SpringShiroFilter

我们发现其返回的是SpringShiroFilter对象,来看下这个SpringShiroFilter的源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
private static final class SpringShiroFilter extends AbstractShiroFilter {

protected SpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
super();
if (webSecurityManager == null) {
throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
}
setSecurityManager(webSecurityManager);
if (resolver != null) {
setFilterChainResolver(resolver);
}
}
}

这个类已经是够简单的了,没并要看了,我们将目光转移到它的父类AbstractShiroFilter。

1
2
3
4
5
6
7
8
public abstract class AbstractShiroFilter extends OncePerRequestFilter {
}
public abstract class OncePerRequestFilter extends NameableFilter {
}
public abstract class NameableFilter extends AbstractFilter implements Nameable{
}
public abstract class AbstractFilter extends ServletContextSupport implements Filter {
}

继承关系比较复杂,最终实现的还是Filter接口,所以AbstractShiroFilter既然也是个Filter了。自然ShiroFilterFactoryBean就是一个filter。

3.、请求解析过程

我们前面分析的都是ShiroFilterFactoryBean其创建的过程,DelegatingFilterProxy.doFilter()中我们分析了delegate代理类就是ShiroFilterFactoryBean,我们分析下invokeDelegate()方法

1
2
3
4
protected void invokeDelegate(Filter delegate, ServletRequest request, ServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
delegate.doFilter(request, response, filterChain);
}

明显是调用ShiroFilterFactoryBean的doFilter()方法,上面我们也分析了实际返回的是SpringShiroFilter对象我们重点分析下其执行过程。

SpringShiroFilter是ShiroFilterFactoryBean的内部类属于,继承AbstractShiroFilter,其doFilter方法在父类中,OncePerRequestFilter的子类不能重写doFilter(),这才是业务核心代码所在,分析首先分析下shiro提供的默认Filter

已经很清楚的说明了这一切,shiro提供的10个默认的过滤器都继承了AdviceFilter,当然我们自定义过滤器的时候也可以继承AbstractShiroFilter,一般自定义Filter继承AdviceFilter或其子类。

我们发现AdviceFilter的父类OncePerRequestFilter是abstract class 继承了Filter,OncePerRequestFilter实现了doFilter()方法

先到达AbstractShiroFilter.executeChain()方法,去根据request解析出来的url找到对应的过滤链,然后执行过滤器链。 executeChain()方法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {
FilterChain chain = origChain;
//resolver即是前面说的PathMatchingFilterChainResolver对象。里面保存有过滤器管理器实例
FilterChainResolver resolver = getFilterChainResolver();
if (resolver == null) {
log.debug("No FilterChainResolver configured. Returning original FilterChain.");
return origChain;
}
//进入PathMatchingFilterChainResolver对象里,根据解析出来的requestURI找到对应的过滤器链并返回
FilterChain resolved = resolver.getChain(request, response, origChain);
if (resolved != null) {
log.trace("Resolved a configured FilterChain for the current request.");
chain = resolved;
} else {
log.trace("No FilterChain configured for the current request. Using the default.");
}

return chain;
}

其中chain是SimpleNamedFilterList对象,name即是匹配的URL

public class ProxiedFilterChain implements FilterChain 相应的实现doFilter()方法。

上面我们讲到一般默认的过滤器和自定义的过滤器都是OncePerRequestFilter的子类执行其doFilter(),上面也分析了其业务逻辑,这个请求第一次执行该过滤器需要先执行doFilterInternal()

查看其子类有两个AbstractShiroFilter、AdviceFilter具体实现。具体调用那个需要根据具体当前的过滤器的父类是哪个决定,一般都是AdviceFilter(默认的filter都是其子类),我们具体分析下这个。

首先看下preHander()

上面分析过退出的过滤器LogoutFilter不继承PathMatchingFilter,都退出了肯定是终止过滤器链的执行。分析一下PathMatchingFilter,其方法的调用关系。

其中重点看看AccessControlFilter和AnonymousFilter。一个是需要身份验证的,一个是匿名访问的。

AnonymousFilter直接放行。

直接看AccessControlFilter,一般访问控制的过滤器都会去继承AccessControlFilter或其子类。

如果isAccessAllowed()验证成功则返回true,否则交由onAccessDenied()方法处理,最后将onAccessDenied()方法处理的结果返回。
isAccessAllowed()方法是决定了当前请求的subject是否允许访问。如果以前做过验证则返回true,否则返回false。
onAccessDenied()方法是在被拒绝访问时处理。AccessControlFilter类有很多子类重载了该方法。

补充说明:

过滤器执行原理:

先从PathMatchingFilter类讲解开始,匹配路径的过滤器,因为大部分(除了LogoutFilter)的过滤器都会继承PathMatchingFilter类,它的作用是路径匹配。过滤器单独维护自己需要过滤的url。

pathMatcher:匹配器,就是当请求过来时,匹配哪个url对应哪个过滤器的。
processPathConfig:解析xml里配置的url对应的过滤器,分别加到appliedPaths要应用的map集合里,由于每个过滤器都继承PathMatchingFilter,故每个过滤器都会经过这步操作。
比如如下图,刚启动web服务时过滤器管理器会解析xml里filterChainDefinitions配置的过滤链,在根据配置给每个url创建过滤器链时,会调用不同过滤器的processPathConfig方法让过滤器自己把url添加到appliedPaths集合里,因为过滤器是单例的,过滤器管理器的filters里也只维护过滤器的单个实例

preHandle方法:请求过来时该方法匹配url路径是否是该过滤器要处理的。遍历appliedPaths里所有的url直到完全匹配成功或遍历完为止。如果匹配成功则表明该url请求是需要该过滤器处理。然后就会进入onPreHandle方法。

onPreHandle方法:该方法在路径匹配成功时决定对url是否需要身份验证。默认是返回true,意思是不需要验证的。但子类需要根据业务逻辑自己重写该方法。

4、自定义过滤器

上面我们分析过Shiro提供了10种过滤器,我们先介绍下,再自定义过滤器

4.1、默认的过滤器

过滤器简称 对应的java类
anon org.apache.shiro.web.filter.authc.AnonymousFilter
authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
port org.apache.shiro.web.filter.authz.PortFilter
rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
ssl org.apache.shiro.web.filter.authz.SslFilter
user org.apache.shiro.web.filter.authc.UserFilter
logout org.apache.shiro.web.filter.authc.LogoutFilter

注解:

  • anon:例子/admins/**=anon 没有参数,表示可以匿名使用。

  • authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,FormAuthenticationFilter是表单认证,没有参数

  • roles:例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles[“admin,guest”],每个参数通过才算通过,相当于hasAllRoles()方法。

  • perms:例子/admins/user/**=perms[user:add:],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/\*=perms[“user:add:,user:modify:\“],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。

  • rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。

  • port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数。

  • authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证

  • ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https

  • user:例如/admins/user/**=user没有参数表示必须存在用户, 身份认证通过或通过记住我认证通过的可以访问,当登入操作时不做检查

anon,authcBasic,auchc,user是认证过滤器,

perms,roles,ssl,rest,port是授权过滤器

4.2、自定义过滤器

我们自定义一个系统日志过滤器,slogFilter

xml中配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!-- 系统日志拦截器 -->
<bean id="slogFilter" class="cn.com.gumx.common.filter.SLogFilter"></bean>

<!-- Shiro的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/a/login"/>
<property name="filters">
<util:map>
<entry key="forceLogout" value-ref="forceLogoutFilter"/>
<entry key="authc" value-ref="formAuthenticationFilter"/>
<entry key="logout" value-ref="logoutFilter" />
<entry key="slog" value-ref="slogFilter" />
<entry key="userAccess" value-ref="userAccessFilter"/>
</util:map>
</property>
<!--anon匿名访问,authc身份验证后访问,roles指定角色授权访问,perms指定类似user:create权限访问-->
<property name="filterChainDefinitions">
<value>
${adminPath}/myapply/** = anon
${adminPath}/register = anon,slog
${restPath}/** = anon,userAccess,slog
${frontPath}/** = anon,forceLogout,slog
${adminPath}/login = authc,slog
${adminPath}/logout = logout,slog
${adminPath}/** = user,perms,roles,forceLogout,slog
</value>
</property>
</bean>

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class SLogFilter extends AdviceFilter {

protected Logger logger = LoggerFactory.getLogger(getClass());

private static final ThreadLocal<Long> startTimeThreadLocal =
new NamedThreadLocal<Long>("ThreadLocal StartTime");

@Override
protected boolean preHandle(ServletRequest requestParm, ServletResponse response)
throws Exception {
HttpServletRequest request = (HttpServletRequest)requestParm;
long beginTime = System.currentTimeMillis();//1、开始时间
startTimeThreadLocal.set(beginTime); //线程绑定变量(该数据只有当前请求的线程可见)
return true;
}

@Override
protected void postHandle(ServletRequest request, ServletResponse response)
throws Exception {

}

@Override
public void afterCompletion(ServletRequest requestParm,
ServletResponse response, Exception exception) throws Exception {
HttpServletRequest request = (HttpServletRequest)requestParm;
long beginTime = startTimeThreadLocal.get();//得到线程绑定的局部变量(开始时间)
long endTime = System.currentTimeMillis(); //2、结束时间
// 保存系统日志
LogUtils.saveSLog(request,
UserUtils.getLoginUser(), exception, DateUtils.formatDateTime(endTime - beginTime));
}
}
本文作者: 顾 明 训
本文链接: https://www.itzones.cn/2019/07/28/Shiro工作原理/
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!
  • shiro源码分析
  • shiro

扫一扫,分享到微信

微信分享二维码
Shiro认证流程
Shiro简单使用
  1. 1. 1、DelegatingFilterProxy
    1. 1.1. 1.1、init()
    2. 1.2. 1.2、doFilter()
  2. 2. 2、ShiroFilterFactoryBean
    1. 2.1. 2.1、FilterChainManager过滤器管理类
      1. 2.1.1. 2.1.1、属性讲解
      2. 2.1.2. 2.1.2、创建过程
    2. 2.2. 2.2、PathMatchingFilterChainResolver
    3. 2.3. 2.3、SpringShiroFilter
  3. 3. 3.、请求解析过程
  4. 4. 4、自定义过滤器
    1. 4.1. 4.1、默认的过滤器
    2. 4.2. 4.2、自定义过滤器
© 2020 IT小栈
载入天数...载入时分秒... || 本站总访问量次 || 本站访客数人次
Hexo Theme Yilia by Litten
  • 所有文章
  • 友链

tag:

  • jvm
  • Java基础
  • kafka HW
  • kafka Leader Epoch
  • kafka
  • kafka位移主题
  • kafka位移提交
  • kafka副本机制
  • kafka ISR
  • zookeeper
  • kafka消息丢失
  • kafka日志存储
  • kafka Log Clean
  • kafka Log Compaction
  • kafka消费位移设置
  • kafka Rebalance
  • kafka分区算法
  • kafka生产者拦截器
  • kafka SASL/SCRAM
  • kafka ACL
  • redis
  • redis Ziplist
  • redis Hashtable
  • redis LinkedList
  • redis QuickList
  • redis intset
  • redis String
  • redis SDS
  • redis SkipList
  • redisDb
  • redisServer
  • redis 简介
  • Redis Cluster
  • 主从同步
  • RocketMQ高可用HA
  • 事务消息
  • 内存映射
  • MMAP
  • 同步刷盘
  • 异步刷盘
  • 消息存储文件
  • RocketMQ安装
  • 延迟消息
  • RocketMQ入门
  • 推拉模式
  • PushConsumer
  • 消费结果处理
  • rebalance
  • RocketMQ权限控制
  • RocketMQ ACL
  • 消息过滤
  • 消息重试
  • 消费位置
  • 集群消费
  • 广播消费
  • 运维命令
  • shiro源码分析
  • shiro入门
  • IOC和DI
  • Spring创建Bean
  • Bean生命周期
  • Sping属性注入
  • 异常
  • SpringMVC
  • springCloud
  • Eureka

    缺失模块。
    1、请确保node版本大于6.2
    2、在博客根目录(注意不是yilia根目录)执行以下命令:
    npm i hexo-generator-json-content --save

    3、在根目录_config.yml里添加配置:

      jsonContent:
        meta: false
        pages: false
        posts:
          title: true
          date: true
          path: true
          text: false
          raw: false
          content: false
          slug: false
          updated: false
          comments: false
          link: false
          permalink: false
          excerpt: false
          categories: false
          tags: true
    

  • 我的OSCHINA
  • 我的CSDN
  • 我的GITHUB
  • 一生太水