1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Spring Boot動(dòng)態(tài)權(quán)限變更實(shí)現(xiàn)的整體方案

        共 44950字,需瀏覽 90分鐘

         ·

        2021-07-05 13:52

        點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”

        優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

          作者 |  阿拉伯1999

        來源 |  urlify.cn/ZRriqe

        1、前言

        ??在Web項(xiàng)目中,權(quán)限管理即權(quán)限訪問控制為網(wǎng)站訪問安全提供了保障,并且很多項(xiàng)目使用了Session作為緩存,結(jié)合AOP技術(shù)進(jìn)行token認(rèn)證和權(quán)限控制。權(quán)限控制流程大致如下圖所示:

        ??現(xiàn)在,如果管理員修改了用戶的角色,或修改了角色的權(quán)限,都會(huì)導(dǎo)致用戶權(quán)限發(fā)生變化,此時(shí)如何實(shí)現(xiàn)動(dòng)態(tài)權(quán)限變更,使得前端能夠更新用戶的權(quán)限樹,后端訪問鑒權(quán)AOP模塊能夠知悉這種變更呢?

        2、問題及解決方案

        ??現(xiàn)在的問題是,管理員沒法訪問用戶Session,因此沒法將變更通知此用戶。而用戶如果已經(jīng)登錄,或直接關(guān)閉瀏覽器頁面而不是登出操作,Session沒有過期前,用戶訪問接口時(shí),訪問鑒權(quán)AOP模塊仍然是根據(jù)之前緩存的Session信息進(jìn)行處理,沒法做到動(dòng)態(tài)權(quán)限變更。

        ??使用Security+WebSocket是一個(gè)方案,但沒法處理不在線用戶。

        ??解決方案的核心思想是利用ServletContext對象的共享特性,來實(shí)現(xiàn)用戶權(quán)限變更的信息傳遞。然后在AOP類中查詢用戶是否有變更通知記錄需要處理,如果權(quán)限發(fā)生變化,則修改response消息體,添加附加通知信息給前端。前端收到附加的通知信息,可更新功能權(quán)限樹,并進(jìn)行相關(guān)處理。

        ??這樣,利用的變更通知服務(wù),不僅后端的用戶url訪問接口可第一時(shí)間獲悉變更,還可以通知到前端,從而實(shí)現(xiàn)了動(dòng)態(tài)權(quán)限變更。

        3、方案實(shí)現(xiàn)

        3.1、開發(fā)變更通知類

        ??服務(wù)接口類ChangeNotifyService,代碼如下:

        package com.abc.questInvest.service;

        /**
         * @className  : ChangeNotifyService
         * @description  : 變更通知服務(wù)
         * @summary  :
         * @history  :
         * ------------------------------------------------------------------------------
         * date   version  modifier  remarks                   
         * ------------------------------------------------------------------------------
         * 2021/06/28 1.0.0  sheng.zheng  初版
         *
         */
        public interface ChangeNotifyService {

         /**
          * 
          * @methodName  : getChangeNotifyInfo
          * @description  : 獲取指定用戶ID的變更通知信息 
          * @param userId : 用戶ID
          * @return  : 返回0表示無變更通知信息,其它值按照bitmap編碼。目前定義如下:
          *   bit0: : 修改用戶的角色組合值,從而導(dǎo)致權(quán)限變更;
          *   bit1: : 修改角色的功能項(xiàng),從而導(dǎo)致權(quán)限變更;
          *   bit2: : 用戶禁用,從而導(dǎo)致權(quán)限變更;
          *   bit3: : 用戶調(diào)整部門,從而導(dǎo)致數(shù)據(jù)權(quán)限變更;
          * @history  :
          * ------------------------------------------------------------------------------
          * date   version  modifier  remarks                   
          * ------------------------------------------------------------------------------
          * 2021/06/28 1.0.0  sheng.zheng  初版
          *
          */
         public Integer getChangeNotifyInfo(Integer userId);
         
         /**
          * 
          * @methodName  : setChangeNotifyInfo
          * @description  : 設(shè)置變更通知信息
          * @param userId : 用戶ID
          * @param changeNotifyInfo : 變更通知值
          *   bit0: : 修改用戶的角色組合值,從而導(dǎo)致權(quán)限變更;
          *   bit1: : 修改角色的功能項(xiàng),從而導(dǎo)致權(quán)限變更;
          *   bit2: : 用戶禁用,從而導(dǎo)致權(quán)限變更;
          *   bit3: : 用戶調(diào)整部門,從而導(dǎo)致數(shù)據(jù)權(quán)限變更;
          * @history  :
          * ------------------------------------------------------------------------------
          * date   version  modifier  remarks                   
          * ------------------------------------------------------------------------------
          * 2021/06/28 1.0.0  sheng.zheng  初版
          *
          */
         public void setChangeNotifyInfo(Integer userId,Integer changeNotifyInfo);  
        }

        ??服務(wù)實(shí)現(xiàn)類ChangeNotifyServiceImpl,代碼如下:

        package com.abc.questInvest.service.impl;

        import java.util.HashMap;
        import java.util.Map;

        import org.springframework.stereotype.Service;

        import com.abc.questInvest.service.ChangeNotifyService;

        /**
         * @className  : ChangeNotifyServiceImpl
         * @description  : ChangeNotifyService實(shí)現(xiàn)類
         * @summary  :
         * @history  :
         * ------------------------------------------------------------------------------
         * date   version  modifier  remarks                   
         * ------------------------------------------------------------------------------
         * 2021/06/28 1.0.0  sheng.zheng  初版
         *
         */
        @Service
        public class ChangeNotifyServiceImpl implements ChangeNotifyService {
         
         //用戶ID與變更過通知信息映射表
         private Map<Integer,Integer> changeNotifyMap = new HashMap<Integer,Integer>();
         
         /**
          * 
          * @methodName  : getChangeNotifyInfo
          * @description  : 獲取指定用戶ID的變更通知信息 
          * @param userId : 用戶ID
          * @return  : 返回0表示無變更通知信息,其它值按照bitmap編碼。目前定義如下:
          *   bit0: : 修改用戶的角色組合值,從而導(dǎo)致權(quán)限變更;
          *   bit1: : 修改角色的功能項(xiàng),從而導(dǎo)致權(quán)限變更;
          *   bit2: : 用戶禁用,從而導(dǎo)致權(quán)限變更;
          *   bit3: : 用戶調(diào)整部門,從而導(dǎo)致數(shù)據(jù)權(quán)限變更;
          * @history  :
          * ------------------------------------------------------------------------------
          * date   version  modifier  remarks                   
          * ------------------------------------------------------------------------------
          * 2021/06/28 1.0.0  sheng.zheng  初版
          *
          */
         @Override
         public Integer getChangeNotifyInfo(Integer userId) {
          Integer changeNotifyInfo = 0;
          //檢查該用戶是否有變更通知信息
          if (changeNotifyMap.containsKey(userId)) {
           changeNotifyInfo = changeNotifyMap.get(userId);
           //移除數(shù)據(jù),加鎖保護(hù)
           synchronized(changeNotifyMap) {
            changeNotifyMap.remove(userId);
           }
          }
          return changeNotifyInfo;
         }
         
         /**
          * 
          * @methodName  : setChangeNotifyInfo
          * @description  : 設(shè)置變更通知信息,該功能一般由管理員觸發(fā)調(diào)用
          * @param userId : 用戶ID
          * @param changeNotifyInfo : 變更通知值
          *   bit0: : 修改用戶的角色組合值,從而導(dǎo)致權(quán)限變更;
          *   bit1: : 修改角色的功能項(xiàng),從而導(dǎo)致權(quán)限變更;
          *   bit2: : 用戶禁用,從而導(dǎo)致權(quán)限變更;
          *   bit3: : 用戶調(diào)整部門,從而導(dǎo)致數(shù)據(jù)權(quán)限變更;
          * @history  :
          * ------------------------------------------------------------------------------
          * date   version  modifier  remarks                   
          * ------------------------------------------------------------------------------
          * 2021/06/28 1.0.0  sheng.zheng  初版
          *
          */
         @Override
         public void setChangeNotifyInfo(Integer userId,Integer changeNotifyInfo) {
          //檢查該用戶是否有變更通知信息
          if (changeNotifyMap.containsKey(userId)) {
           //如果有,表示之前變更通知未處理
           //獲取之前的值
           Integer oldChangeNotifyInfo = changeNotifyMap.get(userId);
           //計(jì)算新值。bitmap編碼,或操作
           Integer newChangeNotifyInfo = oldChangeNotifyInfo | changeNotifyInfo;
           //設(shè)置數(shù)據(jù),加鎖保護(hù)
           synchronized(changeNotifyMap) {
            changeNotifyMap.put(userId,newChangeNotifyInfo);
           }
          }else {
           //如果沒有,設(shè)置一條
           changeNotifyMap.put(userId,changeNotifyInfo);
          }
         }
        }

        ??此處,變更通知類型,與使用的demo項(xiàng)目有關(guān),目前定義了4種變更通知類型。實(shí)際上,除了權(quán)限相關(guān)的變更,還有與Session緩存字段相關(guān)的變更,也需要通知,否則用戶還是在使用舊數(shù)據(jù)。

        3.2、將變更通知類對象,納入全局配置服務(wù)對象中進(jìn)行管理

        ??全局配置服務(wù)類GlobalConfigService,負(fù)責(zé)管理全局的配置服務(wù)對象,服務(wù)接口類代碼如下:

        package com.abc.questInvest.service;

        /**
         * @className  : GlobalConfigService
         * @description  : 全局變量管理類
         * @summary  :
         * @history  :
         * ------------------------------------------------------------------------------
         * date   version  modifier  remarks                   
         * ------------------------------------------------------------------------------
         * 2021/06/02 1.0.0  sheng.zheng  初版
         *
         */
        public interface GlobalConfigService {
         
         /**
          * 
          * @methodName  : loadData
          * @description  : 加載數(shù)據(jù) 
          * @return  : 成功返回true,否則返回false
          * @history  :
          * ------------------------------------------------------------------------------
          * date   version  modifier  remarks                   
          * ------------------------------------------------------------------------------
          * 2021/06/02 1.0.0  sheng.zheng  初版
          *
          */
         public boolean loadData();
         
         //獲取TableCodeConfigService對象
         public TableCodeConfigService getTableCodeConfigService(); 
         
         //獲取SysParameterService對象
         public SysParameterService getSysParameterService();
         
         //獲取FunctionTreeService對象
         public FunctionTreeService getFunctionTreeService();

         //獲取RoleFuncRightsService對象
         public RoleFuncRightsService getRoleFuncRightsService();
         
         //獲取ChangeNotifyService對象
         public ChangeNotifyService getChangeNotifyService();
         
        }

        ??服務(wù)實(shí)現(xiàn)類GlobalConfigServiceImpl,代碼如下:

        package com.abc.questInvest.service.impl;

        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.stereotype.Service;

        import com.abc.questInvest.service.ChangeNotifyService;
        import com.abc.questInvest.service.FunctionTreeService;
        import com.abc.questInvest.service.GlobalConfigService;
        import com.abc.questInvest.service.RoleFuncRightsService;
        import com.abc.questInvest.service.SysParameterService;
        import com.abc.questInvest.service.TableCodeConfigService;

        /**
         * @className  : GlobalConfigServiceImpl
         * @description  : GlobalConfigService實(shí)現(xiàn)類
         * @summary  :
         * @history  :
         * ------------------------------------------------------------------------------
         * date   version  modifier  remarks                   
         * ------------------------------------------------------------------------------
         * 2021/06/02 1.0.0  sheng.zheng  初版
         *
         */
        @Service
        public class GlobalConfigServiceImpl implements GlobalConfigService{
         
         //ID編碼配置表數(shù)據(jù)服務(wù)
         @Autowired
         private TableCodeConfigService tableCodeConfigService;
         
         //系統(tǒng)參數(shù)表數(shù)據(jù)服務(wù)
         @Autowired
         private SysParameterService sysParameterService;
         
         //功能樹表數(shù)據(jù)服務(wù)
         @Autowired
         private FunctionTreeService functionTreeService;
         
         //角色權(quán)限表數(shù)據(jù)服務(wù)
         @Autowired 
         private RoleFuncRightsService roleFuncRightsService;
         
         //變更通知服務(wù)
         @Autowired 
         private ChangeNotifyService changeNotifyService;
         
         
         /**
          * 
          * @methodName  : loadData
          * @description  : 加載數(shù)據(jù) 
          * @return  : 成功返回true,否則返回false
          * @history  :
          * ------------------------------------------------------------------------------
          * date   version  modifier  remarks                   
          * ------------------------------------------------------------------------------
          * 2021/06/02 1.0.0  sheng.zheng  初版
          *
          */
         @Override
         public boolean loadData() {
          boolean bRet = false;
          
          //加載table_code_config表記錄
          bRet = tableCodeConfigService.loadData();
          if (!bRet) {
           return bRet;
          }
          
          //加載sys_parameters表記錄
          bRet = sysParameterService.loadData();
          if (!bRet) {
           return bRet;
          }
          
          //changeNotifyService目前沒有持久層,無需加載
          //如果服務(wù)重啟,信息丟失,也沒關(guān)系,因?yàn)榇藭r(shí)Session也會(huì)失效
          
          //加載function_tree表記錄
          bRet = functionTreeService.loadData();
          if (!bRet) {
           return bRet;
          }
          
          //加載role_func_rights表記錄
          //先設(shè)置完整功能樹
          roleFuncRightsService.setFunctionTree(functionTreeService.getFunctionTree());
          //然后加載數(shù)據(jù)
          bRet = roleFuncRightsService.loadData();
          if (!bRet) {
           return bRet;
          }
          
          return bRet;
         }
         
         //獲取TableCodeConfigService對象
         @Override
         public TableCodeConfigService getTableCodeConfigService() {
          return tableCodeConfigService;
         }
         
         //獲取SysParameterService對象
         @Override
         public SysParameterService getSysParameterService() {
          return sysParameterService;
         }
         
         //獲取FunctionTreeService對象
         @Override
         public FunctionTreeService getFunctionTreeService() {
          return functionTreeService;
         } 
         
         //獲取RoleFuncRightsService對象
         @Override
         public RoleFuncRightsService getRoleFuncRightsService() {
          return roleFuncRightsService;
         }
         
         //獲取ChangeNotifyService對象
         @Override
         public ChangeNotifyService getChangeNotifyService() {
          return changeNotifyService;
         }

        }

        ??GlobalConfigServiceImpl類,管理了很多配置服務(wù)類,此處主要關(guān)注ChangeNotifyService類對象。

        3.3、使用ServletContext,管理全局配置服務(wù)類對象

        ??全局配置服務(wù)類在應(yīng)用啟動(dòng)時(shí)加載到Spring容器中,這樣可實(shí)現(xiàn)共享,減少對數(shù)據(jù)庫的訪問壓力。

        ??實(shí)現(xiàn)一個(gè)ApplicationListener類,代碼如下:

        package com.abc.questInvest;

        import javax.servlet.ServletContext;
        import org.springframework.context.ApplicationListener;
        import org.springframework.context.event.ContextRefreshedEvent;
        import org.springframework.stereotype.Component;
        import org.springframework.web.context.WebApplicationContext;

        import com.abc.questInvest.service.GlobalConfigService;

        /**
         * @className : ApplicationStartup
         * @description : 應(yīng)用偵聽器
         *
         */
        @Component
        public class ApplicationStartup implements ApplicationListener<ContextRefreshedEvent>{
            //全局變量管理對象,此處不能自動(dòng)注入
            private GlobalConfigService globalConfigService = null;
            
            @Override
            public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
                try {
                 if(contextRefreshedEvent.getApplicationContext().getParent() == null){ 
                  //root application context 沒有parent.
            
                  System.out.println("========定義全局變量==================");
                  // 將 ApplicationContext 轉(zhuǎn)化為 WebApplicationContext
                     WebApplicationContext webApplicationContext =
                             (WebApplicationContext)contextRefreshedEvent.getApplicationContext();
                     // 從 webApplicationContext 中獲取  servletContext
                     ServletContext servletContext = webApplicationContext.getServletContext();
                     
                     //加載全局變量管理對象
                     globalConfigService = (GlobalConfigService)webApplicationContext.getBean(GlobalConfigService.class);
                     //加載數(shù)據(jù)
                     boolean bRet = globalConfigService.loadData();
                     if (false == bRet) {
                      System.out.println("加載全局變量失敗");
                      return;
                     }        
                     //======================================================================
                     // servletContext設(shè)置值
                     servletContext.setAttribute("GLOBAL_CONFIG_SERVICE", globalConfigService);  
                     
                 }
             } catch (Exception e) {
                 e.printStackTrace();
             }        
            }
        }

        ??在啟動(dòng)類中,加入該應(yīng)用偵聽器ApplicationStartup。

         public static void main(String[] args) {
             SpringApplication springApplication = new SpringApplication(QuestInvestApplication.class);
                springApplication.addListeners(new ApplicationStartup());
                springApplication.run(args);  
         }

        ??現(xiàn)在,有了一個(gè)GlobalConfigService類型的全局變量globalConfigService。

        3.4、發(fā)出變更通知

        ??此處舉2個(gè)例子,說明發(fā)出變更通知的例子,這兩個(gè)例子,都在用戶管理模塊,UserManServiceImpl類中。

        ??1)管理員修改用戶信息,可能導(dǎo)致權(quán)限相關(guān)項(xiàng)發(fā)生變動(dòng),2)禁用用戶,發(fā)出變更過通知。

        ??發(fā)出通知的相關(guān)代碼如下:

         /**
          * 
          * @methodName  : editUser
          * @description  : 修改用戶信息
          * @param userInfo : 用戶信息對象
          * @history  :
          * ------------------------------------------------------------------------------
          * date   version  modifier  remarks                   
          * ------------------------------------------------------------------------------
          * 2021/06/08 1.0.0  sheng.zheng  初版
          * 2021/06/28 1.0.1  sheng.zheng  增加變更通知的處理
          *
          */
         @Override
         public void editUser(HttpServletRequest request,UserInfo userInfo) {
          //輸入?yún)?shù)校驗(yàn)
          checkValidForParams("editUser",userInfo);
          
          //獲取操作人賬號
          String operatorName = (String) request.getSession().getAttribute("username");
          userInfo.setOperatorName(operatorName);  

          //登錄名和密碼不修改
          userInfo.setLoginName(null);
          userInfo.setSalt(null);
          userInfo.setPasswd(null);
          
          //獲取修改之前的用戶信息
          Integer userId = userInfo.getUserId();
          UserInfo oldUserInfo = userManDao.selectUserByKey(userId);

          //修改用戶記錄
          try {
           userManDao.updateSelective(userInfo);   
          }catch(Exception e) {
           e.printStackTrace();
           log.error(e.getMessage());
           throw new BaseException(ExceptionCodes.USERS_EDIT_USER_FAILED);
          }
          
          //檢查是否有需要通知的變更
          Integer changeFlag = 0;
          if (userInfo.getRoles() != null) {
           if(oldUserInfo.getRoles() != userInfo.getRoles()) {
            //角色組合有變化,bit0
            changeFlag |= 0x01;
           }
          }
          if (userInfo.getDeptId() != null) {
           if (oldUserInfo.getDeptId() != userInfo.getDeptId()) {
            //部門ID有變化,bit3
            changeFlag |= 0x08;
           }
          }
          if (changeFlag > 0) {
           //如果有變更過通知項(xiàng)
           //獲取全局變量
           ServletContext servletContext = request.getServletContext();
           GlobalConfigService globalConfigService = (GlobalConfigService)servletContext.getAttribute("GLOBAL_CONFIG_SERVICE");
           globalConfigService.getChangeNotifyService().setChangeNotifyInfo(userId, changeFlag);   
          }
         }

         /**
          * 
          * @methodName  : disableUser
          * @description  : 禁用用戶
          * @param params : map對象,形式如下:
          *  {
          *   "userId" : 1
          *  }
          * @history  :
          * ------------------------------------------------------------------------------
          * date   version  modifier  remarks                   
          * ------------------------------------------------------------------------------
          * 2021/06/08 1.0.0  sheng.zheng  初版
          * 2021/06/28 1.0.1  sheng.zheng  增加變更通知的處理
          *
          */
         @Override
         public void disableUser(HttpServletRequest request,Map<String,Object> params) {
          //輸入?yún)?shù)校驗(yàn)
          checkValidForParams("disableUser",params);
          
          UserInfo userInfo = new UserInfo();
          
          //獲取操作人賬號
          String operatorName = (String) request.getSession().getAttribute("username");
          
          //設(shè)置userInfo信息
          Integer userId = (Integer)params.get("userId");
          userInfo.setUserId(userId);
          userInfo.setOperatorName(operatorName);
          //設(shè)置禁用標(biāo)記
          userInfo.setDeleteFlag((byte)1);
          
          //修改密碼
          try {
           userManDao.updateEnable(userInfo);   
          }catch(Exception e) {
           e.printStackTrace();
           log.error(e.getMessage());
           throw new BaseException(ExceptionCodes.USERS_EDIT_USER_FAILED);
          }  
          
          //禁用用戶,發(fā)出變更通知
          //獲取全局變量
          ServletContext servletContext = request.getServletContext();
          GlobalConfigService globalConfigService = (GlobalConfigService)servletContext.getAttribute("GLOBAL_CONFIG_SERVICE");
          //禁用用戶:bit2
          globalConfigService.getChangeNotifyService().setChangeNotifyInfo(userId, 0x04);    
         }

        ??本demo項(xiàng)目的角色相對較少,沒有使用用戶角色關(guān)系表,而是使用了bitmap編碼,角色I(xiàn)D取值為2^n,用戶角色組合roles字段為一個(gè)Integer值。如roles=7,表示角色I(xiàn)D組合=[1,2,4]。
        ??另外,如果修改了角色的功能權(quán)限集合,則需要查詢受影響的用戶ID列表,依次發(fā)出通知,可類似處理。

        3.5、修改Response響應(yīng)消息體

        ??Response響應(yīng)消息體,為BaseResponse,代碼如下:

        package com.abc.questInvest.vo.common;

        import lombok.Data;

        /**
         * @className  : BaseResponse
         * @description  : 基本響應(yīng)消息體對象
         * @summary  :
         * @history  :
         * ------------------------------------------------------------------------------
         * date   version  modifier  remarks                   
         * ------------------------------------------------------------------------------
         * 2021/05/31 1.0.0  sheng.zheng  初版
         * 2021/06/28 1.0.1  sheng.zheng  增加變更通知的附加信息
         *
         */
        @Data
        public class BaseResponse<T> {
            //響應(yīng)碼
            private int code;

            //響應(yīng)消息
            private String message;
                
            //響應(yīng)實(shí)體信息
            private T data;

            //分頁信息
            private Page page;

            //附加通知信息
            private Additional additional;
        }

        ??BaseResponse類增加了Additional類型的additional屬性字段,用于輸出附加信息。

        ??Additional類的定義如下:

        package com.abc.questInvest.vo.common;

        import lombok.Data;

        /**
         * @className  : Additional
         * @description  : 附加信息
         * @summary  :
         * @history  :
         * ------------------------------------------------------------------------------
         * date   version  modifier  remarks                   
         * ------------------------------------------------------------------------------
         * 2021/06/28 1.0.0  sheng.zheng  初版
         *
         */
        @Data
        public class Additional {
            //通知碼,附加信息
            private int notifycode;

            //通知碼對應(yīng)的消息
            private String notification;
            
            //更新的token
            private String token;
            
            //更新的功能權(quán)限樹
            private String rights;

        }

        ??附加信息類Additional中,各屬性字段的說明:

        • notifycode,為通知碼,即可對應(yīng)通知消息的類型,目前只有一種,可擴(kuò)展。

        • notification,為通知碼對應(yīng)的消息。

        ??通知碼,在ExceptionCodes枚舉文件中定義:

            //變更通知信息
            USER_RIGHTS_CHANGED(51, "message.USER_RIGHTS_CHANGED""用戶權(quán)限發(fā)生變更"),
         ;  //end enum

            ExceptionCodes(int code, String messageId, String message) {
                this.code = code;
                this.messageId = messageId;
                this.message = message;
            }
        • token,用于要求前端更新token。更新token的目的是確認(rèn)前端已經(jīng)收到權(quán)限變更通知。因?yàn)橄麓蝩rl請求將使用新的token,如果前端未收到或未處理,仍然用舊的token訪問,就要跳到登錄頁了。

        • rights,功能樹的字符串輸出,是樹型結(jié)構(gòu)的JSON字符串。

        3.6、AOP鑒權(quán)處理

        ??AuthorizationAspect為鑒權(quán)認(rèn)證的切面類,代碼如下:

        package com.abc.questInvest.aop;

        import java.util.List;

        import javax.servlet.ServletContext;
        import javax.servlet.http.HttpServletRequest;

        import org.aspectj.lang.annotation.AfterReturning;
        import org.aspectj.lang.annotation.Aspect;
        import org.aspectj.lang.annotation.Before;
        import org.aspectj.lang.annotation.Pointcut;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.core.annotation.Order;
        import org.springframework.stereotype.Component;
        import org.springframework.web.context.request.RequestContextHolder;
        import org.springframework.web.context.request.ServletRequestAttributes;

        import com.abc.questInvest.common.constants.Constants;
        import com.abc.questInvest.common.utils.Utility;
        import com.abc.questInvest.dao.UserManDao;
        import com.abc.questInvest.entity.FunctionInfo;
        import com.abc.questInvest.entity.UserInfo;
        import com.abc.questInvest.exception.BaseException;
        import com.abc.questInvest.exception.ExceptionCodes;
        import com.abc.questInvest.service.GlobalConfigService;
        import com.abc.questInvest.service.LoginService;
        import com.abc.questInvest.vo.TreeNode;
        import com.abc.questInvest.vo.common.Additional;
        import com.abc.questInvest.vo.common.BaseResponse;

        /**
         * @className  : AuthorizationAspect
         * @description  : 接口訪問鑒權(quán)切面類
         * @summary  : 使用AOP,進(jìn)行token認(rèn)證以及用戶對接口的訪問權(quán)限鑒權(quán)
         * @history  :
         * ------------------------------------------------------------------------------
         * date   version  modifier  remarks                   
         * ------------------------------------------------------------------------------
         * 2021/06/06 1.0.0  sheng.zheng  初版
         * 2021/06/28 1.0.1  sheng.zheng  增加變更通知的處理,增加了afterReturning增強(qiáng)
         *
         */
        @Aspect
        @Component
        @Order(2)
        public class AuthorizationAspect {
         @Autowired
            private UserManDao userManDao;
         
         //設(shè)置切點(diǎn)
            @Pointcut("execution(public * com.abc.questInvest.controller..*.*(..))" +
            "&& !execution(public * com.abc.questInvest.controller.LoginController.*(..))" + 
            "&& !execution(public * com.abc.questInvest.controller.QuestInvestController.*(..))")    
            public void verify(){}
            
            @Before("verify()"
            public void doVerify(){ 
          ServletRequestAttributes attributes=(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

          HttpServletRequest request=attributes.getRequest(); 
          
          // ================================================================================
          // token認(rèn)證
          
          //從header中獲取token值
          String token = request.getHeader("Authorization");
          if (null == token || token.equals("")){ 
           //return;
           throw new BaseException(ExceptionCodes.TOKEN_IS_NULL); 
          } 
             
          //從session中獲取token和過期時(shí)間
          String sessionToken = (String)request.getSession().getAttribute("token");
          
          //判斷session中是否有信息,可能是非登錄用戶
          if (null == sessionToken || sessionToken.equals("")) {
           throw new BaseException(ExceptionCodes.TOKEN_WRONG);
          }
             
          //比較token
          if(!token.equals(sessionToken)) {
           //如果請求頭中的token與存在session中token兩者不一致
           throw new BaseException(ExceptionCodes.TOKEN_WRONG);   
          }
          
          long expireTime = (long)request.getSession().getAttribute("expireTime");
          //檢查過期時(shí)間
          long time = System.currentTimeMillis();
          if (time > expireTime) {
           //如果token過期
           throw new BaseException(ExceptionCodes.TOKEN_EXPIRED);
          }else {
           //token未過期,更新過期時(shí)間
           long newExpiredTime = time + Constants.TOKEN_EXPIRE_TIME * 1000;
           request.getSession().setAttribute("expireTime", newExpiredTime);
          }
          
          // ============================================================================
          // 接口調(diào)用權(quán)限
          //獲取用戶ID
          Integer userId = (Integer)request.getSession().getAttribute("userId"); 
          //獲取全局變量
          ServletContext servletContext = request.getServletContext();
          GlobalConfigService globalConfigService = (GlobalConfigService)servletContext.getAttribute("GLOBAL_CONFIG_SERVICE");
          
          //===================變更通知處理開始==============================================
          //檢查有無變更通知信息
          Integer changeNotifyInfo = globalConfigService.getChangeNotifyService().getChangeNotifyInfo(userId);
          //設(shè)置成員屬性為false
          boolean rightsChangedFlag = false;  
          if (changeNotifyInfo > 0) {
           //有通知信息
           if ((changeNotifyInfo & 0x09) > 0) {
            //bit0:修改用戶的角色組合值,從而導(dǎo)致權(quán)限變更
            //bit3:用戶調(diào)整部門,從而導(dǎo)致數(shù)據(jù)權(quán)限變更
            //mask 0b1001 = 0x09 
            //都需要查詢用戶表,并更新信息;合在一起查詢。
            UserInfo userInfo = userManDao.selectUserByKey(userId);
            //更新Session
                       request.getSession().setAttribute("roles", userInfo.getRoles());
                       request.getSession().setAttribute("deptId", userInfo.getDeptId()); 
                         if ((changeNotifyInfo & 0x01) > 0) {
                          //權(quán)限變更標(biāo)志置位
                          rightsChangedFlag = true;
                         }
           }else if((changeNotifyInfo & 0x02) > 0) {
            //bit1:修改角色的功能值,從而導(dǎo)致權(quán)限變更
                       //權(quán)限變更標(biāo)志置位
                     rightsChangedFlag = true;
           }else if((changeNotifyInfo & 0x04) > 0) {
            //bit2:用戶禁用,從而導(dǎo)致權(quán)限變更
            //設(shè)置無效token,可阻止該用戶訪問系統(tǒng)
            request.getSession().setAttribute("token""");
            //直接拋出異常,由前端顯示:Forbidden頁面
            throw new BaseException(ExceptionCodes.ACCESS_FORBIDDEN);
           }
           if (rightsChangedFlag == true) {
            //寫Session,用于將信息傳遞到afterReturning方法中
            request.getSession().setAttribute("rightsChanged", 1);
           }
          }
          //===================變更通知處理結(jié)束==============================================
            
          //從session中獲取用戶權(quán)限值
          Integer roles = (Integer)request.getSession().getAttribute("roles");
          //獲取當(dāng)前接口url值
          String servletPath = request.getServletPath();
            
          //獲取該角色對url的訪問權(quán)限
          Integer rights = globalConfigService.getRoleFuncRightsService().getRoleUrlRights(Utility.parseRoles(roles), servletPath);
          if (rights == 0) {
           //如果無權(quán)限訪問此接口,拋出異常,由前端顯示:Forbidden頁面
           throw new BaseException(ExceptionCodes.ACCESS_FORBIDDEN);
          }  
            }    
            
            @AfterReturning(value="verify()" ,returning="result")
            public void afterReturning(BaseResponse result) {
             //限制必須是BaseResponse類型,其它類型的返回值忽略
             //獲取Session
                ServletRequestAttributes sra = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
                HttpServletRequest request = sra.getRequest();
             Integer rightsChanged = (Integer)request.getSession().getAttribute("rightsChanged");
             if (rightsChanged != null && rightsChanged == 1) {
              //如果有用戶權(quán)限變更,通知前端來刷新該用戶的功能權(quán)限樹
              //構(gòu)造附加信息
              Additional additional = new Additional();
              additional.setNotifycode(ExceptionCodes.USER_RIGHTS_CHANGED.getCode());
              additional.setNotification(ExceptionCodes.USER_RIGHTS_CHANGED.getMessage());
              //更新token
              String loginName = (String)request.getSession().getAttribute("username");
              String token = LoginService.generateToken(loginName);
              additional.setToken(token);
              //更新token,要求下次url訪問使用新的token
              request.getSession().setAttribute("token", token);
              //獲取用戶的功能權(quán)限樹
              Integer roles = (Integer)request.getSession().getAttribute("roles");
              ServletContext servletContext = request.getServletContext();
              GlobalConfigService globalConfigService = (GlobalConfigService)servletContext.getAttribute("GLOBAL_CONFIG_SERVICE");
                 //獲取用戶權(quán)限的角色功能數(shù)
              List<Integer> roleList = Utility.parseRoles(roles);
                 TreeNode<FunctionInfo> rolesFunctionTree = 
                   globalConfigService.getRoleFuncRightsService().
                   getRoleRights(roleList);
                 additional.setRights(rolesFunctionTree.toString());
              //修改response信息
                 result.setAdditional(additional);
              //移除Session的rightsChanged項(xiàng)
              request.getSession().removeAttribute("rightsChanged");
             }
            }
        }

        ??AuthorizationAspect類定義了切點(diǎn)verify(),@Before增強(qiáng)用于鑒權(quán)驗(yàn)證,增加了對變更通知信息的處理。并利用Session,用rightsChanged屬性字段記錄需要通知前端的標(biāo)志,在@AfterReturning后置增強(qiáng)中根據(jù)該屬性字段的值,進(jìn)行一步的處理。

        ??@Before增強(qiáng)的doVerify方法中,如果發(fā)現(xiàn)角色組合有改變,但仍有訪問此url權(quán)限時(shí),會(huì)繼續(xù)后續(xù)處理,這樣不會(huì)中斷業(yè)務(wù);如果沒有訪問此url權(quán)限,則返回訪問受限異常信息,由前端顯示訪問受限頁碼(類似403 Forbidden 頁碼)。

        ??在后置增強(qiáng)@AfterReturning中,限定了返回值類型,如果該請求響應(yīng)的類型是BaseResponse類型,則修改reponse消息體,附加通知信息;如果不是,則不處理,會(huì)等待下一個(gè)url請求,直到返回類型是BaseResponse類型。也可以采用自定義response的header的方式,這樣,就無需等待了。

        ??generateToken方法,是LoginService類的靜態(tài)方法,用于生成用戶token。

        ??至于Utility的parseRoles方法,是將bitmap編碼的roles解析為角色I(xiàn)D的列表,代碼如下:

         //========================= 權(quán)限組合值解析 ======================================     
            /**
             * 
             * @methodName  : parseRoles
             * @description  : 解析角色組合值
             * @param roles  : 按位設(shè)置的角色組合值
             * @return   : 角色I(xiàn)D列表
             * @history   :
             * ------------------------------------------------------------------------------
             * date   version  modifier  remarks                   
             * ------------------------------------------------------------------------------
             * 2021/06/24 1.0.0  sheng.zheng  初版
             *
             */
            public static List<Integer> parseRoles(int roles){
             List<Integer> roleList = new ArrayList<Integer>();

             int newRoles = roles;
             int bit0 = 0;
             int roleId = 0;
             for (int i = 0; i < 32; i++) {
              //如果組合值的余位都為0,則跳出
              if (newRoles == 0) {
               break;
              }
              
              //取得最后一位
              bit0 = newRoles & 0x01;
              if (bit0 == 1) {
               //如果該位為1,左移i位
               roleId = 1 << i;
               roleList.add(roleId);
              }
              
              //右移一位
              newRoles = newRoles >> 1;
             }
             return roleList;
            } 

        ??getRoleRights方法,是角色功能權(quán)限服務(wù)類RoleFuncRightsService的方法,它提供了根據(jù)List類型的角色I(xiàn)D列表,快速獲取功能權(quán)限樹的功能。







        瀏覽 61
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            五月伊人网| 北条麻妃无码视频在线| 在线A∨视频| 日本无码片| 青青精品视频| 免费观看的av| 成人无码影院日韩,成人年…| www.99热视频| 男人天堂视频网站| 国产TS变态重口人妖| www.99av| 国产一级a毛一级a毛观看视频网站| 自拍偷拍成人视频| 国产一区在线视频| 麻豆成人91精品二区三区| 欧美国产日韩综合在线观看170| 韩日不卡视频| 国产成人精品一区二区三区四区| 欧美性受XXXX黑人XYX性爽一| 最近最火中文字幕mv歌词| 无码视频在线免费播放| 蜜桃人妻无码AV天堂二区| eeuss在线| 人妻斩り43歳| 日韩欧美国产视频| 国产一区二区三区免费| 中文字幕在线第一页| 欧美亚洲成人精品| 黄色美女毛片| 99久久婷婷国产综合精品| 日韩激情一区| 欧美精品成人免码在线| 韩国精品无码一区二区三区18| 国产乱婬AAAA片视频| 成人精品| 成人福利免费视频| 日本无码精品| 无码内射在线播放| 国产一级AV国产免费| 久久久天堂| 中文字幕高清| 免费观看黄色一级片| 九九九av| 国产麻豆精品成人毛片| 成人AA片| 亲子乱婬一级A片| 2025天天操夜夜操| 九色91PORNY国产| 亚洲A片V一区二区三区| 青草91| 俺去操| 蜜桃视频网站在线观看| 91三级片在线观看| 国产成人在线免费| 国产成人AV在线| 国产成人无码毛片| Av高清无码| 男人午夜天堂| 无码国产精品一区二区免费96| 高清色色女网站| 久久久偷拍视频| 无码AV网| 欧美第一区| a在线观看| 操逼操逼操逼操逼操逼操逼| 欧美二区三区| 成人爽a毛片一区二区免费| 日韩激情视频| 成年人黄色视频免费观看| 在线观看免费视频黄| www.91在线| 大香蕉啪啪啪| 性性性性性XXXXX| 日本理论片一道本| 91精品国自产在线观看| 老妇性BBWBBWBBWBBW| 丁香婷婷在线| 18禁黄色免费网站| 国产成人亚洲日韩| 午夜无码久久| 波多野结衣久久中文字幕| 国产无码久久| 安微妇搡BBBB搡BBBB日| 91麻豆精品视频| 欧美日韩黄| 综合久久久久| 天堂资源中文在线| 无码视频在线免费观看| 久久久伊人网| 青草青在线视频| 国产午夜福利电影| 久久福利视频导航| 亚洲三级久久| 91精品国产成人www| 成人在线网址| 翔田千里一区二区| 日韩成人AV在线播放| 麻豆成人精品国产免费| 亚洲无码性爱| a免费视频在线观看| 日批网站视频| 天天影视综合网免费观看电视剧国产 | 超碰在线99| 国产探花一区二区三区| 国产午夜精品电影| 国产另类自拍| 色婷婷五月天在线观看| 一区二区三区在线看| 久视频在线观看| 成人在线h| 天天噜天天操| 裸体美女视频欧美18| 亚洲国产三级| 日本黄色视频在线免费观看| 亚洲无码免费观看视频| 乱伦播放五月天| 中文字幕第2页| 俺来俺去www色婷婷| 男人的天堂色婷婷| 一级片在线播放| 亚洲精品乱码久久久久久久| 亚洲精品在线视频| 国产精品久免费的黄网站| 18国产免费视频在线观看| 欧美96| 天天日天天搞| 久久人搡人人玩人妻精AV香蕉| 欧美成年人视频| 日本Sm/调教/捆绑/紧缚| 久久久久亚洲AV无码专区| 欧美国产精品一二三产品在哪买 | 91人妻人人澡人人精品| 蜜桃亚洲AV无码一区二区三区| 韩日一级片| 日韩视频91| 天天干视频| 成人性爱视频免费观看| 在线观看黄色小视频| 免费看无码| 在线天堂网| 久久久激情| 五月天婷婷丁香| 亚洲视频福利| 日韩性爱视频| 韩国中文无码| a片视频免费观看| 中文字幕一区在线| 黄p网站| 国产精品久久精品| 一本久久综合亚洲鲁鲁五月天| www.黄色在线观看| 成人精品一区日本无码网站suv/| 亚洲色图图片| 国产精品theporn| 四虎2025在线51| 浮力影院欧美| 亚洲成a人无码| 一级女婬片A片AAAA片| 黑人亚洲娇小videos∞| 日本无码一区二区三三| 内射视频在线免费观看| 一本色道精品久久一区二区三区 | 亚洲天堂成人在线| 国产麻豆视频| 黄色免费观看网站| 人人澡人人爽欧一区| 7799精品| 亚洲福利免费观看| 日韩群交| av资源免费观看| 亚洲福利在线免费观看| 日本不卡在线| 操逼的网站| 日韩高清无码观看| 午夜福利干B在线免费小视频| 9l农村站街老熟女| 日韩wuma| 国产精品免费在线| 神马影院午夜福利| 欧美性一区| eeuss一区| h片免费网站| 国产欧美综合视频| 国产在线中文| 日韩无码视屏| 亚洲成人中文字幕在线| 欧美一级免费| 欧一美一婬一伦一区二区三区黑人-亚 | 亚洲AV秘一区二区色盗战流出| 日韩一级大片| 亚洲乱码一区二区三区| 人人操人人干人人| 天天日天天舔| 亚洲精品中文字幕乱码三区91| 先锋影音亚洲无码av| 九色PORNY自拍视频| 伊人免费在线| 亚洲插逼视频| 香蕉成人A片视频| 午夜福利视频91| 亚洲男女内射| 蜜挑视频一区二区三区| 啪啪视频免费观看| 亚洲尤物| 白嫩无码| 日韩精品三区| 日韩精品视频一区二区| 欧美色色视频| 俺也色俺也干| 欲色av| 2025天天干| 无码爆操| 中文字幕精品久久久久人妻红杏Ⅰ| 亚洲无线观看| 免费在线看a| 91精品久久久久久久久久| 九九热日本| 久久欧洲成人精品无码区| 在线看黄片| 超小超嫩国产合集六部| av一区二区三区| 日本成人黄色视频| 60分钟上大床又黄又爽| av福利在线| 亚洲中文字幕无码爆乳av| 高清无码免费在线观看| 人人妻人人爽人人精品| 在线黄色AV| 99黄网| 亚洲视频在线免费| 搡BBBB搡BBB搡五十粉嫩| 欧美亚韩一区二区三区| 日韩性爱视频在线观看| 强奸乱伦五月天| 91一起草高清资源| 六月婷婷七月丁香| 高清无码色播| 欧美日日日| 亚洲免费在线视频观看| 亚洲高清无码在线视频| 日本中文视频| 九九热这里有精品| 内射网站在线观看| 黄片高清免费观看| 国产精品视频免费观看| 伊人网在线视频观看| 欧美天堂在线观看| 粉嫩小泬BBBB免费看-百度 | 日本丰满老熟妇乱子伦| 大香蕉中文网| 91AV电影网| 91麻豆国产福利在线观看| 51成人免费| 久久久久久国际四虎免费精品视频| 欧美成人精品欧美一级乱黄| 男人的天堂2019| 青青草超碰在线| 日本欧美黄色| 自拍成人视频| 日日艹夜夜艹| 超碰在线人妻| 91无码国产成人精品| 日本天堂网站| 一本色道久久综合熟妇| 亚洲欧美天堂| 日本精品视频一区二区| 中文字幕乱码亚洲无线码在线日噜噜| AV无码网站| 一级特黄AA片| 探花AV| 成人高清无码视频| 日本高清不卡视频| 青青草精品在线视频| 粉嫩小泬粉嫩小泬在线| 免费版成人久久幺| 91无码人妻精品1国产四虎| 男女成人视频| 在线国产激情| av网站免费在线观看| 成人丁香五月天| 中文字幕乱码在线| 一本之道DVD不卡视频| 亚洲欧美日韩免费| 五月黄片| 久久久久成人视频| 中文在线第一页| 欧美一级A片免费看视频小说| 黄页免费无码| 青娱乐亚洲视频在线| 天天毛片| 亚洲天堂视频网站| 色片免费| 久久97人妻AⅤ无码一区| jizz丝袜| 日韩在线视频二区| 亚洲在线观看免费| 国产中文自拍| 成人网站欧美| 亚洲国产成人91精品| 人人操人人摸人人看| 神马午夜秋霞不卡| 黄色一级免费看| 国产黄色免费乱伦片| 成人网站免费在线观看| 欧美日韩国产一区二区三区 | 亚洲欧美第一页| 中文字幕欧美视频| 人妻无码久久| 亚洲中文AV| 91搞搞| 啪啪啪网站| 国产乱码在线| 亚洲成人AV一区二区| 999成人网| 在线视频A| 精品视频在线观看免费| 久久久精品免费视频| 草逼视频网| 国产图区| 国产无码高清| 日韩中字无码黄片| 亚洲A片免费看| 中文字幕在线无码视频| 黄色无码视频在线观看| 蜜臀AV在线播放| 国产精品二区高清在线苍井空| 无码AV一区二区| 99re视频在线观看| 日韩人妻精品无码久久边| 黄色片视频日本| 人人摸天天| 亚洲天堂大香蕉| 97黄片| 日韩五码| 五夜福利成人视频| 日本色影院| 特级西西西88大胆无码| 青青草原无码| 韩国高清无码视频| 日韩无码一区二区三区| 精品国产精品国产精品国产网站 | 夜夜骑夜夜操| 人妻性爱| 精品无码AV一区二区三区| 五月丁香婷婷色色| 欧美成人网站在线| 国产18| 91丨九色丨熟女老版| 特爽特黄特级特色视频| 丰满人妻一区二区三区视频54 | 18精品爽视频| 抽插免费视频| 亚洲精品国产精品国自产曰本| 韩国毛片| 日韩无码123区| 国产丰满| 青青草原在线| 人人干人人摸| 成人一级黄色电影| 91无码秘蜜桃一区二区三区-百度 精品人妻一区二区三区在线视频不卡 | 国产在线小视频| 久久久精品999| 丁香五月婷婷中文字幕| 色婷婷在线视频观看| 黄片www| 国产麻豆免费| 国产伦子伦一级A片在线| 人成在线免费视频| 欧美精品一区二区少妇免费A片| 国产黄片在线视频| 99久久婷婷国产精品2020| 图片区视频区小说区| 亚洲中文字幕播放| 中文字幕在线乱| 欧美亚洲日本| 九九九成人视频| 暖暖日本在线| 五月婷婷丁香五月| 免费在线观看AV片| 在线免费观看黄| 人妻少妇精品视频一区二区三区 | 成人图片小说| 综合网亚洲| 成人性爱视频免费观看| 久久午夜福利| 青青大香蕉| 91av在线免费观看| 艹逼视频免费观看| 久久91欧美特黄A片| 欧美精品一区二区三区蜜臀 | 久久久免费观看视频| 内射学生妹| 好吊妞在线观看| AV麻豆| 操极品少妇逼| 国产精品天天干| 国产高清黑人| 亚洲男同tv| 亚洲成人在线无码| 久热综合| 国产91探花精品一区二区| 亚洲三级久久| 亚洲美女喷水视频| 99精品视频免费在线观看| 午夜电影福利| 丁香六月婷婷| 一区二区三区四区在线看| 91成人视频18| 成人影片在线观看18| 亚洲成人在线网| 三级黄色片| 影音先锋AV天堂| 最近中文字幕mv第三季歌词| 大地8免费高清视频观看大全| 国产做受91电影| 亚洲成人自拍| 日韩成人免费观看| 欧美日韩a| 欧美老女人性| 日本一区二区在线| 国产又爽又黄视频| 欧美va| 色片网| 西西444WWW无码大胆| 日本色影院| 无码狠狠躁久久久久久久91| 成人午夜视频精品一区| 精品一区二区久久久久久久网站 | 青青草国产亚洲精品久久| 摸BBB槡BBBB搡BBB,,,,,| 色婷婷六月天| 免费黄片无码| 亚洲最大视频| 久久露脸国语精品国产91| a片在线免费看| 免费AV资源在线观看| 日逼电影网| 9久精品| 亚洲国产中文字幕| 久久精品婷婷| 激情91| 国产乱伦AV网站| 一级黄片免费看| 欧美亚洲成人电影| 吴梦梦《女教师时间暂停》| 婷婷精品国产一区二区三区日韩| 日韩黄色电影在线免费观看 | 嫩草亚洲小泬久久夂| 国产一级婬乱A片| 国产强伦轩免费视频在线| 国产久久久久久久久| H片在线观看| 一级二级三级无码| 国产一级二级三级视频| 2018天天干天天操| 伊人视频网| 成人做爰A片AAA毛真人| 免费无码在线视频| 久久丁香五月婷婷五月天激情视频| 18禁av在线| 人人操人人操人人操人人操人人操| av无码中文| 无码三级午夜久久人妻| 婷婷五月激情网| 色福利视频| 国产午夜91人妻| 狼友无码| 亚洲三级在线免费观看| 欧美日韩在线观看视频| 久久久精品免费视频| 在线免费亚洲视频| 日本AV在线播放| 婷婷综合一区| 小泽玛利亚一区二区免费| 91精品视频网站| 成人日韩AV| 色情一级A片成人片| 日日夜夜拍| 国产精品一区二区三区不卡| 男女日逼视频| 牛牛精品视频| 怡红院成人在线| 亚洲人成电影| 色鬼综合| 欧美日韩高清丝袜| 不迷路福利视频| 一级a一级a爱片免费免免高潮| 狠狠色噜噜狠狠狠888| 91熟女视频| 逼特逼视频在线观看| 伊人成人片| 国产成人女人在线观看| 狼友视频在线播放| 18禁污网站| 日本AA视频| 嫩BBB槡BBBB槡BBB| 理论片91| 国产精品无码无套在线照片 | 淫揉BBB揉揉揉BBBBB| 夜夜操天天日| 亚洲二区无码| 久久一级片| 国产精品人妻AⅤ在线看| 九色蝌蚪视频| 国产成人A∨| 无码不卡视频| 日本十八禁网站| 九九韩剧网最新电视剧免费观看| 欧美性爱动态| 欧美一级A片高清免费播放| 插菊花综合网3| 午夜福利视频网站| 欧美va视频| aa无码视频| 人人操人人摸人人| 亚洲成色A片77777在线小说| 懂色AV无码中字幕一区| 欧美爆操视频| 国产午夜视频在线观看| 性饥渴熟妇乱子伦| 精品国内自产拍在线观看视频 | 天天干天天操天天射| 91青青草视频| 色色五月天视频| 国产精品777777| 搡BBBB搡BBB搡五十粉嫩| 国产精品久久久久久久久久王安宇 | 亚洲搞清视频日本| 搡BBBB搡BBB搡五十| 天堂а√在线中文在线新版| 中文字幕在线乱| 怡春院首页| 91麻豆精品国产91久久久久久久久 | 亚洲无码久久久| 人人操夜夜操| 亚洲免费观看高清完整版在线| 成人免费乱码大片a毛片蜜芽| 安微妇搡BBBB搡BBBB日| 日本成人中文字幕在线观看 | 91免费在线视频观看| 亚洲激情小说| 东北女人操逼| 国产棈品久久久久久久久久九秃| 三级片男人的天堂| 黄色视频免费观看国产| 欧美精品一区二区三区蜜臀 | 不卡av在线| 亚洲精品999| 日韩无码高清一区| 成人性爱免费网站| 韩国一区二区在线观看| 加勒比久久久| 综合色国产精品欧美在线观看| 天天撸视频| 91色噜噜狠狠色婷婷| AV网站免费看| 亚洲免费观看高清完整版在线| 日韩无码少妇| 污视频网站免费在线观看| 99在线观看视频在线高清| 久久6热| aaa在线免费视频| 天天天天干| 色婷婷激情AV| 一级生活片| 2014天堂网| 国产欧美性爱| www欧美日韩| 99人人操| 国产真实露脸乱子伦对白高清视频| 国产高潮白浆喷| 乱伦激情视频| 国产色色视频| 91aV视频| 亚洲一| 久久婷综合| 西西WWW888大胆无码| 蜜桃视频无码区在线观看| 欧美性综合网| 亚洲爱爱网站| 色播视频在线观看| 少妇高潮一区二区三区99| 黄色免费视频网站| AA无码| A在线免费观看| 婷婷久月| 国产一级特黄A片| 日日碰狠狠| 日逼片A| 亚洲美女网站在线观看| 亚洲最新AV在线| 欧美aⅤ| 欧美性爱免费在线视频| 在线观看一区二区视频| 美女毛片网站| 亚洲欧美另类图片| 影音先锋女人aV鲁色资源网站| 国产传媒av| 夏目あきら被续侵犯7天| av大片在线观看| 最新av网| 人人操人人操人人操人人操| 久久久精品网站| 青娱乐偷拍视频| 无码人妻一区二区三区免费n鬼沢| 日韩人妻精品无码制服| 成人无码一区| 欧美footjob高跟脚交| 亚洲视频网址| 精品无码不卡| 高清无码在线免费观看视频| 一级黄色电影免费观看| 成人在线国产| 蜜桃视频网站| 亚洲AV自拍| 免费观看黄色一级片| 动漫一区二区三区| 东北老女人操逼| 久操视频免费| 欧美老妇另类| 日本乱伦电影中文字幕| 伊人影院在线免费观看| 老熟女搡BBBB搡BBBB视频| 99re在线精品| 99色在线视频| 精品视频91| 免费毛片+一区二区三区| 国产又粗又大| 韩国中文字幕HD久久| 欧美A片免费观看| 久久久极品| 欧美亚洲成人电影| 日韩中文字幕高清| 婷婷伊人| 啪啪啪免费网站| JlZZJLZZJlZZ亚洲女人17| 91在线欧美| 亚洲成人不卡| 久久精品www人人爽人人| 玖玖国产| 一本一道久久综合狠狠躁牛牛影视| 日本少妇高清视频| 91香蕉网站| 人人天天爽| 任我操在线视频| 色色亚洲| 成人毛片在线播放免费| 亚洲四房播| 日韩欧美在线中文字幕| 青娱乐自拍偷拍| 特级西西西西4444级酉西88wwww特 | 久久精品v| 欧美AⅤ视频| 成人综合网站| 天天日天天射天天干| 夜夜嗨老熟女AV一区二区三区| 五月天激情网址| 日韩欧美在线一区| 欧美成人精品在线观看| 人人爽人人爽人人爽| 黄色特级aaa片| 青青青国产在线| 日韩无码成人片| 亚洲无码A片在线观看| 久久内射| 黄片网站入口| 91亚洲国产成人久久精品麻豆| 国产又爽又黄免费视频免费| 日韩一级在线观看| 激情五月天av| 在线天堂19| 大香蕉在线网| 亚洲性爱片| 曰韩精品| 中文字幕一区在线| 四川w搡BBB搡wBBB搡| 伊人日日| 蜜桃久久av一区| 亚洲欧美国产日韩字幕| 国产精品无码一区二区三区免费| 老鸭窝成人| 五月天久久精品| 日韩极品视频| 夜夜嗨av无码一区二区三区 | 伊人春色AV| 日本一级特级毛片视频| 特级婬片AAAAAAA级| 亚洲AV免费在线观看| 日本大香蕉在线视频| 九九精品热播| 亚洲午夜视频在线观看| 久久久久久成人无码| 一本无码高清| 国产对白在线| 97夜色| 日韩大香蕉视频| 日日撸| www.97cao| 亚洲人成无码| 欧美成人视频电影无码高清| 一级特黄A片| 人妻操逼| 高清无码免费在线视频| 亚洲欧美日韩另类| 熟妇一区| 少妇人妻一级A毛片| 操少妇逼| 欧美九九| 日本黄色片视频| 色综合色综合| 伊人蕉| 一级a免一级a做免费线看内祥| 免费在线观看黄色视频| 无码人妻精品一区二区蜜桃网站| 北条麻妃精品| 狠狠撸天天日| 黑人大肉棒| H无码| 俺来也俺去| 少妇高潮喷水| 麻豆AV电影| 在线观看视频免费无码免费视频| 操逼操| av在线三级| 日韩黄色av| 日韩无码波多野结衣| 亚洲美女操| 91探花足浴店按摩店| 精品人妻一区二区免费蜜桃| 韩国精品无码一区二区三区18| 中国老少配BBwBBwBBW| 丰满岳乱妇一区二区三区| 91麻豆国产福利精品| 四虎成人网址| 久久黄色免费看| 五月天婷婷久久| 国产麻豆视频| 欧美日韩一区在线观看| 免费无码高清视频| 婷婷成人综合网| 亚洲精品无码电影| 91久久性奴调教| 日韩无码免费看| 操逼AⅤ| 日韩无码AV一区二区| 最新AV在线播放| 亚洲操B视频| 女孩自慰在线观看| 亚洲天堂精品视频| 亚洲永久在线| 日本黄色录像| 操极品美女| 精品一区无码| 国产成人精品无码片区在线观91| 97日日| 在线观看2区| 成人精品无码免费视频| 黄色视频免费观看| 国产精品77777| 91视频青青草| 国产女人18毛片水真多1| 裸体黄色一极大片| 无码一卡| 欧美国产中文| 91在线免费视频| 人人色视频| 99久久丫e6| 欧洲黑人成人A版免费视频| 91探花秘入囗| 口爆在线| 亚洲一区二区精品| 高圆圆一区二区三区| 国产综合自拍| 色婷婷日韩精品一区二区三区| 亚洲色图15| 91乱伦| 亚洲网站视频| 日韩精品小电影| 国产免费一区二区三区四区| 人人射人人操| 老熟妇搡BBBB搡BBBB| 韩国一区二区三区在线观看| 人人妻人人澡人人爽人人DVD| 日本韩国高清无码| 日韩欧美不卡色不卡| 国产精品三级在线观看| 91无码国产成人精品| 浪潮在线观看完整版| 国产精品久久久久久亚洲影视| 99爱在线| 亚洲性精| 亚洲AV无码电影| 日韩一区二区三区精品| 99久久综合九九| 国产十八岁在线观看| 蜜臀久久99精品久久久久酒店更新时间 | 91精品人妻人人爽| 免费亲子乱婬一级A片| 国产在线视频网站| www99| 久99热| 青青青草视频在线观看| 黄色视频在线观看地址| 欧美精品A级片| 五月丁香大香蕉| 亚洲欧美不卡| 亚洲在线观看网站| 91久久精品无码一区二区三区| 欧美一级特黄A片免费| 天天撸在线| WWW黄片| 婷婷亚洲综合| 黄色伊人网| 午夜AV福利| 成人自拍网站| 激情一一区二区三区| 综合成人| 99性爱| 国产七区| 五月亚洲六月婷婷| 欧美精产国品一二三产品在哪买| 成人精品一区二区三区中文字幕 | 成人免费毛片视频| www.yw尤物| 一级黄色操逼视频| 久久超碰99| 性爱一级视频| 精品人妻少妇| 日韩精品三级片| 麻豆精品在线观看| 亚洲无码三级视频| 露脸偷拍AV2025| www.五月丁香| 国产人妻精品一区二区三区不卡| 久久草在线| 无码视频在线| 国产最新福利| 国精产品一品二品国精| 中文字幕+乱码+中文乱码91| 无码激情18激情视频| 日韩无码AV一区二区| 欧美色图第一页| 免费成人视频在线观看| 成人精品久久久| AV天堂小说| 欧美污视频在线观看| 91视频在线免费看| 国产特級黃色大片| 91x色| 久草福利在线视频| 一级aa免费视频| 青娱乐最新官网| 久久久久久97电影院电影院无码 | 亚洲国产av一区| 午夜福利aaa| 久久精品福利| 三级理论网站| 蜜芽成人在线| 超碰自拍99| 精品区| 欧美成人色| 亚洲欧美国产日韩字幕| 好屌肏| 黄网站免费在线观看| 国产又粗又猛又黄又爽无遮挡| 欧美成人黄色A片| 亚洲色图一区二区| 中文无码熟妇人妻| 亚欧无码| 亚洲无吗在线观看| 亚洲AV无码乱码精品| 91久久精品一区二区三区| 大伊人久久| 91国产精品在线视频| aⅴ无码| 五月天超碰| 国产午夜成人免费看片无遮挡| 亚洲成a| 五月久久| 国产综合一区二区| 撸撸操在线视频观看只有精品| 亚洲精品久久久蜜桃| 国产青娱乐在线视频| 91精品丝袜久久久久久久久粉嫩| 五月婷婷中文版| 欧美手机在线视频| 广州媚黑妇系列视频在线| 91麻豆福利在线| 色五月婷婷中文字幕| 国产高清中文字幕| 亚洲综合五月天婷婷丁香| 成人电影无码| 怡春院综合| 人人爽人人澡| 亚洲成人影片在线观看| 色伊人网| 人人摸人人操人人| 色婷婷色婷婷| 女人18特级毛片。| 婷婷成人综合|