目前爲止,咱們已經學習了動態代理技術和註解技術了。因而咱們想要爲以前的bookStore項目添加權限控制…..java
只有用戶有權限的時候,後臺管理才能夠進行相對應的操做…..web
以前咱們作權限管理系統的時候,是根據用戶請求的URI來判斷該連接是否須要權限的。此次咱們使用動態代理的技術和註解來判斷:用戶調用該方法時,檢查該方法是否須要權限…sql
根據MVC模式,咱們在web層都是調用service層來實現功能的。那麼咱們具體的思路是這樣的:數據庫
上次咱們作的權限管理系統是引入了角色這個概念的,此次主要爲了練習動態代理和註解技術,就以簡單爲主,不引入角色這個實體。直接是用戶和權限之間的關係了。markdown
public class Privilege { private String id ; private String name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
CREATE TABLE privilege ( id VARCHAR(40) PRIMARY KEY, name VARCHAR(40) );
privilege和user是多對多的關係,因而使用第三方表來維護他們的關係ide
CREATE TABLE user_privilege ( privilege_id VARCHAR(40), user_id VARCHAR(40), PRIMARY KEY (privilege_id, user_id), CONSTRAINT privilege_id_FK FOREIGN KEY (privilege_id) REFERENCES privilege(id), CONSTRAINT user_id_FK1 FOREIGN KEY (user_id) REFERENCES user(id) );
爲了方便,直接添加數據了。就不寫詳細的DAO了。學習
後面在動態代理中,咱們須要檢查該用戶是否有權限…那麼就必須查找出該用戶擁有的哪些權限。再看看用戶有沒有相對應的權限測試
//查找用戶的全部權限 public List<Privilege> findUserPrivilege(String user_id) { QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource()); String sql = "SELECT p.* FROM privilege p, user_privilege up WHERE p.id = up.privilege_id AND up.user_id = ?"; try { return (List<Privilege>) queryRunner.query(sql, new Object[]{user_id}, new BeanListHandler(Privilege.class)); } catch (SQLException e) { throw new RuntimeException(e); } }
List<Privilege> findUserPrivilege(String user_id);
@Retention(RetentionPolicy.RUNTIME) public @interface permission { String value(); }
@permission("添加分類") /*添加分類*/ public void addCategory(Category category) { categoryDao.addCategory(category); } /*查找分類*/ public void findCategory(String id) { categoryDao.findCategory(id); } @permission("查找分類") /*查看分類*/ public List<Category> getAllCategory() { return categoryDao.getAllCategory(); }
把Service的方法抽取成ServiceDao。在Servlet中,也是經過ServiceFactory來獲得Service的對象【和DaoFactory是相似的】this
@permission("添加分類") /*添加分類*/ void addCategory(Category category); /*查找分類*/ void findCategory(String id); @permission("查找分類") /*查看分類*/ List<Category> getAllCategory();
public class ServiceDaoFactory { private static final ServiceDaoFactory factory = new ServiceDaoFactory(); private ServiceDaoFactory() { } public static ServiceDaoFactory getInstance() { return factory; } //須要判斷該用戶是否有權限 public <T> T createDao(String className, Class<T> clazz, final User user) { System.out.println("添加分類進來了!"); try { //獲得該類的類型 final T t = (T) Class.forName(className).newInstance(); //返回一個動態代理對象出去 return (T) Proxy.newProxyInstance(ServiceDaoFactory.class.getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, PrivilegeException { //判斷用戶調用的是什麼方法 String methodName = method.getName(); System.out.println(methodName); //獲得用戶調用的真實方法,注意參數!!! Method method1 = t.getClass().getMethod(methodName,method.getParameterTypes()); //查看方法上有沒有註解 permission permis = method1.getAnnotation(permission.class); //若是註解爲空,那麼表示該方法並不須要權限,直接調用方法便可 if (permis == null) { return method.invoke(t, args); } //若是註解不爲空,獲得註解上的權限 String privilege = permis.value(); //設置權限【後面經過它來判斷用戶的權限有沒有本身】 Privilege p = new Privilege(); p.setName(privilege); //到這裏的時候,已是須要權限了,那麼判斷用戶是否登錄了 if (user == null) { //這裏拋出的異常是代理對象拋出的,sun公司會自動轉換成運行期異常拋出,因而在Servlet上咱們根據getCause()來判斷是否是該異常,從而作出相對應的提示。 throw new PrivilegeException("對不起請先登錄"); } //執行到這裏用戶已經登錄了,判斷用戶有沒有權限 Method m = t.getClass().getMethod("findUserPrivilege", String.class); List<Privilege> list = (List<Privilege>) m.invoke(t, user.getId()); //看下權限集合中有沒有包含方法須要的權限。使用contains方法,在Privilege對象中須要重寫hashCode和equals() if (!list.contains(p)) { //這裏拋出的異常是代理對象拋出的,sun公司會自動轉換成運行期異常拋出,因而在Servlet上咱們根據getCause()來判斷是否是該異常,從而作出相對應的提示。 throw new PrivilegeException("您沒有權限,請聯繫管理員!"); } //執行到這裏的時候,已經有權限了,因此能夠放行了 return method.invoke(t, args); } }); } catch (Exception e) { new RuntimeException(e); } return null; } }
當用戶沒有登錄或者沒有權限的時候,咱們應該給用戶一些友好的提示….因而咱們自定義了PrivilegeExceptionspa
public class PrivilegeException extends Exception { public PrivilegeException() { super(); } public PrivilegeException(String message) { super(message); } public PrivilegeException(String message, Throwable cause) { super(message, cause); } public PrivilegeException(Throwable cause) { super(cause); } }
咱們繼承的是Exception,經過方法名拋出去。可是咱們是經過代理對象調用方法的,因而sun公司的策略就是把它們轉換成運行期異常拋出去。
所以,咱們就在Servlet上獲得異常,再給出友好的提示。。
該權限控制是十分優雅的,只要我在Service層中添加一個註解…那麼當web層調用該方法的時候就須要判斷用戶有沒有該權限….