首先在我们之前的chapter1文章中讲到shiro配置类中有这个方法:
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
System.out.println("ShiroConfiguration.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//拦截器.
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
// 配置不会被拦截的链接 顺序判断
filterChainDefinitionMap.put("/static/**", "anon");
//配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put("/logout.html", "logout");
//<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
//<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
filterChainDefinitionMap.put("/**", "authc");
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/index.html");
//未授权界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/403.html");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
就从这个方法开始讲shiro与springboot是如何整合在一起的。首先该方法中实例化了一个ShiroFilterFactoryBean,继承结构为:
可以明显看出,该类实现了Spring中的FactoryBean和BeanPostProcessor接口,来看看都做了那些事情
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//如果该bean实现了java.servlet.Filter接口,则把该bean添加到该类的内部变量
//Map<String,Filter> filters;中,以后再传到过滤器链中
if (bean instanceof Filter) {
Filter filter = (Filter) bean;
//如何该过滤器继承了AccessControlFilter,AuthenticationFilter,AuthorizationFilter
//而且没有更改默认的登录url,登录成功url,权限失败url,就设置成上面我们在配置类中传入的url,否则不用我们上面设置的url
applyGlobalPropertiesIfNecessary(filter);
getFilters().put(beanName, filter);
} else {
}
return bean;
}
//没做啥事,直接返回
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
然后再看BeanFactory接口中的方法实现,此方法的主要目的是生成shiro的一个过滤器,拦截每一个request请求,该过滤器中又包含所有(shiro内部拦截器和我们自己添加进去的拦截器)拦截器。
public Object getObject() throws Exception {
if (instance == null) {
instance = createInstance();
}
return instance;
}
此方法主要验证securityManager实例的有效性,就是我们上面注入到ShiroFilterBeanFactory中的那个。然后,哈哈,这里会创建一个默认的过滤器链管理类,shiro内部自己实现的,这就涉及到了过滤器设计模式了。关于shiro这里的过滤器设计代码就不在这里分析了,会单独写篇文章分析。最后把securityManager和chainResolver传入SpringShiroFilter
protected AbstractShiroFilter createInstance() throws Exception {
SecurityManager securityManager = getSecurityManager();
if (securityManager == null) {
throw new BeanInitializationException(msg);
}
if (!(securityManager instanceof WebSecurityManager)) {
throw new BeanInitializationException(msg);
}
FilterChainManager manager = createFilterChainManager();
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
chainResolver.setFilterChainManager(manager);
return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
}
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);
}
}
在哪里把SecurityManager保存在 ThreadContext 中的那 ?原来是执行Filter 的init方法的时候:
public final void init(FilterConfig filterConfig) throws ServletException {
setFilterConfig(filterConfig);
try {
onFilterConfigSet();
} catch (Exception e) {
if (e instanceof ServletException) {
throw (ServletException) e;
} else {
if (log.isErrorEnabled()) {
log.error("Unable to start Filter: [" + e.getMessage() + "].", e);
}
throw new ServletException(e);
}
}
}
protected final void onFilterConfigSet() throws Exception {
//added in 1.2 for SHIRO-287:
applyStaticSecurityManagerEnabledConfig();
init();
ensureSecurityManager();
//added in 1.2 for SHIRO-287:
if (isStaticSecurityManagerEnabled()) {
SecurityUtils.setSecurityManager(getSecurityManager());
}
}
SecurityUtils.setSecurityManager(getSecurityManager());真想大白了,所以我们能过该工具类获取到SecurityManager