您的位置:首页 > 其它

shiro注解授权源码分析

2016-11-16 16:26 519 查看
老问题,要知道注解授权方式为什么能生效,首先得找到入口,相比登录的过程不同,对于这个验证权限的这个过程shiro采用了springAOP的方式

首先在spring配置文件中 我们得开启shiro的注解:

<!-- 开启shiro注解支持-->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
那么我们的入口就找到了,AuthorizationAttributeSourceAdvisor类继承了StaticMethodMatcherPointcutAdvisor类,至于这个StaticMethodMatcherPointcutAdvisor类

是干嘛的呢,不错就是springAOP的一种方式,这里就不多说了,具体怎样后面我自己还得学习下  ( 静态切入点)

public class AuthorizationAttributeSourceAdvisor extends StaticMethodMatcherPointcutAdvisor {

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

private static final Class<? extends Annotation>[] AUTHZ_ANNOTATION_CLASSES =
new Class[] {
RequiresPermissions.class, RequiresRoles.class,
RequiresUser.class, RequiresGuest.class, RequiresAuthentication.class
};

protected SecurityManager securityManager = null;

/**
* Create a new AuthorizationAttributeSourceAdvisor.
*/
public AuthorizationAttributeSourceAdvisor() {
setAdvice(new AopAllianceAnnotationsAuthorizingMethodInterceptor());
}

public SecurityManager getSecurityManager() {
return securityManager;
}

public void setSecurityManager(org.apache.shiro.mgt.SecurityManager securityManager) {
this.securityManager = securityManager;
}

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());
if ( isAuthzAnnotationPresent(m) ) {
return true;
}
} 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;
}

private boolean isAuthzAnnotationPresent(Method method) {
for( Class<? extends Annotation> annClass : AUTHZ_ANNOTATION_CLASSES ) {
Annotation a = AnnotationUtils.findAnnotation(method, annClass);
if ( a != null ) {
return true;
}
}
return false;
}

}
    构造函数中调用了setAdvice方法,传入参数为匿名AopAllianceAnnotationsAuthorizingMethodInterceptor类对象

public AopAllianceAnnotationsAuthorizingMethodInterceptor() {
List<AuthorizingAnnotationMethodInterceptor> interceptors =
new ArrayList<AuthorizingAnnotationMethodInterceptor>(5);
AnnotationResolver resolver = new SpringAnnotationResolver();
//we can re-use the same resolver instance - it does not retain state:
interceptors.add(new RoleAnnotationMethodInterceptor(resolver));
interceptors.add(new PermissionAnnotationMethodInterceptor(resolver));
interceptors.add(new AuthenticatedAnnotationMethodInterceptor(resolver));
interceptors.add(new UserAnnotationMethodInterceptor(resolver));
interceptors.add(new GuestAnnotationMethodInterceptor(resolver));

setMethodInterceptors(interceptors);
}
这里按照Permission分析  PermissionAnnotationMethodInterceptor的带参构造函数

public PermissionAnnotationMethodInterceptor(AnnotationResolver resolver) {
super( new PermissionAnnotationHandler(), resolver);
}
调用父类构造函数并且第一个参数 字面意思可以看出 是对  权限注解的处理类,父类为AuthorizingAnnotationMethodInterceptor,一般我们知道AOP 执行的方法为

invoke() 看到此类中的invoke方法

public Object invoke(MethodInvocation methodInvocation) throws Throwable {
assertAuthorized(methodInvocation);
return methodInvocation.proceed();
}
可以看到其中调用了assertAuthorized方法

public void assertAuthorized(MethodInvocation mi) throws AuthorizationException {
try {
((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;
}
}
其中((AuthorizingAnnotationHandler)getHandler()).assertAuthorized(getAnnotation(mi));这行代码实际就是调用我们之前传入的注解处理类的assertAuthorized方法

查看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.checkPermission(perms[0]);
return;
}
if (Logical.AND.equals(rpAnnotation.logical())) {
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]);

}
}
代码分析,前面就不用说了获取subject,获取注解的permission字符串   以一个字符串分析  

查看checkPermission方法

public void checkPermission(String permission) throws AuthorizationException {
assertAuthzCheckPossible();
securityManager.checkPermission(getPrincipals(), permission);
}
继续看securityManager的checkPermission方法,中间类似方法的调用 最后在ModularRealmAuthorizer中

public void checkPermission(PrincipalCollection principals, String permission) throws AuthorizationException {
assertRealmsConfigured();
if (!isPermitted(principals, permission)) {
throw new UnauthorizedException("Subject does not have permission [" + permission + "]");
}
}
if语句中判断是否满足条件

public boolean isPermitted(PrincipalCollection principals, String permission) {
assertRealmsConfigured();
for (Realm realm : getRealms()) {
if (!(realm instanceof Authorizer)) continue;
if (((Authorizer) realm).isPermitted(principals, permission)) {
return true;
}
}
return false;
}
最后调到AuthorizingRealm的isPermitted方法 这里就不啰嗦了

public boolean isPermitted(PrincipalCollection principals, Permission permission) {
AuthorizationInfo info = getAuthorizationInfo(principals);
return isPermitted(permission, info);
}
getAuthorizationInfo方法为

protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {

if (principals == null) {
return null;
}

AuthorizationInfo info = null;

if (log.isTraceEnabled()) {
log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]");
}

Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache();
if (cache != null) {
if (log.isTraceEnabled()) {
log.trace("Attempting to retrieve the AuthorizationInfo from cache.");
}
Object key = getAuthorizationCacheKey(principals);
info = cache.get(key);
if (log.isTraceEnabled()) {
if (info == null) {
log.trace("No AuthorizationInfo found in cache for principals [" + principals + "]");
} else {
log.trace("AuthorizationInfo found in cache for principals [" + principals + "]");
}
}
}

if (info == null) {
// Call template method if the info was not found in a cache
info = doGetAuthorizationInfo(principals);
// If the info is not null and the cache has been created, then cache the authorization info.
if (info != null && cache != null) {
if (log.isTraceEnabled()) {
log.trace("Caching authorization info for principals: [" + principals + "].");
}
Object key = getAuthorizationCacheKey(principals);
cache.put(key, info);
}
}

return info;
}
上面同样是从缓存中取,直接看下面  ,doGetAuthorizationInfo()方法 有没有很眼熟,没错 就是我们自定义realm里面可以拓展的方法,一般就是从数据库获取配置的

权限字符串然后返回过来了

最后就是比较权限字符串是否包含权限注解中的字符串了

protected boolean isPermitted(Permission permission, AuthorizationInfo info) {
Collection<Permission> perms = getPermissions(info);
if (perms != null && !perms.isEmpty()) {
for (Permission perm : perms) {
if (perm.implies(permission)) {
return true;
}
}
}
return false;
}
    当然没满足就会抛出没有权限的异常了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: