简介
Shiro框架中的授权注解有:
- @RequiresPermissions
- @RequiresRoles
- @RequiresUser
- @RequiresGuest
- @RequiresAuthentication
一般在Controller的方法上加上授权注解,表明需要满足一定条件,才能执行该方法;
举例:
@RestController
public class DemoController {
// 当发起请求的主体拥有权限"demo:operation"才可以访问该方法
@RequiresPermissions("demo:operation")
@GetMapping(value = "/demo")
public String operation() {
return "OK";
}
}
那么Shiro框架是如何实现的呢?
主要分为两步:
- DemoController创建时,会经过Bean后置处理器,其中DefaultAdvisorAutoProxyCreator会将AuthorizationAttributeSourceAdvisor应用到该Bean的代理;
- 访问DemoController.operation方法,其实是执行DemoController的代理的operation方法,在执行会先执行AuthorizationAttributeSourceAdvisor的逻辑,校验方法上授权注解(如果存在),校验通过则继续执行原方法,否则抛出异常;
接下来分别进行分析。
Bean的代理的创建
Spring Boot创建Bean后,会对其进行后置处理,即AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization,代码如下:
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
// 遍历Bean后置处理器链
// 一般Bean后置处理器,如果处理逻辑,直接返回原Bean
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
if (current == null) {
// 无需继续处理,直接返回
return result;
}
result = current;
}
return result;
}
Bean后置处理器链,如下图所示:
TODO
其中DefaultAdvisorAutoProxyCreator(需要在Shiro配置中配置,但如果引入AOP组件,AOP组件会自动引入AnnotationAwareAspectJAutoProxyCreator,则无需配置),会为可以被切面拦截的Bean,利用切面创建增强代理,其处理逻辑如下:
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
// 代理Bean
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
/**
* 代理Bean
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
// 无需增强,设置标识
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
// 获取可以拦截该Bean的切面
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
// 设置增强标识
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 利用切面创建该Bean的增强代理
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
// 返回代理
return proxy;
}
// 无需增强,设置标识
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
该Bean后置处理器的后置处理主要分两步:
- 获取可以拦截Bean的切面;
- 利用切面创建Bean的增强代理;
获取可以拦截Bean的切面
AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean;
内部调用findEligibleAdvisors,代码如下:
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 获取当前存在的切面(包括AuthorizationAttributeSourceAdvisor)
// AnnotationAwareAspectJAutoProxyCreator重写父类方法,根据advisorRetrievalHelper查找切面
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 获取可以应用到该Bean的切面,即切面是否匹配该Bean的类型,内部调用AopUtils.findAdvisorsThatCanApply判断
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
获取到的candidateAdvisors,如下图所示:
TODO
然后筛选可应用的的切面,AopUtils.findAdvisorsThatCanApply方法会对candidateAdvisors进行遍历,通过canApply筛选,代码如下:
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
// 检查类型
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
MethodMatcher methodMatcher = pc.getMethodMatcher();
if (methodMatcher == MethodMatcher.TRUE) {
// 全匹配直接返回true
// No need to iterate the methods if we're matching any method anyway...
return true;
}
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
Set<Class<?>> classes = new LinkedHashSet<>();
if (!Proxy.isProxyClass(targetClass)) {
// 添加该类
classes.add(ClassUtils.getUserClass(targetClass));
}
// 添加该类实现的接口类型
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
// 遍历所有类型
for (Class<?> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
// 遍历该类型的所有方法
for (Method method : methods) {
if (introductionAwareMethodMatcher != null ?
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
// 只要有一个方法满足,那么就表示切面可以应用到该类上
return true;
}
}
}
// 都不满足,则不支持
return false;
}
canApply实现中会调用切面的matches方法,AuthorizationAttributeSourceAdvisor.macthes的实现如下:
public boolean matches(Method method, Class targetClass) {
Method m = method;
// 方法上有授权注解则匹配成功
if ( isAuthzAnnotationPresent(m) ) {
return true;
}
// 本类的方法没有,则判断父类的同名方法上是否存在,或者本类上是否存在授权注解
//The 'method' parameter could be from an interface that doesn't have the annotation.
//Check to see if the implementation has it.
if ( targetClass != null) {
try {
m = targetClass.getMethod(m.getName(), m.getParameterTypes());
return isAuthzAnnotationPresent(m) || isAuthzAnnotationPresent(targetClass);
} catch (NoSuchMethodException ignored) {
//default return value is false. If we can't find the method, then obviously
//there is no annotation, so just use the default return value.
}
}
return false;
}
只要该Bean的方法上存在Shiro框架的授权注解:
- @RequiresPermissions
- @RequiresRoles
- @RequiresUser
- @RequiresGuest
- @RequiresAuthentication
那么就可以应用切面AuthorizationAttributeSourceAdvisor到该Bean,如下图所示:
TODO
利用切面创建Bean的增强代理
AbstractAutoProxyCreator.createProxy,代码如下:
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
// 创建代理工厂,并配置
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 根据拦截器构建切面
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// 创建代理
return proxyFactory.getProxy(getProxyClassLoader());
}
根据获取切面,构建代理需要的切面,会添加公共拦截器,代码如下:
protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors) {
// 根据AbstractAutoProxyCreator.interceptorNames属性,解析公共拦截器
// Handle prototypes correctly...
Advisor[] commonInterceptors = resolveInterceptorNames();
List<Object> allInterceptors = new ArrayList<>();
if (specificInterceptors != null) {
if (specificInterceptors.length > 0) {
// specificInterceptors may equal PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS
allInterceptors.addAll(Arrays.asList(specificInterceptors));
}
if (commonInterceptors.length > 0) {
// 添加公共拦截器
if (this.applyCommonInterceptorsFirst) {
allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
}
else {
allInterceptors.addAll(Arrays.asList(commonInterceptors));
}
}
}
if (logger.isTraceEnabled()) {
int nrOfCommonInterceptors = commonInterceptors.length;
int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0);
logger.trace("Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors +
" common interceptors and " + nrOfSpecificInterceptors + " specific interceptors");
}
// 包装拦截器为切面
Advisor[] advisors = new Advisor[allInterceptors.size()];
for (int i = 0; i < allInterceptors.size(); i++) {
advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
}
return advisors;
}
配置好代理工厂后,就可以创建代理了:proxyFactory.getProxy(getProxyClassLoader()),内部实现为:createAopProxy().getProxy(classLoader);
createAopProxy最终由DefaultAopProxyFactory.createAopProxy创建出代理工厂,代码如下:
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!NativeDetector.inNativeImage() &&
(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 创建CGLIB代理
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
TODO
然后就是从代理工厂ObjenesisCglibAopProxy获取代理,代码如下:
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
}
try {
Class<?> rootClass = this.advised.getTargetClass();
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Class<?> proxySuperClass = rootClass;
if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
proxySuperClass = rootClass.getSuperclass();
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// Validate the class, writing log messages as necessary.
validateClassIfNecessary(proxySuperClass, classLoader);
// 创建CGLIB 增强器
// Configure CGLIB Enhancer...
Enhancer enhancer = createEnhancer();
// 配置CGLIB 增强器
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
// 获取回调
// 会根据切面生成DynamicAdvisedInterceptor,用于AOP回调
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// 创建增强类和实例
// Generate the proxy class and create a proxy instance.
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
": Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (Throwable ex) {
// TargetSource.getTarget() failed
throw new AopConfigException("Unexpected AOP exception", ex);
}
}
至此,DefaultAdvisorAutoProxyCreator的后置处理就结束了。
Bean的代理的方法的执行
Bean的增强代理的方法的执行流程,如下图所示:
TODO
入口:CglibAopProxy.DynamicAdvisedInterceptor.intercept,代码如下:
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 获取匹配当前方法的拦截器
// 1. AopAllianceAnnotationsAuthorizingMethodInterceptor
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// 创建原方法的调用,并执行
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
创建方法调用:new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy),代码如下:
public CglibMethodInvocation(Object proxy, @Nullable Object target, Method method,
Object[] arguments, @Nullable Class<?> targetClass,
List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
// 调用父类的构造方法
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
this.methodProxy = methodProxy;
this.publicMethod = Modifier.isPublic(method.getModifiers());
}
// 父类构造方法
protected ReflectiveMethodInvocation(
Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments,
@Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
this.proxy = proxy;
this.target = target;
this.targetClass = targetClass;
this.method = BridgeMethodResolver.findBridgedMethod(method);
this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
// 注入拦截器
this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
}
CglibMethodInvocation内部封装了拦截器集合,类似于过滤器链执行机制,CglibMethodInvocation.proceed会先执行剩下的拦截器,若所有的拦截器都执行完了,才执行代理方法;
拦截器方法的返回有两种形式:
- 回调CglibMethodInvocation.proceed;
- 抛出异常,终止代理方法的调用;
CglibMethodInvocation.proceed代码如下:
public Object proceed() throws Throwable {
// this.currentInterceptorIndex从-1开始递增,直到遍历完所有的方法拦截器
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
// 遍历完所有的方法拦截器,开始执行原方法
return invokeJoinpoint();
}
// 获取当前处理的方法拦截器
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
// 递归实现拦截器的遍历
return proceed();
}
}
else {
// 调用方法拦截器
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
本文关注拦截器AopAllianceAnnotationsAuthorizingMethodInterceptor的调用过程,代码如下:
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
// 使用Shiro框架的方法拦截器,适配AOP的方法拦截器
org.apache.shiro.aop.MethodInvocation mi = createMethodInvocation(methodInvocation);
// 调用父类方法执行方法拦截器
return super.invoke(mi);
}
/**
* 适配AOP 方法拦截器
*/
protected org.apache.shiro.aop.MethodInvocation createMethodInvocation(Object implSpecificMethodInvocation) {
final MethodInvocation mi = (MethodInvocation) implSpecificMethodInvocation;
// 简单的封装各方法
return new org.apache.shiro.aop.MethodInvocation() {
public Method getMethod() {
return mi.getMethod();
}
public Object[] getArguments() {
return mi.getArguments();
}
public String toString() {
return "Method invocation [" + mi.getMethod() + "]";
}
public Object proceed() throws Throwable {
return mi.proceed();
}
public Object getThis() {
return mi.getThis();
}
};
}
/**
* 拦截处理
*/
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
// 校验授权
assertAuthorized(methodInvocation);
// 继续执行方法调用
// 类似于过滤器链执行机制,方法调用封装若干方法拦截器
// 执行方法调用时,会先执行剩下的方法拦截器,如果没有则才执行原方法
// 方法拦截器成功执行完拦截逻辑,会继续回调执行方法调用,从而实现遍历所有的方法拦截器
return methodInvocation.proceed();
}
/**
* 校验授权
*/
protected void assertAuthorized(MethodInvocation methodInvocation) throws AuthorizationException {
// 获取配置的方法拦截器
// 默认为:
//default implementation just ensures no deny votes are cast:
Collection<AuthorizingAnnotationMethodInterceptor> aamis = getMethodInterceptors();
if (aamis != null && !aamis.isEmpty()) {
for (AuthorizingAnnotationMethodInterceptor aami : aamis) {
// 是否匹配当前方法拦截器
if (aami.supports(methodInvocation)) {
// 校验授权
aami.assertAuthorized(methodInvocation);
}
}
}
}
/**
* 校验授权
*/
public void assertAuthorized(MethodInvocation mi) throws AuthorizationException {
try {
// 使用内部的AnnotationHandler校验原方法上的注解信息
((AuthorizingAnnotationHandler)getHandler()).assertAuthorized(getAnnotation(mi));
}
catch(AuthorizationException ae) {
// Annotation handler doesn't know why it was called, so add the information here if possible.
// Don't wrap the exception here since we don't want to mask the specific exception, such as
// UnauthenticatedException etc.
if (ae.getCause() == null) ae.initCause(new AuthorizationException("Not authorized to invoke method: " + mi.getMethod()));
throw ae;
}
}
AnnotationHandler的实现,以PermissionAnnotationHandler为例进行分析,代码如下:
/**
* 校验授权
*/
public void assertAuthorized(Annotation a) throws AuthorizationException {
if (!(a instanceof RequiresPermissions)) return;
RequiresPermissions rpAnnotation = (RequiresPermissions) a;
String[] perms = getAnnotationValue(a);
Subject subject = getSubject();
// 单个权限
if (perms.length == 1) {
// 校验当前Subject是否拥有指定的权限
subject.checkPermission(perms[0]);
return;
}
// 多个权限,需要判断是与逻辑还是或逻辑
if (Logical.AND.equals(rpAnnotation.logical())) {
// 校验当前Subject是否拥有指定的权限集合
getSubject().checkPermissions(perms);
return;
}
if (Logical.OR.equals(rpAnnotation.logical())) {
// Avoid processing exceptions unnecessarily - "delay" throwing the exception by calling hasRole first
boolean hasAtLeastOnePermission = false;
for (String permission : perms) if (getSubject().isPermitted(permission)) hasAtLeastOnePermission = true;
// 或逻辑,一个都不满足的话,则抛出第一个权限校验失败的异常
// Cause the exception if none of the role match, note that the exception message will be a bit misleading
if (!hasAtLeastOnePermission) getSubject().checkPermission(perms[0]);
}
}
|