一、Shiro權限原理:spring-shiro.xml須要配置java
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">spring
</bean>這個bean,用來匹配他本身定義的權限註解express
/*apache
* Licensed to the Apache Software Foundation (ASF) under oneapp
* or more contributor license agreements. See the NOTICE fileless
* distributed with this work for additional informationide
* regarding copyright ownership. The ASF licenses this fileui
* to you under the Apache License, Version 2.0 (thethis
* "License"); you may not use this file except in compliancespa
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.shiro.spring.security.interceptor;
import org.apache.shiro.authz.annotation.*;
import org.apache.shiro.mgt.SecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import org.springframework.core.annotation.AnnotationUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* TODO - complete JavaDoc
*
* @since 0.1
*/
@SuppressWarnings({"unchecked"})
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;
}
/**
* Returns <tt>true</tt> if the method has any Shiro annotations, false otherwise.
* The annotations inspected are:
* <ul>
* <li>{@link org.apache.shiro.authz.annotation.RequiresAuthentication RequiresAuthentication}</li>
* <li>{@link org.apache.shiro.authz.annotation.RequiresUser RequiresUser}</li>
* <li>{@link org.apache.shiro.authz.annotation.RequiresGuest RequiresGuest}</li>
* <li>{@link org.apache.shiro.authz.annotation.RequiresRoles RequiresRoles}</li>
* <li>{@link org.apache.shiro.authz.annotation.RequiresPermissions RequiresPermissions}</li>
* </ul>
*
* @param method the method to check for a Shiro annotation
* @param targetClass the class potentially declaring Shiro annotations
* @return <tt>true</tt> if the method has a Shiro annotation, false otherwise.
* @see org.springframework.aop.MethodMatcher#matches(java.lang.reflect.Method, Class)
*/
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;
}
}
此類繼承StaticMethodMatcherPointcutAdvisor父類, AUTHZ_ANNOTATION_CLASSES 是他本身定義的註解類集合,matches方法匹配訪問方法是否有他本身定義的註解。AopAllianceAnnotationsAuthorizingMethodInterceptor這個類是攔截器的總類
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.shiro.spring.security.interceptor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.shiro.aop.AnnotationResolver;
import org.apache.shiro.authz.aop.*;
import org.apache.shiro.spring.aop.SpringAnnotationResolver;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* Allows Shiro Annotations to work in any <a href="http://aopalliance.sourceforge.net/">AOP Alliance</a>
* specific implementation environment (for example, Spring).
*
* @since 0.2
*/
public class AopAllianceAnnotationsAuthorizingMethodInterceptor
extends AnnotationsAuthorizingMethodInterceptor implements MethodInterceptor {
public AopAllianceAnnotationsAuthorizingMethodInterceptor() {
List<AuthorizingAnnotationMethodInterceptor> interceptors =
new ArrayList<AuthorizingAnnotationMethodInterceptor>(5);
//use a Spring-specific Annotation resolver - Spring's AnnotationUtils is nicer than the
//raw JDK resolution process.
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);
}
/**
* Creates a {@link MethodInvocation MethodInvocation} that wraps an
* {@link org.aopalliance.intercept.MethodInvocation org.aopalliance.intercept.MethodInvocation} instance,
* enabling Shiro Annotations in <a href="http://aopalliance.sourceforge.net/">AOP Alliance</a> environments
* (Spring, etc).
*
* @param implSpecificMethodInvocation AOP Alliance {@link org.aopalliance.intercept.MethodInvocation MethodInvocation}
* @return a Shiro {@link MethodInvocation MethodInvocation} instance that wraps the AOP Alliance instance.
*/
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();
}
};
}
/**
* Simply casts the method argument to an
* {@link org.aopalliance.intercept.MethodInvocation org.aopalliance.intercept.MethodInvocation} and then
* calls <code>methodInvocation.{@link org.aopalliance.intercept.MethodInvocation#proceed proceed}()</code>
*
* @param aopAllianceMethodInvocation the {@link org.aopalliance.intercept.MethodInvocation org.aopalliance.intercept.MethodInvocation}
* @return the {@link org.aopalliance.intercept.MethodInvocation#proceed() org.aopalliance.intercept.MethodInvocation.proceed()} method call result.
* @throws Throwable if the underlying AOP Alliance <code>proceed()</code> call throws a <code>Throwable</code>.
*/
protected Object continueInvocation(Object aopAllianceMethodInvocation) throws Throwable {
MethodInvocation mi = (MethodInvocation) aopAllianceMethodInvocation;
return mi.proceed();
}
/**
* Creates a Shiro {@link MethodInvocation MethodInvocation} instance and then immediately calls
* {@link org.apache.shiro.authz.aop.AuthorizingMethodInterceptor#invoke super.invoke}.
*
* @param methodInvocation the AOP Alliance-specific <code>methodInvocation</code> instance.
* @return the return value from invoking the method invocation.
* @throws Throwable if the underlying AOP Alliance method invocation throws a <code>Throwable</code>.
*/
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
org.apache.shiro.aop.MethodInvocation mi = createMethodInvocation(methodInvocation);
return super.invoke(mi);
}
}
這個類是申明的AopAllianceAnnotationsAuthorizingMethodInterceptor攔截器的集合,須要注意的是他的每個攔截器 如RoleAnnotationMethodInterceptor:主要由一個XXHandler(匹配註解值和用戶權限)和AnnotationResolver(獲取註解對象)構成。
二、仿寫:bean類繼承org.springframework.aop.support.StaticMethodMatcherPointcutAdviso
r這個類,經過實現match方法匹配切點,實例所示
public class AuthorizationAdvisor extends StaticMethodMatcherPointcutAdvisor{
/**
* @Fields serialVersionUID : TODO
*/
private static final long serialVersionUID = -880533033433547002L;
private static final Logger log = LoggerFactory.getLogger(AuthorizationAdvisor.class);
@SuppressWarnings("unchecked")
private static final Class<? extends Annotation>[] AUTHZ_ANNOTATION_CLASSES =
new Class[] {RequiresResource.class};
/**
* Create a new AuthorizationAttributeSourceAdvisor.
*/
public AuthorizationAdvisor() {
setAdvice(new AnnotationsMethodInterceptor());
}
/**
* Returns <tt>true</tt> if the method has any Shiro annotations, false otherwise.
* The annotations inspected are:
* <ul>
* <li>{@link org.apache.shiro.authz.annotation.RequiresAuthentication RequiresAuthentication}</li>
* <li>{@link org.apache.shiro.authz.annotation.RequiresUser RequiresUser}</li>
* <li>{@link org.apache.shiro.authz.annotation.RequiresGuest RequiresGuest}</li>
* <li>{@link org.apache.shiro.authz.annotation.RequiresRoles RequiresRoles}</li>
* <li>{@link org.apache.shiro.authz.annotation.RequiresPermissions RequiresPermissions}</li>
* </ul>
*
* @param method the method to check for a Shiro annotation
* @param targetClass the class potentially declaring Shiro annotations
* @return <tt>true</tt> if the method has a Shiro annotation, false otherwise.
* @see org.springframework.aop.MethodMatcher#matches(java.lang.reflect.Method, Class)
*/
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) {
log.error("targetClass is null");
//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;
}
RequiresResource這個是自定義的註解類;matches匹配方法註解假如返回true,就會進入攔截器AnnotationsMethodInterceptor,攔截器方法以下
public class AnnotationsMethodInterceptor extends MethodInterceptorSupport implements MethodInterceptor {
protected Collection<AuthorizingAnnotationMethodInterceptor> methodInterceptors;
public AnnotationsMethodInterceptor() {
List<AuthorizingAnnotationMethodInterceptor> interceptors = new ArrayList<AuthorizingAnnotationMethodInterceptor>(
5);
AnnotationResolver resolver = new SpringAnnotationResolver();
interceptors.add(new RequiresResourceMethodInterceptor(resolver));
setMethodInterceptors(interceptors);
}
/**
* Returns the method interceptors to execute for the annotated method.
* <p/>
* Unless overridden by the
* {@link #setMethodInterceptors(java.util.Collection)} method, the default
* collection contains a {@link RoleAnnotationMethodInterceptor
* RoleAnnotationMethodInterceptor} and a
* {@link PermissionAnnotationMethodInterceptor
* PermissionAnnotationMethodInterceptor} to support role and permission
* annotations automatically.
*
* @return the method interceptors to execute for the annotated method.
*/
public Collection<AuthorizingAnnotationMethodInterceptor> getMethodInterceptors() {
return methodInterceptors;
}
/**
* Sets the method interceptors to execute for the annotated method.
*
* @param methodInterceptors
* the method interceptors to execute for the annotated method.
* @see #getMethodInterceptors()
*/
public void setMethodInterceptors(Collection<AuthorizingAnnotationMethodInterceptor> methodInterceptors) {
this.methodInterceptors = methodInterceptors;
}
/**
* Creates a {@link MethodInvocation MethodInvocation} that wraps an
* {@link org.aopalliance.intercept.MethodInvocation
* org.aopalliance.intercept.MethodInvocation} instance, enabling Shiro
* Annotations in <a href="http://aopalliance.sourceforge.net/">AOP
* Alliance</a> environments (Spring, etc).
*
* @param implSpecificMethodInvocation
* AOP Alliance {@link org.aopalliance.intercept.MethodInvocation
* MethodInvocation}
* @return a Shiro {@link MethodInvocation MethodInvocation} instance that
* wraps the AOP Alliance instance.
*/
protected MethodInvocation createMethodInvocation(Object implSpecificMethodInvocation) {
final org.aopalliance.intercept.MethodInvocation mi = (org.aopalliance.intercept.MethodInvocation) implSpecificMethodInvocation;
return new 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();
}
};
}
/**
* Simply casts the method argument to an
* {@link org.aopalliance.intercept.MethodInvocation
* org.aopalliance.intercept.MethodInvocation} and then calls
* <code>methodInvocation.{@link org.aopalliance.intercept.MethodInvocation#proceed proceed}()</code>
*
* @param aopAllianceMethodInvocation
* the {@link org.aopalliance.intercept.MethodInvocation
* org.aopalliance.intercept.MethodInvocation}
* @return the {@link org.aopalliance.intercept.MethodInvocation#proceed()
* org.aopalliance.intercept.MethodInvocation.proceed()} method call
* result.
* @throws Throwable
* if the underlying AOP Alliance <code>proceed()</code> call
* throws a <code>Throwable</code>.
*/
protected Object continueInvocation(Object aopAllianceMethodInvocation) throws Throwable {
MethodInvocation mi = (MethodInvocation) aopAllianceMethodInvocation;
return mi.proceed();
}
/*
* (non-Javadoc) 獲取對應的註解並解析
*/
protected boolean assertAuthorized(MethodInvocation methodInvocation) throws AuthorizationException {
// default implementation just ensures no deny votes are cast:
boolean b = true;
Collection<AuthorizingAnnotationMethodInterceptor> aamis = getMethodInterceptors();
if (aamis != null && !aamis.isEmpty()) {
for (AuthorizingAnnotationMethodInterceptor aami : aamis) {
if (aami.supports(methodInvocation)) {
b = aami.assertAuthorized(methodInvocation);
}
}
}
return b;
}
@Override
public Object invoke(org.aopalliance.intercept.MethodInvocation invocation) throws Throwable {
MethodInvocation mi = createMethodInvocation(invocation);
if (assertAuthorized(mi)) {
return mi.proceed();
} else {
return null;
}
}
}
大部分代碼來源shiro,這裏不詳細說明了就,須要注意的是紅色區域內,假如爲true,方法正常往下走(客戶端訪問的方法),假如爲false,不往下進行,什麼也不返回。