1.自定義注解
元注解介紹
@Target說明了Annotation所修飾的對象范圍:Annotation可被用于 packages谦去、types(類、接口蹦哼、枚舉鳄哭、Annotation類型)纲熏、類型成員(方法妆丘、構(gòu)造方法、成員變量局劲、枚舉值)勺拣、方法參數(shù)和本地變量(如循環(huán)變量、catch參數(shù))鱼填。在Annotation類型的聲明中使用了target可更加明晰其修飾的目標(biāo)药有。
CONSTRUCTOR:用于描述構(gòu)造器
FIELD:用于描述域
LOCAL_VARIABLE:用于描述局部變量
METHOD:用于描述方法
PACKAGE:用于描述包
PARAMETER:用于描述參數(shù)
TYPE:用于描述類、接口(包括注解類型) 或enum聲明
表示需要在什么級別保存該注釋信息苹丸,用于描述注解的生命周期(即:被描述的注解在什么范圍內(nèi)有效)
3.@Documented
Documented注解表明這個注釋是由 javadoc記錄的愤惰,在默認(rèn)情況下也有類似的記錄工具苇经。 如果一個類型聲明被注釋了文檔化,它的注釋成為公共API的一部分羊苟。
自定義注解示例
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AuthCheck
{
public abstract String module();
public abstract String oper();
public abstract String userProfile();
public abstract String dbYear();
public abstract String entityId();
public abstract String entity();
public abstract boolean failWhenUserProfileIsNull();
public abstract String failCode();
}
2.AOP
AOP概念
面向切面編程:擴(kuò)展功能不修改源代碼實現(xiàn),AOP采用橫向抽取機(jī)制塑陵,取代傳統(tǒng)的縱向繼承體系重復(fù)性代碼(性能監(jiān)視,事務(wù)管理蜡励,安全檢查令花,緩存)
AOP操作術(shù)語:
(JoinPoint)連接點:類里面可以被增強(qiáng)的方法
(Pointcut)切入點:實際增強(qiáng)的方法,也就是新增的方法
(Advice)通知/增強(qiáng):增強(qiáng)的邏輯凉倚,比如擴(kuò)展日志功能兼都,這個日志功能增強(qiáng)
前置通知、后置通知稽寒、異常通知扮碧、最終通知、環(huán)繞通知
(Aspect)切面:把增強(qiáng)應(yīng)用到具體方法上杏糙,過程稱為切面
對@AuthCheck注解的處理實例
package com.supporter.prj.eip.auth_engine.service;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Service;
/**
* 提供對@AuthCheck注解的處理.
* @Aspect 實現(xiàn)spring aop 切面(Aspect):
* 一個關(guān)注點的模塊化慎王,這個關(guān)注點可能會橫切多個對象。事務(wù)管理是J2EE應(yīng)用中一個關(guān)于橫切關(guān)注點的很好的例子宏侍。 在Spring
* AOP中赖淤,切面可以使用通用類(基于模式的風(fēng)格) 或者在普通類中以 @Aspect 注解(@AspectJ風(fēng)格)來實現(xiàn)。
* AOP代理(AOP Proxy): AOP框架創(chuàng)建的對象谅河,用來實現(xiàn)切面契約(aspect contract)(包括通知方法執(zhí)行等功能)咱旱。
* 在Spring中,AOP代理可以是JDK動態(tài)代理或者CGLIB代理绷耍。 注意:Spring 2.0引入的基于模式(schema-based)風(fēng)格和@AspectJ注解風(fēng)格的切面聲明吐限,對于使用這些風(fēng)格的用戶來說,代理的創(chuàng)建是透明的褂始。
*/
@Service
@Aspect
public class AuthAnnotationService {
private ILogService getLogService() {
return LogUtil.getAuthDebugLogService();
}
/**
* 構(gòu)造方法.
*/
public AuthAnnotationService() {}
/**
* 定義一個PointCut.
* @param point
* @throws Throwable
*/
@Pointcut("@annotation(com.supporter.prj.eip_service.authority.annotation.AuthCheck)")
public void methodPointcut() {}
// 方法執(zhí)行的前后調(diào)用
@Around("methodPointcut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
//getLogService().debug("類實例:" + point.getTarget().getClass() + ":" + point.getTarget().hashCode());
boolean authCheckPassed = true;
getLogService().debug("[權(quán)限引擎]注解方式開始執(zhí)行诸典,進(jìn)入around(), 方法:" + point.getSignature().getName());
String moduleSetting = this.getModule(point);
IModule module = EIPService.getModuleService().getModule(moduleSetting);
if (module != null) {
//只有module設(shè)置有效才繼續(xù),否則會影響性能
String operSetting = this.getOper(point);
getLogService().debug("[權(quán)限引擎]operSetting:" + operSetting);
IOper oper = EIPService.getModuleService().getOper(operSetting, module);
if (oper != null) {
//只有oper設(shè)置有效才繼續(xù)病袄,否則會影響性能
UserProfile userProfile = this.getUserProfile(point);
//getLogService().debug("[權(quán)限引擎]userProfile:" + userProfile.getAccountLogin());
if (userProfile != null) {
//只有userProfile才繼續(xù)搂赋,否則影響性能
String dbYearSetting = this.getDbYearSetting(point);
getLogService().debug("[權(quán)限引擎]dbYearSetting:" + dbYearSetting);
if (dbYearSetting.length() > 0) {
//如果存在dbYear設(shè)置,那么說明是年庫相關(guān)
int dbYear = this.getDbYear(point);
getLogService().debug("[權(quán)限引擎]dbYear:" + dbYear);
if (dbYear <= 0) {
getLogService().debug("[權(quán)限引擎]failed to get dbYear, auth check point:" + this.getMethodDesc(point));
authCheckPassed = false;
} else {
EIPService.getAuthorityService().canAccess(dbYear, userProfile, oper,entity);
if (getLogService().isDebugEnabled()) {
getLogService().debug("[權(quán)限引擎]權(quán)限檢查: " + userProfile.getAccountLogin()
+ ":" + oper.getModuleId() + "." + oper.getName());
}
authCheckPassed = EIPService.getAuthorityService().canAccess(userProfile, oper);
// }
}
} else {
//否則與年庫無關(guān)益缠,在應(yīng)用庫中
// if (entity != null) {
// authCheckPassed = EIPService.getAuthorityService().canAccess(userProfile, oper, entity);
// } else {
//沒有傳遞過來entityId,那么忽略數(shù)據(jù)限定條件
authCheckPassed = EIPService.getAuthorityService().canAccess(userProfile, oper);
// }
}
} else {
//注:在userProfile為null的情況下基公,是否允許執(zhí)行幅慌?根據(jù)failWhenUserProfileIsNull來判斷
getLogService().debug("[權(quán)限引擎]userProfile is null. auch check for method:" + this.getMethodDesc(point));
boolean failWhenUserProfileIsNull = getFailWhenUserProfileIsNull(point);
if (failWhenUserProfileIsNull) authCheckPassed = false;
}
} else {
getLogService().debug("[權(quán)限引擎]invalid oper:" + operSetting + ", module:" + moduleSetting);
}
} else {
getLogService().debug("[權(quán)限引擎]invalid module:" + moduleSetting);
}
if (!authCheckPassed) throw new BaseRuntimeException(getFailCode(point), "[權(quán)限引擎]當(dāng)前用戶無權(quán)執(zhí)行本操作:" + this.getMethodDesc(point));
//執(zhí)行被注解的方法.
Object object;
try {
object = point.proceed();
} catch (Throwable t) {
t.printStackTrace();
throw t;
}
long e = System.currentTimeMillis();
getLogService().debug("[權(quán)限引擎]注解方式執(zhí)行完畢:" + point.getSignature().getName()+",耗時:"+(e-s));
return object;
}
// 方法運行出現(xiàn)異常時調(diào)用
// @AfterThrowing(pointcut = "execution(* com.supporter..*(..))", throwing = "ex")
public void afterThrowing(Exception ex) {
if (ex != null) ex.printStackTrace();
}
/**
* key為"className:methodName",value是一個List,里面是同名的方法(可能參數(shù)不同).
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private static Map < String, List < Method > > methodMap = new ConcurrentHashMap();
@SuppressWarnings({ "unchecked", "rawtypes" })
private static List < Method > getMethods(String className, String methodName) {
String key = className + ":" + methodName;
if (methodMap.containsKey(key)) return methodMap.get(key);
//否則獲取一番
try {
Class < ? > targetClass = Class.forName(className);
return getMethods(targetClass, methodName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return new ArrayList();
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private static List < Method > getMethods(Class < ? > clazz, String methodName) {
if (clazz == null) return null;
String key = clazz.getName() + ":" + methodName;
if (methodMap.containsKey(key)) return methodMap.get(key);
List < Method > methodList = new ArrayList();
//否則獲取一番
//Method[] methods = targetClass.getMethods(); //getMethods()只返回public的方法
Method[] methods = clazz.getDeclaredMethods();
for (Method m : methods) {
if (m.getName().equals(methodName)) {
methodList.add(m);
}
}
if (methodList.size() == 0) {
//在當(dāng)前類中找不到轰豆,試試看祖先有沒有
if (clazz.getSuperclass() != null) {
methodList = getMethods(clazz.getSuperclass(), methodName);
}
}
methodMap.put(key, methodList); //放入緩存中
return methodList;
}
private String getMethodDesc(ProceedingJoinPoint joinPoint) {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
String paramNameStr = "";
String[] paramNames = methodSignature.getParameterNames();
for (String paramName : paramNames) {
if (paramNameStr.length() > 0) paramNameStr += ",";
paramNameStr += paramName;
}
return targetName + "." + methodName + "(" + paramNameStr + ")";
}
/**
* 獲取被注解的方法所屬的模塊.
* @param joinPoint
* @return
* @throws Exception
*/
private String getModule(ProceedingJoinPoint joinPoint) throws Exception {
Method m = getMethod(joinPoint);
if (m == null) {
String methodDesc = this.getMethodDesc(joinPoint);
throw new RuntimeException("找不到被注解的方法:" + methodDesc);
} else {
AuthCheck authCheck = m.getAnnotation(AuthCheck.class);
if (authCheck != null) {
return authCheck.module();
} else {
return null;
}
}
}
/**
* 獲取被注解的方法所屬的操作.
* @param joinPoint
* @return
* @throws Exception
*/
private String getOper(ProceedingJoinPoint joinPoint) throws Exception {
Method m = getMethod(joinPoint);
if (m == null) {
String methodDesc = this.getMethodDesc(joinPoint);
throw new RuntimeException("找不到被注解的方法:" + methodDesc);
} else {
AuthCheck authCheck = m.getAnnotation(AuthCheck.class);
if (authCheck != null) {
return authCheck.oper();
} else {
return null;
}
}
}
/**
* 獲取被注解的方法所屬的模塊.
* @param joinPoint
* @return
* @throws Exception
*/
private boolean getFailWhenUserProfileIsNull(ProceedingJoinPoint joinPoint) throws Exception {
Method m = getMethod(joinPoint);
if (m == null) {
String methodDesc = this.getMethodDesc(joinPoint);
throw new RuntimeException("找不到被注解的方法:" + methodDesc);
} else {
AuthCheck authCheck = m.getAnnotation(AuthCheck.class);
if (authCheck != null) {
return authCheck.failWhenUserProfileIsNull();
} else {
return true;
}
}
}
/**
* 獲取被注解的方法在校驗失敗后返回的代碼.
* @param joinPoint
* @return
* @throws Exception
*/
private String getFailCode(ProceedingJoinPoint joinPoint) throws Exception {
Method m = getMethod(joinPoint);
if (m == null) {
String methodDesc = this.getMethodDesc(joinPoint);
throw new RuntimeException("找不到被注解的方法:" + methodDesc);
} else {
AuthCheck authCheck = m.getAnnotation(AuthCheck.class);
if (authCheck != null) {
return authCheck.failCode();
} else {
return null;
}
}
}
/**
* 獲取被注解的方法所屬的userProfile.
* @param joinPoint
* @return
* @throws Exception
*/
private UserProfile getUserProfile(ProceedingJoinPoint joinPoint) throws Exception {
Method m = getMethod(joinPoint);
if (m == null) {
String methodDesc = this.getMethodDesc(joinPoint);
throw new RuntimeException("找不到被注解的方法:" + methodDesc);
} else {
AuthCheck authCheck = m.getAnnotation(AuthCheck.class);
if (authCheck != null) {
String userProfileSetting = authCheck.userProfile();
if (userProfileSetting == null) throw new RuntimeException("userProfileSetting is null");
userProfileSetting = userProfileSetting.trim();
//logService.debug("userProfileSetting:" + userProfileSetting);
if (userProfileSetting.endsWith("()")) {
//應(yīng)該是一個方法名,那么通過反射的形式來獲取相應(yīng)的值
String methodName = userProfileSetting.substring(0, userProfileSetting.length() - 2);
Object returnVal = this.getReturnVal(methodName, joinPoint);
if (returnVal == null) {
return null;
} else {
return (UserProfile) returnVal;
}
} else {
//應(yīng)該是參數(shù)名,那么尋找對應(yīng)的參數(shù)值
//尋找對應(yīng)的參數(shù)值
Object paramVal = getParamVal(userProfileSetting, joinPoint);
if (paramVal == null) {
return null;
} else {
return (UserProfile) paramVal;
}
}
} else {
return null;
}
}
}
/**
* 獲取被注解的方法對應(yīng)的操作相關(guān)的業(yè)務(wù)實體ID.
* @param joinPoint
* @return
* @throws Exception
*/
private Object getEnityId(ProceedingJoinPoint joinPoint) throws Exception {
String entityIdSetting = getEntityIdSetting(joinPoint);
if (entityIdSetting.length() == 0) {
return null;
}
if (entityIdSetting.endsWith("()")) {
//應(yīng)該是一個方法名膊夹,那么通過反射的形式來獲取相應(yīng)的值
String methodName = entityIdSetting.substring(0, entityIdSetting.length() - 2);
return this.getReturnVal(methodName, joinPoint);
} else {
//應(yīng)該是參數(shù)名扔茅,那么尋找對應(yīng)的參數(shù)值
//尋找對應(yīng)的參數(shù)值
return getParamVal(entityIdSetting, joinPoint);
}
}
/**
* 獲取被注解的方法所屬的entity.
* @param joinPoint
* @return
* @throws Exception
*/
private Object getEntity(ProceedingJoinPoint joinPoint) throws Exception {
String entitySetting = getEntitySetting(joinPoint);
if (entitySetting.length() == 0) {
return null;
}
if (entitySetting.endsWith("()")) {
//應(yīng)該是一個方法名,那么通過反射的形式來獲取相應(yīng)的值
String methodName = entitySetting.substring(0, entitySetting.length() - 2);
return this.getReturnVal(methodName, joinPoint);
} else {
//應(yīng)該是參數(shù)名诉字,那么尋找對應(yīng)的參數(shù)值
//尋找對應(yīng)的參數(shù)值
return getParamVal(entitySetting, joinPoint);
}
}
/**
* 獲取被注解的方法所屬的dbYear設(shè)置值字符串,它的含義可能是一個參數(shù)名,也可能是一個方法名.
* @param joinPoint
* @return
* @throws Exception
*/
private String getDbYearSetting(ProceedingJoinPoint joinPoint) throws Exception {
Method m = getMethod(joinPoint);
if (m == null) {
String methodDesc = this.getMethodDesc(joinPoint);
throw new RuntimeException("找不到被注解的方法:" + methodDesc);
} else {
AuthCheck authCheck = m.getAnnotation(AuthCheck.class);
if (authCheck == null) {
EIPService.getLogService().error("無法找到相應(yīng)的注解:" + getMethodDesc(joinPoint));
return "";
}
String dbYear = authCheck.dbYear();
if (dbYear == null) throw new RuntimeException("dbYear is null");
return dbYear.trim();
}
}
/**
* 獲取被注解的方法所屬的entityId設(shè)置值字符串但汞,它的含義可能是一個參數(shù)名,也可能是一個方法名.
* @param joinPoint
* @return
* @throws Exception
*/
private String getEntityIdSetting(ProceedingJoinPoint joinPoint) throws Exception {
Method m = getMethod(joinPoint);
if (m == null) {
String methodDesc = this.getMethodDesc(joinPoint);
throw new RuntimeException("找不到被注解的方法:" + methodDesc);
} else {
AuthCheck authCheck = m.getAnnotation(AuthCheck.class);
if (authCheck == null) {
EIPService.getLogService().error("無法找到相應(yīng)的注解:" + getMethodDesc(joinPoint));
return "";
}
String entityId = authCheck.entityId();
if (entityId == null) return "";
return entityId.trim();
}
}
/**
* 獲取被注解的方法所屬的entity設(shè)置值字符串互站,它的含義可能是一個參數(shù)名私蕾,也可能是一個方法名.
* @param joinPoint
* @return
* @throws Exception
*/
private String getEntitySetting(ProceedingJoinPoint joinPoint) throws Exception {
Method m = getMethod(joinPoint);
if (m == null) {
String methodDesc = this.getMethodDesc(joinPoint);
throw new RuntimeException("找不到被注解的方法:" + methodDesc);
} else {
AuthCheck authCheck = m.getAnnotation(AuthCheck.class);
if (authCheck == null) {
EIPService.getLogService().error("無法找到相應(yīng)的注解:" + getMethodDesc(joinPoint));
return "";
}
String entity = authCheck.entity();
if (entity == null) return "";
return entity.trim();
}
}
/**
* 獲取被注解的方法的指定名稱的參數(shù)值.
* @param paramName
* @param joinPoint
* @return
*/
private Object getParamVal(String paramName, ProceedingJoinPoint joinPoint) {
String pName = CommonUtil.trim(paramName);
if (pName.length() == 0) {
getLogService().error("paramName is empty.");
return null;
}
//尋找對應(yīng)的參數(shù)值
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
String[] paramNames = methodSignature.getParameterNames();
Object[] paramVals = joinPoint.getArgs();
for (int i = 0; i < paramNames.length; i++) {
String name = paramNames[i];
//logService.debug(paramNames[i] + ":" + paramVals[i]);
if (pName.equals(name)) {
return paramVals[i];
}
}
getLogService().error("找不到指定的參數(shù):" + paramName + ", 方法:" + this.getMethodDesc(joinPoint));
return null;
}
/**
* 獲取指定方法(沒有任何參數(shù))的返回值.
* @param methodName
* @param joinPoint
* @return
*/
private Object getReturnVal(String methodName, ProceedingJoinPoint joinPoint) {
String mName = CommonUtil.trim(methodName);
if (mName.length() == 0) {
getLogService().error("找不到指定的方法:" + methodName + ", 類:" + joinPoint.getTarget().getClass().getName());
return null;
}
Object target = joinPoint.getTarget();
List < Method > methods = getMethods(target.getClass().getName(), methodName);
Method method = null;
for (Method mthd : methods) {
if (mthd.getParameterTypes().length > 0) {
continue;
} else {
method = mthd;
break;
}
}
if (method == null) {
String msg = "無法在類中找到指定方法:" + methodName + ", 所屬類:" + target.getClass().getName();
EIPService.getLogService().error(msg);
throw new RuntimeException(msg);
} else {
//以反射形式執(zhí)行
Object returnVal = null;
try {
method.setAccessible(true); //據(jù)稱設(shè)置為true可以讓java不再檢查方法是否私有等,有助性能提高
returnVal = method.invoke(target);
} catch (Throwable e) {
e.printStackTrace();
}
if (returnVal == null) {
String msg = "指定方法返回null值或沒有返回值:" + methodName + ", 所屬類:" + target.getClass().getName();
EIPService.getLogService().error(msg);
throw new RuntimeException(msg);
} else {
return returnVal;
}
}
}
/**
* 獲取被注解的方法所屬的userProfile.
* @param joinPoint
* @return
* @throws Exception
*/
private int getDbYear(ProceedingJoinPoint joinPoint) throws Exception {
String dbYearSetting = getDbYearSetting(joinPoint);
if (dbYearSetting.length() == 0) {
return 0;
}
if (dbYearSetting.endsWith("()")) {
//應(yīng)該是一個方法名胡桃,那么通過反射的形式來獲取相應(yīng)的值
String methodName = dbYearSetting.substring(0, dbYearSetting.length() - 2);
Object returnVal = this.getReturnVal(methodName, joinPoint);
if (returnVal == null) {
return 0;
} else {
return CommonUtil.parseInt(returnVal.toString());
}
} else {
//應(yīng)該是參數(shù)名踩叭,那么尋找對應(yīng)的參數(shù)值
//尋找對應(yīng)的參數(shù)值
Object paramVal = getParamVal(dbYearSetting, joinPoint);
if (paramVal == null) {
return 0;
} else {
return CommonUtil.parseInt(paramVal.toString(), 0);
}
}
}
/**
* 獲取被注解的方法.
* @param joinPoint
* @return
* @throws Exception
*/
private Method getMethod(ProceedingJoinPoint joinPoint) throws Exception {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
List < Method > methods = getMethods(targetName, methodName);
if (methods == null || methods.size() == 0) return null; //不存在,不過這個一般是不可能的
if (methods.size() == 1) return methods.get(0);
for (Method m : methods) {
//如果不止一個同名方法翠胰,那么要判斷一番
Class < ? > [] paramTypes = m.getParameterTypes();
if (paramTypes.length != arguments.length) continue; //參數(shù)的個數(shù)不一樣容贝,跳過
//檢查是否真的匹配
boolean matched = true; //先假設(shè)是匹配的
for (int i = 0; i < paramTypes.length; i++) {
Class < ? > paramType = paramTypes[i];
Object argument = arguments[i];
if (argument == null) continue;
if (!paramType.isInstance(argument)) {
matched = false;
break; //不匹配,不需要再繼續(xù)
}
}
if (matched) return m;
}
return null;
}
}
權(quán)限控制邏輯處理
1.通過反射機(jī)制在目標(biāo)對象中獲取用戶信息
2.通過應(yīng)用id及權(quán)限項找到所有有該用戶權(quán)限的用戶
3.若所有有該權(quán)限用戶集合中包含此目標(biāo)用戶則放行