今天看了一下黑馬程序員的視頻,上面講到一個使用sPRing AOP + 自定義注解的方式來實現權限控制的一個小例子,個人覺得還是可以借鑒,整理出來與大家分享。
需求:service層有一些方法,這些方法需要不同的權限才能訪問。
實現方案:自定義一個PrivilegeInfo的注解,使用這個注解為service層中的方法進行權限配置,在aop中根據PrivilegeInfo注解的值,判斷用戶是否擁有訪問目標方法的權限,有則訪問目標方法,沒有則給出提示。
關鍵技術:自定義注解及注解解析,spring aop
最終實現后的目錄結構:

具體步驟: 下面我們來具體實現這個需求。 首先來實現這個自定義注解,為了簡單起見,我們演示的這個注解,只是給了一個權限名的屬性。
package privilege.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * 權限注解 * @author Minhellic * */@Target(ElementType.METHOD)//這個注解是應用在方法上@Retention(RetentionPolicy.RUNTIME)public @interface PrivilegeInfo { /** * 權限的名稱 * @return */ String value() default "";}123456789101112131415161718192021123456789101112131415161718192021為這個自定義的注解,寫一個解析器,主要是用于返回目標方法上的注解PrivilegeInfo設置的value值
package privilege.annotation;import java.lang.reflect.Method;/** * 權限注解解析器 * 這個解析器的主要功能,是解析目標方法上如果有PrivilegeInfo注解,那么解析出這個注解中的value值(權限的值) * @author Minhellic * */public class PrivilegeAnnotationParse { /** * 解析注解 * @param targetClass 目標類的class形式 * @param methodName 在客戶端調用哪個方法,methodName就代表哪個方法 * @return * @throws Exception */ public static String parse(Class targetClass, String methodName) throws Exception { String methodaccess = ""; /* * 為簡單起見,這里考慮該方法沒有參數 */ Method method = targetClass.getMethod(methodName); //判斷方法上是否有Privilege注解 if (method.isAnnotationPresent(PrivilegeInfo.class)) { //得到方法上的注解 PrivilegeInfo privilegeInfo = method.getAnnotation(PrivilegeInfo.class); methodAccess = privilegeInfo.value(); } return methodAccess; }}1234567891011121314151617181920212223242526272829303132333412345678910111213141516171819202122232425262728293031323334自定義的注解和解析器有了,我們把對應的Service層寫出來,并在需要使用這個注解配置權限的方法上,添加上這個注解。我們知道Service層是由接口和實現類組成,這是規范,雖然對我們這次的演示沒有什么作用,但我們還是遵守一下:
接口源碼:
package privilege.service;/** * 用戶業務接口 * @author Minhellic * */public interface FirmService { /** * 在需要權限的目標方法上,使用PrivilegeInfo注解,配置權限為save */ public void save(); /** * 在需要權限的目標方法上,使用PrivilegeInfo注解,配置權限為update */ public void update(); /** * 不需要權限的目標方法上,則不添加PrivilegeInfo注解 * 在切面中,默認用戶擁有權限 */ public void get();}12345678910111213141516171819202122231234567891011121314151617181920212223實現類源碼:
package privilege.service.impl;import privilege.annotation.PrivilegeInfo;import privilege.service.FirmService;/** * 用戶業務實現 * @author Minhellic * */public class FirmServiceImpl implements FirmService { /** * 在需要權限的目標方法上,使用PrivilegeInfo注解,配置權限 */ @Override @PrivilegeInfo("save") public void save() { System.out.println("FirmServiceImpl.save()"); } /** * 在需要權限的目標方法上,使用PrivilegeInfo注解,配置權限 */ @Override @PrivilegeInfo("update") public void update() { System.out.println("FirmServiceImpl.update()"); } /** * 不需要權限的目標方法上,則不添加PrivilegeInfo注解 * 在切面中,默認用戶擁有權限 */ @Override public void get() { System.out.println("FirmServiceImpl.get()"); }}1234567891011121314151617181920212223242526272829303132333435363738394041424312345678910111213141516171819202122232425262728293031323334353637383940414243為了更好地管理權限,我們專門建立一個權限類,當然,為了簡單,這里還是只封裝了權限的名稱
package privilege.userprivilege;/** * 封裝用戶權限 * 為簡單,只封裝了權限的名稱 * @author Minhellic * */public class FirmPrivilege { /** * 用戶權限的名稱 */ private String value; public String getValue() { return value; } public void setValue(String value) { this.value = value; } public FirmPrivilege(String value) { this.value = value; } public FirmPrivilege() { }}1234567891011121314151617181920212223242526272829303112345678910111213141516171819202122232425262728293031現在Service層和注解都已經有了,就需要寫切面的代碼了。 在切面中,我們使用環繞通知,在調用目標方法之前,我們先用目標方法上的PrivilegeInfo注解配置的權限,與用戶擁有的權限進行匹配,如果匹配成功,則認為用戶擁有這個目標方法的權限,則調用目標方法,否則,給出提示信息,不調用目標方法。 這里之所以不使用前置通知的方式來匹配權限,就是因為前置通知雖然也可以檢查,但是無論檢查是否通過,目標方法都會被調用,起不到根據權限來控制目標方法調用的目的。
package privilege.aspect;import java.util.List;import org.aspectj.lang.ProceedingJoinPoint;import privilege.annotation.PrivilegeAnnotationParse;import privilege.userprivilege.FirmPrivilege;/** * 權限檢查切面 * 根據用戶原有的權限,與目標方法的權限配置進行匹配, * 如果目標方法需要的權限在用戶原有的權限以內,則調用目標方法 * 如果不匹配,則不調用目標方法 * @author Minhellic * */public class PrivilegeAspect { /** * 用戶本身的權限 */ private List<FirmPrivilege> privileges; public List<FirmPrivilege> getPrivileges() { return privileges; } public void setPrivileges(List<FirmPrivilege> privileges) { this.privileges = privileges; } /** * aop中的環繞通知 * 在這個方法中檢查用戶的權限和目標方法的需要的權限是否匹配 * 如果匹配則調用目標方法,不匹配則不調用 * @param joinPoint 連接點 * @throws Throwable */ public void isAccessMethod(ProceedingJoinPoint joinPoint) throws Throwable { /** * 1.獲取訪問目標方法應該具備的權限 * 為解析目標方法的PrivilegeInfo注解,根據我們定義的解析器,需要得到:目標類的class形式 方法的名稱 */ Class targetClass = joinPoint.getTarget().getClass(); String methodName = joinPoint.getSignature().getName(); //得到該方法的訪問權限 String methodAccess = PrivilegeAnnotationParse.parse(targetClass, methodName); /* * 2.遍歷用戶的權限,看是否擁有目標方法對應的權限 */ boolean isAccessed = false; for (FirmPrivilege firmPrivilege : privileges) { /* * 如果目標方法沒有使用PrivilegeInfo注解,則解析出來的權限字符串就為空字符串 * 則默認用戶擁有這個權限 */ if ("".equals(methodAccess)) { isAccessed = true; break; } /* * 用戶原有權限列表中有的權限與目標方法上PrivilegeInfo注解配置的權限進行匹配 */ if (firmPrivilege.getValue() != null && firmPrivilege.getValue().equalsIgnoreCase(methodAccess)) { isAccessed = true; break; } } /* * 3.如果用戶擁有權限,則調用目標方法 ,如果沒有,則不調用目標方法,只給出提示 */ if (isAccessed) { joinPoint.proceed();//調用目標方法 } else { System.out.println("你沒有權限"); } }}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879801234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980最后,配置好spring的配置文件,要使用spring aop,配置文件中,必須包含有aop的命名空間,并引入相應的xsd

配置文件的源碼為:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="firmService" class="privilege.service.impl.FirmServiceImpl"></bean> <bean id="privilegeAspect" class="privilege.aspect.PrivilegeAspect"></bean> <!-- 配置切面 --> <aop:config> <!-- 切入點表達式,確認目標類 privilege.service.impl包中的所有類中的所有方法 --> <aop:pointcut expression="execution(* privilege.service.impl.*.*(..))" id="perform"/> <!-- ref指向的對象就是切面 --> <aop:aspect ref="privilegeAspect"> <!-- 環繞通知 --> <aop:around method="isAccessMethod" pointcut-ref="perform"/> </aop:aspect> </aop:config></beans>123456789101112131415161718192021222324252627123456789101112131415161718192021222324252627所有的工作都已做好,我們來測試一下:
package privilege.test;import java.util.ArrayList;import java.util.List;import org.junit.Before;import org.junit.Test;import org.springframework.context.applicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import privilege.aspect.PrivilegeAspect;import privilege.service.FirmService;import privilege.userprivilege.FirmPrivilege;/** * aop+注解權限控制測試類 * * @author Minhellic * */public class PrivilegeTest { /** * 客戶端直接調用這個Service的方法,而不需要關心權限問題 */ private FirmService firmService; /** * 在初始化方法中,初始化firmService * 同時為用戶賦上原始權限,這個在項目中,會使用別的方式實現,這里只是模擬,就不搞那么復雜了 */ @Before public void init() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); firmService = (FirmService) context.getBean("firmService"); /* * 給用戶添加默認權限 */ PrivilegeAspect privilegeAspect = (PrivilegeAspect) context.getBean("privilegeAspect"); List<FirmPrivilege> privileges = new ArrayList<FirmPrivilege>(); //privileges.add(new FirmPrivilege("save")); privileges.add(new FirmPrivilege("update")); privilegeAspect.setPrivileges(privileges); } /** * 客戶端直接調用Service中的方法,而不需要關心權限問題,會有切面去做 */ @Test public void test() { firmService.save(); firmService.update(); firmService.get(); }}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455561234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556運行test方法,根據控制臺的輸出結果,就可以看到權限控制是起到了作用,因為用戶初始權限中的save權限被注釋掉,則用戶不會擁有save權限,調用save方法時,提示沒有權限。 從上面的測試方法可以看出,使用了aop之后,我們只需要關心主要業務,而不需要再分心去管理權限問題。

這篇博客,雖是我本人整理,但所有的思路及實現方式,都來源于黑馬程序員的視頻,算是半原創吧。如寫得很爛,歡迎大神們噴,但請不要辱罵。
新聞熱點
疑難解答