前言
这篇看看shiro的工作流程,研究一下config文件中的3个Bean是怎么运作的。
开头
回顾一下配置文件里的三个Bean,他们的返回类型和参数类型是互相吻合的:
@Bean
public ShiroFilterFactoryBean myShiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> map = new HashMap<>();
map.put("/shiro", "authc");
map.put("/logout", "authc");
shiroFilterFactoryBean.setLoginUrl("/index");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
@Bean
public DefaultWebSecurityManager myDefaultWebSecurityManager(Realm realm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
@Bean
public Realm myRealm() {
return new MyRealm();
}
通过调试可以发现,它们是套娃调用,上一个作为下一个的参数,最后合并成一个ShiroFilterFactoryBean。
配置加载-BeanPostProcessor注册
这个ShiroFilterFactoryBean对象继承了FactoryBean和BeanPostProcessor两个接口,BeanPostProcessor接口可以用于在实例化Bean时对其进行拦截、修改,具有比较高的加载优先度。
在加载BeanPostProcessor的registerBeanPostProcessors函数中,会通过beanFactory的getBeanNamesForType函数获取要加载的BeanPostProcessor名称:
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
通过调试可以发现,这里获取到了我们自定义的Bean,名字叫做&myShiroFilterFactoryBean:
进入这个getBeanNamesForType函数,来到doGetBeanNamesForType函数:
private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
List<String> result = new ArrayList<>();
// Check all bean definitions.
for (String beanName : this.beanDefinitionNames) {
// Only consider bean as eligible if the bean name is not defined as alias for some other bean.
if (!isAlias(beanName)) {
try {
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// Only check bean definition if it is complete.
if (!mbd.isAbstract() && (allowEagerInit ||
(mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&
!requiresEagerInitForType(mbd.getFactoryBeanName()))) {
boolean isFactoryBean = isFactoryBean(beanName, mbd);
BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
boolean matchFound = false;
boolean allowFactoryBeanInit = (allowEagerInit || containsSingleton(beanName));
boolean isNonLazyDecorated = (dbd != null && !mbd.isLazyInit());
if (!isFactoryBean) {
if (includeNonSingletons || isSingleton(beanName, mbd, dbd)) {
matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
}
}
else {
if (includeNonSingletons || isNonLazyDecorated ||
(allowFactoryBeanInit && isSingleton(beanName, mbd, dbd))) {
matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
}
if (!matchFound) {
// In case of FactoryBean, try to match FactoryBean instance itself next.
beanName = FACTORY_BEAN_PREFIX + beanName;
matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
}
}
if (matchFound) {
result.add(beanName);
}
}
}
...
}
}
...
return StringUtils.toStringArray(result);
}
这里遍历了定义的Bean进行确认,根据调试中看到的Bean名前多出来的&,我们可以确定当遍历到myShiroFilterFactoryBean时进入了这段代码:
if (!matchFound) {
// In case of FactoryBean, try to match FactoryBean instance itself next.
beanName = FACTORY_BEAN_PREFIX + beanName;
matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
}
首先是这一段判断:
// Only check bean definition if it is complete.
if (!mbd.isAbstract() && (allowEagerInit ||
(mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&
!requiresEagerInitForType(mbd.getFactoryBeanName())))
根据注释,这里只是确认该Bean是否完整,略过,然后是第二个判断用的布尔值isFactoryBean,其通过isFactoryBean函数赋值:
boolean isFactoryBean = isFactoryBean(beanName, mbd);
跟进去看看:
protected boolean isFactoryBean(String beanName, RootBeanDefinition mbd) {
Boolean result = mbd.isFactoryBean;
if (result == null) {
...
}
return result;
}
可以看到结果直接来自mbd对象的isFactoryBean属性,回头看看这个对象是通过getMergedLocalBeanDefinition函数生成的:
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
// Quick check on the concurrent map first, with minimal locking.
RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
if (mbd != null && !mbd.stale) {
return mbd;
}
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
调试可以发现,此时的mbd是直接从内部map中取出的,虽然不会直接返回,但是其isFactoryBean属性和resolvedTargetType属性等已经有了值:
调试发现对这些属性的赋值是在注册BeanPostProcessor之前,会加载Bean,期间还有一次调用doGetBeanNamesForType函数的操作:
这次调用isFactoryBean函数时就会因为其isFactoryBean属性未赋值而进入predictBeanType函数:
protected Class<?> predictBeanType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
Class<?> targetType = determineTargetType(beanName, mbd, typesToMatch);
...
return targetType;
}
然后到determineTargetType函数:
protected Class<?> determineTargetType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
Class<?> targetType = mbd.getTargetType();
if (targetType == null) {
targetType = (mbd.getFactoryMethodName() != null ?
getTypeForFactoryMethod(beanName, mbd, typesToMatch) :
resolveBeanClass(mbd, beanName, typesToMatch));
if (ObjectUtils.isEmpty(typesToMatch) || getTempClassLoader() == null) {
mbd.resolvedTargetType = targetType;
}
}
return targetType;
}
再到getTypeForFactoryMethod函数,这里会遍历myShiroFilterFactoryBean所属的BeanFactory,即myShiroConfig类中的函数,找到与该Bean同名的函数:
for (Method candidate : candidates) {
if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate) &&
candidate.getParameterCount() >= minNrOfArgs) {
// Declared type variables to inspect?
if (candidate.getTypeParameters().length > 0) {
...
}
else {
uniqueCandidate = (commonType == null ? candidate : null);
commonType = ClassUtils.determineCommonAncestor(candidate.getReturnType(), commonType);
if (commonType == null) {
// Ambiguous return types found: return null to indicate "not determinable".
return null;
}
}
}
}
再:
cachedReturnType = (uniqueCandidate != null ?
ResolvableType.forMethodReturnType(uniqueCandidate) : ResolvableType.forClass(commonType));
mbd.factoryMethodReturnType = cachedReturnType;
return cachedReturnType.resolve();
简单来说就是返回了该函数的返回类型,即ShiroFilterFactoryBean。然后回到determineTargetType函数将其放入mbd的resolvedTargetType属性中,再回到isFactoryBean函数中,因为ShiroFilterFactoryBean是FactoryBean类的子类,所以mbd的isFactoryBean属性被赋值为了true。
回到doGetBeanNamesForType函数,最后会通过isTypeMatch函数确认myShiroFilterFactoryBean是否满足BeanPostProcessor这个Type的要求。
当bean名称前面有一个&符号时,isFactoryDereference就会是true,代表这个bean是一个FactoryBean:
boolean isFactoryDereference = BeanFactoryUtils.isFactoryDereference(name);
...
public static boolean isFactoryDereference(@Nullable String name) {
return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
}
然后整一个typesToMatch变量:
// Setup the types that we want to match against
Class<?> classToMatch = typeToMatch.resolve();
if (classToMatch == null) {
classToMatch = FactoryBean.class;
}
Class<?>[] typesToMatch = (FactoryBean.class == classToMatch ?
new Class<?>[] {classToMatch} : new Class<?>[] {FactoryBean.class, classToMatch});
此时classToMatch变量值为BeanPostProcessor,因为不等于FactoryBean,所以typesToMatch是一个数组,需要该bean同时继承BeanPostProcessor和FactoryBean接口。
因为myShiroFilterFactoryBean是一个FactoryBean,所以会跳过下面的判断到:
// If we couldn't use the target type, try regular prediction.
if (predictedType == null) {
predictedType = predictBeanType(beanName, mbd, typesToMatch);
if (predictedType == null) {
return false;
}
}
predictBeanType函数如下:
protected Class<?> predictBeanType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
Class<?> targetType = mbd.getTargetType();
if (targetType != null) {
return targetType;
}
if (mbd.getFactoryMethodName() != null) {
return null;
}
return resolveBeanClass(mbd, beanName, typesToMatch);
}
...
public Class<?> getTargetType() {
if (this.resolvedTargetType != null) {
return this.resolvedTargetType;
}
ResolvableType targetType = this.targetType;
return (targetType != null ? targetType.resolve() : null);
}
返回值来自mbd的resolvedTargetType属性,即ShiroFilterFactoryBean,再下一步:
// We don't have an exact type but if bean definition target type or the factory
// method return type matches the predicted type then we can use that.
if (beanType == null) {
ResolvableType definedType = mbd.targetType;
if (definedType == null) {
definedType = mbd.factoryMethodReturnType;
}
if (definedType != null && definedType.resolve() == predictedType) {
beanType = definedType;
}
}
// If we have a bean type use it so that generics are considered
if (beanType != null) {
return typeToMatch.isAssignableFrom(beanType);
}
beanType来自mbd的factoryMethodReturnType属性为ShiroFilterFactoryBean,而typeToMatch是来自参数的BeanPostProcessor,因为ShiroFilterFactoryBean继承了BeanPostProcessor接口,所以返回值为true。
回到doGetBeanNamesForType函数,将&myShiroFilterFactoryBean加入到了要注册的BeanPostProcessor中,最后返回registerBeanPostProcessors函数进行注册:
String ppName;
BeanPostProcessor pp;
for(int var10 = 0; var10 < var9; ++var10) {
ppName = var8[var10];
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
pp = (BeanPostProcessor)beanFactory.getBean(ppName, BeanPostProcessor.class);
priorityOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
} else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
} else {
nonOrderedPostProcessorNames.add(ppName);
}
}
因为myShiroFilterFactoryBean没有继承PriorityOrdered、Ordered之类的优先级接口,所以会放入低优先级的nonOrderedPostProcessorNames队列中,再到后面进行实例化:
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList(nonOrderedPostProcessorNames.size());
Iterator var17 = nonOrderedPostProcessorNames.iterator();
while(var17.hasNext()) {
ppName = (String)var17.next();
pp = (BeanPostProcessor)beanFactory.getBean(ppName, BeanPostProcessor.class);
nonOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
实例化用的是getBean函数,给myShiroFilterFactoryBean函数下个断点,找到关键实例化函数instantiateUsingFactoryMethod:
String factoryBeanName = mbd.getFactoryBeanName();
if (factoryBeanName != null) {
if (factoryBeanName.equals(beanName)) {
throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
"factory-bean reference points back to the same bean definition");
}
factoryBean = this.beanFactory.getBean(factoryBeanName);
if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {
throw new ImplicitlyAppearedSingletonException();
}
this.beanFactory.registerDependentBean(factoryBeanName, beanName);
factoryClass = factoryBean.getClass();
isStatic = false;
}
首先实例化了一个myShiroConfig对象,然后:
// Need to determine the factory method...
// Try all methods with this name to see if they match the given arguments.
factoryClass = ClassUtils.getUserClass(factoryClass);
List<Method> candidates = null;
if (mbd.isFactoryMethodUnique) {
if (factoryMethodToUse == null) {
factoryMethodToUse = mbd.getResolvedFactoryMethod();
}
if (factoryMethodToUse != null) {
candidates = Collections.singletonList(factoryMethodToUse);
}
}
从mbd中获取了myShiroFilterFactoryBean函数放在candidates中作为下面要调用的factoryMethod,再然后:
for (Method candidate : candidates) {
int parameterCount = candidate.getParameterCount();
if (parameterCount >= minNrOfArgs) {
ArgumentsHolder argsHolder;
Class<?>[] paramTypes = candidate.getParameterTypes();
if (explicitArgs != null) {
...
}
else {
// Resolved constructor arguments: type conversion and/or autowiring necessary.
try {
String[] paramNames = null;
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
if (pnd != null) {
paramNames = pnd.getParameterNames(candidate);
}
argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw,
paramTypes, paramNames, candidate, autowiring, candidates.size() == 1);
}
...
}
...
}
}
这里会调用createArgumentArray处理参数:
MethodParameter methodParam = MethodParameter.forExecutable(executable, paramIndex);
// No explicit match found: we're either supposed to autowire or
// have to fail creating an argument array for the given constructor.
if (!autowiring) {
...
}
try {
Object autowiredArgument = resolveAutowiredArgument(
methodParam, beanName, autowiredBeanNames, converter, fallback);
args.rawArguments[paramIndex] = autowiredArgument;
args.arguments[paramIndex] = autowiredArgument;
args.preparedArguments[paramIndex] = autowiredArgumentMarker;
args.resolveNecessary = true;
}
根据函数名resolveAutowiredArgument可以看出来,下一步通过自动装载获取参数。那什么时候会进入这段代码来获取参数?往上找找,可以看到要求为
if (valueHolder != null)
而valueHolder为null的要求为:
if (resolvedValues != null) {
valueHolder = resolvedValues.getArgumentValue(paramIndex, paramType, paramName, usedValueHolders);
// If we couldn't find a direct match and are not supposed to autowire,
// let's try the next generic, untyped argument value as fallback:
// it could match after type conversion (for example, String -> int).
if (valueHolder == null && (!autowiring || paramTypes.length == resolvedValues.getArgumentCount())) {
valueHolder = resolvedValues.getGenericArgumentValue(null, null, usedValueHolders);
}
}
就是取决于来自参数的变量resolvedValues,回到上一级函数instantiateUsingFactoryMethod中:
// We don't have arguments passed in programmatically, so we need to resolve the
// arguments specified in the constructor arguments held in the bean definition.
if (mbd.hasConstructorArgumentValues()) {
ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
resolvedValues = new ConstructorArgumentValues();
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}
else {
minNrOfArgs = 0;
}
简单来说就是代码中找不到参数,所以要通过自动装载从其他bean中获取参数,也就是说注册BeanPostProcessor要调用myShiroFilterFactoryBean这个bean,而由于缺失参数所以要调用myDefaultWebSecurityManager,又因为缺失参数要调用myRealm,从而形成了一种bean之间的依赖传递的情况。
所以我们把三个bean简化成一个也还是可以用的:
@Configuration
public class MyShiroConfig {
@Bean
public ShiroFilterFactoryBean myShiroFilterFactoryBean() {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(new MyRealm());
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
Map<String, String> map = new HashMap<>();
map.put("/shiro", "authc");
map.put("/logout", "authc");
shiroFilterFactoryBean.setLoginUrl("/index");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
}
至此,我们编写的三个bean已经执行完毕,最后的执行结果就是在内存中形成了一个属于myShiroFilterFactoryBean这个bean的ShiroFilterFactoryBean对象。
权限控制
用于配置权限控制的map会被放入ShiroFilterFactoryBean对象的filterChainDefinitionMap属性中:
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
...
public void setFilterChainDefinitionMap(Map<String, String> filterChainDefinitionMap) {
this.filterChainDefinitionMap = filterChainDefinitionMap;
}
而createFilterChainManager函数会取出此map进行下一步操作,先不看具体的处理代码,下个断点看看调用栈,找到ServletContextInitializerBeans类的addAdaptableBeans函数:
protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
MultipartConfigElement multipartConfig = this.getMultipartConfig(beanFactory);
this.addAsRegistrationBean(beanFactory, Servlet.class, new ServletContextInitializerBeans.ServletRegistrationBeanAdapter(multipartConfig));
this.addAsRegistrationBean(beanFactory, Filter.class, new ServletContextInitializerBeans.FilterRegistrationBeanAdapter());
...
}
可以看到这个函数用于处理跟servlet、filter相关的bean,跟下去可以发现用于获取要处理的bean名称所用的同样是getBeanNamesForType函数:
String[] names = beanFactory.getBeanNamesForType(type, true, false);
不过不同的是这次筛选要求的type为Filter,而且在通过isTypeMatch函数判断是否符合要求时,会进入这段代码:
// Check manually registered singletons.
Object beanInstance = getSingleton(beanName, false);
if (beanInstance != null && beanInstance.getClass() != NullBean.class) {
if (beanInstance instanceof FactoryBean) {
if (!isFactoryDereference) {
Class<?> type = getTypeForFactoryBean((FactoryBean<?>) beanInstance);
return (type != null && typeToMatch.isAssignableFrom(type));
}
else {
return typeToMatch.isInstance(beanInstance);
}
}
...
}
由于内存中已经保存了myShiroFilterFactoryBean这个bean对应的组装好的ShiroFilterFactoryBean对象,所以getSingleton函数就会获取到这个对象,getTypeForFactoryBean函数如下:
protected Class<?> getTypeForFactoryBean(FactoryBean<?> factoryBean) {
try {
if (System.getSecurityManager() != null) {
...
}
else {
return factoryBean.getObjectType();
}
}
...
}
而ShiroFilterFactoryBean类是一个用于生产SpringShiroFilter的类,其getObject函数和getObjectType函数如下:
public Object getObject() throws Exception {
if (instance == null) {
instance = createInstance();
}
return instance;
}
public Class getObjectType() {
return SpringShiroFilter.class;
}
getObjectType函数告知Spring这个工厂bean会生产一个SpringShiroFilter对象,以满足前面type要求的Filter,getObject函数用于后面的生产工作。
回到getOrderedBeansOfType:
Map<String, T> map = new LinkedHashMap();
String[] var6 = names;
int var7 = names.length;
for(int var8 = 0; var8 < var7; ++var8) {
String name = var6[var8];
if (!excludes.contains(name) && !ScopedProxyUtils.isScopedTarget(name)) {
T bean = beanFactory.getBean(name, type);
if (!excludes.contains(bean)) {
map.put(name, bean);
}
}
}
获取到符合filter要求的bean后,会调用getBean函数,同样由于内存中存在这个ShiroFilterFactoryBean对象,会一步步进入doGetObjectFromFactoryBean函数:
private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
Object object;
try {
if (System.getSecurityManager() != null) {
...
}
else {
object = factory.getObject();
}
}
...
return object;
}
调用ShiroFilterFactoryBean类的getObject开始生产,一步步来到我们要看的createFilterChainManager函数:
//build up the chains:
Map<String, String> chains = getFilterChainDefinitionMap();
if (!CollectionUtils.isEmpty(chains)) {
for (Map.Entry<String, String> entry : chains.entrySet()) {
String url = entry.getKey();
String chainDefinition = entry.getValue();
manager.createChain(url, chainDefinition);
}
}
// create the default chain, to match anything the path matching would have missed
manager.createDefaultChain("/**"); // TODO this assumes ANT path matching, which might be OK here
return manager;
url为要拦截的url,chainDefinition为所需的权限。然后调用manager的createChain函数开始创建权限规则,这是一个DefaultFilterChainManager类,经过createChain、addToChain等函数来到addToChain函数:
public void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) {
if (!StringUtils.hasText(chainName)) {
throw new IllegalArgumentException("chainName cannot be null or empty.");
} else {
Filter filter = this.getFilter(filterName);
if (filter == null) {
...
} else {
this.applyChainConfig(chainName, filter, chainSpecificFilterConfig);
NamedFilterList chain = this.ensureChain(chainName);
chain.add(filter);
}
}
}
通过定义的权限获取对应的filter,比如authc获取到的就是FormAuthenticationFilter,然后调用applyChainConfig函数应用新的规则,一路来到processPathConfig函数中:
public Filter processPathConfig(String path, String config) {
String[] values = null;
if (config != null) {
values = StringUtils.split(config);
}
this.appliedPaths.put(path, values);
return this;
}
其实就是将要拦截的路径放入appliedPaths这个map中,当请求到来时就会调用preHandle函数:
for (String path : this.appliedPaths.keySet()) {
// If the path does match, then pass on to the subclass implementation for specific checks
//(first match 'wins'):
if (pathsMatch(path, request)) {
log.trace("Current requestURI matches pattern '{}'. Determining filter chain execution...", path);
Object config = this.appliedPaths.get(path);
return isFilterChainContinued(request, response, path, config);
}
}
将请求路径跟appliedPaths中的配置相匹配,然后调用isAccessAllowed函数进行权限判断:
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
return super.isAccessAllowed(request, response, mappedValue) ||
(!isLoginRequest(request, response) && isPermissive(mappedValue));
}
mappedValue通常为null,所以继续看父类的isAccessAllowed函数:
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
Subject subject = getSubject(request, response);
return subject.isAuthenticated() && subject.getPrincipal() != null;
}
...
protected Subject getSubject(ServletRequest request, ServletResponse response) {
return SecurityUtils.getSubject();
}
跟我们控制器中的写法差不多,简单来说就是通过读取session来检验是否登录。
身份认证
懒得看了,应该差不多那个样子。