Spring Boot 동적 권한 변경 문제 에 대한 실현 방안

1.머리말
  웹 프로젝트 에서 권한 관리 즉 권한 액세스 제 어 는 사이트 액세스 안전 에 보장 을 제공 하고 많은 프로젝트 가 Session 을 캐 시 로 사용 하여 AOP 기술 과 결합 하여 token 인증 과 권한 제 어 를 한다.권한 제어 프로 세 스 는 다음 그림 과 같 습 니 다.

  현재 관리자 가 사용자 의 역할 을 수정 하거나 캐릭터 의 권한 을 수정 하면 사용자 권한 에 변화 가 생 길 수 있 습 니 다.이때 동적 권한 변경 을 어떻게 실현 하여 전단 이 사용자 의 권한 트 리 를 업데이트 할 수 있 고 백 엔 드 액세스 권한 AOP 모듈 은 이러한 변경 사항 을 알 수 있 습 니까?
2.문제 및 해결 방안
현재 문 제 는 관리자 가 사용자 세 션 에 접근 할 수 없 기 때문에 변경 사항 을 사용자 에 게 알 릴 수 없습니다.사용자 가 로그 인 을 했 거나 로그 인 동작 이 아 닌 브 라 우 저 페이지 를 직접 닫 으 면 Session 이 만 료 되 기 전,사용자 가 인터페이스 에 접근 할 때 액세스 권한 AOP 모듈 은 여전히 이전 캐 시 된 Session 정보 에 따라 처리 되 므 로 동적 권한 변경 이 불가능 합 니 다.
*8195:8195:Security+WebSocket 을 사용 하 는 것 은 하나의 방안 이지 만 온라인 사용 자 를 처리 할 수 없습니다.
  솔 루 션 의 핵심 사상 은 ServletContext 대상 의 공유 특성 을 이용 하여 사용자 권한 이 변 경 된 정보 전달 을 실현 하 는 것 이다.그리고 AOP 클래스 에서 사용자 가 변경 알림 기록 을 처리 해 야 하 는 지,권한 이 변경 되면 response 메시지 체 를 수정 하고 전단 에 추가 알림 정 보 를 추가 합 니 다.전단 에 추가 알림 정 보 를 받 으 면 기능 권한 트 리 를 업데이트 하고 관련 처 리 를 할 수 있 습 니 다.
  이렇게 이용 한 변경 알림 서 비 스 는 백 엔 드 의 사용자 url 방문 인터페이스 가 최초 로 변경 을 알 수 있 을 뿐만 아니 라 전단 에 도 알려 동적 권한 변경 을 실현 할 수 있 습 니 다.
3.방안 실현
3.1 개발 변경 알림 유형
  서비스 인터페이스 류 ChangeNotify Service,코드 는 다음 과 같 습 니 다.

package com.abc.questInvest.service;

/**
 * @className		: ChangeNotifyService
 * @description		:       
 * @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:	:           ,        ;
	 * 		bit1:	:         ,        ;
	 * 		bit2:	:     ,        ;
	 * 		bit3:	:       ,          ;
	 * @history		:
	 * ------------------------------------------------------------------------------
	 * date			version		modifier		remarks                   
	 * ------------------------------------------------------------------------------
	 * 2021/06/28	1.0.0		sheng.zheng		  
	 *
	 */
	public Integer getChangeNotifyInfo(Integer userId);
	
	/**
	 * 
	 * @methodName		: setChangeNotifyInfo
	 * @description		:         
	 * @param userId	:   ID
	 * @param changeNotifyInfo	:      
	 * 		bit0:	:           ,        ;
	 * 		bit1:	:         ,        ;
	 * 		bit2:	:     ,        ;
	 * 		bit3:	:       ,          ;
	 * @history		:
	 * ------------------------------------------------------------------------------
	 * date			version		modifier		remarks                   
	 * ------------------------------------------------------------------------------
	 * 2021/06/28	1.0.0		sheng.zheng		  
	 *
	 */
	public void setChangeNotifyInfo(Integer userId,Integer changeNotifyInfo); 	
}
  서비스 실현 류 Change Notify ServiceImpl,코드 는 다음 과 같 습 니 다.

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   
 * @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:	:           ,        ;
	 * 		bit1:	:         ,        ;
	 * 		bit2:	:     ,        ;
	 * 		bit3:	:       ,          ;
	 * @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);
			//    ,    
			synchronized(changeNotifyMap) {
				changeNotifyMap.remove(userId);
			}
		}
		return changeNotifyInfo;
	}
	
	/**
	 * 
	 * @methodName		: setChangeNotifyInfo
	 * @description		:         ,             
	 * @param userId	:   ID
	 * @param changeNotifyInfo	:      
	 * 		bit0:	:           ,        ;
	 * 		bit1:	:         ,        ;
	 * 		bit2:	:     ,        ;
	 * 		bit3:	:       ,          ;
	 * @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);
			//    。bitmap  ,   
			Integer newChangeNotifyInfo = oldChangeNotifyInfo | changeNotifyInfo;
			//    ,    
			synchronized(changeNotifyMap) {
				changeNotifyMap.put(userId,newChangeNotifyInfo);
			}
		}else {
			//    ,    
			changeNotifyMap.put(userId,changeNotifyInfo);
		}
	}
}
*8195:여기 서 알림 유형 을 변경 하고 사용 하 는 demo 프로젝트 와 관련 되 며 현재 4 가지 변경 알림 유형 을 정의 합 니 다.실제로 권한 관련 변경 외 에 도 세 션 캐 시 필드 와 관련 된 변경 사항 이 있 으 므 로 알림 이 필요 합 니 다.그렇지 않 으 면 사용 자 는 오래된 데 이 터 를 사용 하고 있 습 니 다.
3.2 알림 유형 변경 대상 을 전체 설정 서비스 대상 에 포함 시 켜 관리
전체 설정 서비스 클래스 GlobalConfigService 는 전체 설정 서비스 대상 을 관리 하고 서비스 인터페이스 류 코드 는 다음 과 같 습 니 다.

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		:      
	 * @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();
	
}
  서비스 실현 류 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   
 * @summary		:
 * @history		:
 * ------------------------------------------------------------------------------
 * date			version		modifier		remarks                   
 * ------------------------------------------------------------------------------
 * 2021/06/02	1.0.0		sheng.zheng		  
 *
 */
@Service
public class GlobalConfigServiceImpl implements GlobalConfigService{
	
	//ID         
	@Autowired
	private TableCodeConfigService tableCodeConfigService;
	
	//         
	@Autowired
	private SysParameterService sysParameterService;
	
	//        
	@Autowired
	private FunctionTreeService functionTreeService;
	
	//         
	@Autowired	
	private RoleFuncRightsService roleFuncRightsService;
	
	//      
	@Autowired	
	private ChangeNotifyService changeNotifyService;
	
	
	/**
	 * 
	 * @methodName		: loadData
	 * @description		:      
	 * @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       ,    
		//      ,    ,    ,    Session    
		
		//  function_tree   
		bRet = functionTreeService.loadData();
		if (!bRet) {
			return bRet;
		}
		
		//  role_func_rights   
		//        
		roleFuncRightsService.setFunctionTree(functionTreeService.getFunctionTree());
		//      
		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 류 는 많은 설정 서비스 류 를 관 리 했 는데 여 기 는 주로 Change Notify Service 류 대상 에 주목 합 니 다.
3.3,ServletContext 를 사용 하여 전역 설정 서비스 클래스 대상 관리
전체 설정 서비스 류 는 응용 이 시 작 될 때 Spring 용기 에 불 러 옵 니 다.이렇게 하면 공 유 를 실현 하고 데이터 베이스 에 대한 접근 압력 을 줄 일 수 있 습 니 다.
  응용 프로그램 Listener 류 를 실현 합 니 다.코드 는 다음 과 같 습 니 다.

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	:      
 *
 */
@Component
public class ApplicationStartup implements ApplicationListener<ContextRefreshedEvent>{
    //        ,        
    private GlobalConfigService globalConfigService = null;
    
    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        try {
    	    if(contextRefreshedEvent.getApplicationContext().getParent() == null){ 
    	    	//root application context   parent.
				
    	    	System.out.println("========      ==================");
    	    	//   ApplicationContext     WebApplicationContext
    	        WebApplicationContext webApplicationContext =
    	                (WebApplicationContext)contextRefreshedEvent.getApplicationContext();
    	        //   webApplicationContext      servletContext
    	        ServletContext servletContext = webApplicationContext.getServletContext();
    	        
    	        //          
    	        globalConfigService = (GlobalConfigService)webApplicationContext.getBean(GlobalConfigService.class);
    	        //    
    	        boolean bRet = globalConfigService.loadData();
    	        if (false == bRet) {
    	        	System.out.println("        ");
    	        	return;
    	        }        
    	        //======================================================================
    	        // servletContext   
    	        servletContext.setAttribute("GLOBAL_CONFIG_SERVICE", globalConfigService);  
    	        
    	    }
    	} catch (Exception e) {
    	    e.printStackTrace();
    	}        
    }
}
  시동 클래스 에 이 응용 탐지 기 ApplicationStartup 을 추가 합 니 다.

public static void main(String[] args) {
    	SpringApplication springApplication = new SpringApplication(QuestInvestApplication.class);
        springApplication.addListeners(new ApplicationStartup());
        springApplication.run(args);  
	}
현재 GlobalConfigService 형식의 전역 변수 globalConfigService 가 있 습 니 다.
3.4 변경 통지 발송
  여기 서 두 가지 예 를 들 어 변경 통 지 를 보 낸 예 를 설명 한다.이 두 가지 예 는 모두 사용자 관리 모듈,UserManServiceImpl 류 에 있다.
  1)관리자 가 사용자 정 보 를 수정 하면 권한 관련 항목 에 변동 이 생 길 수 있 습 니 다.2)사용 자 를 사용 하지 않 고 변경 통 지 를 보 냈 습 니 다.
『8195』알림 을 보 내 는 관련 코드 는 다음 과 같 습 니 다.

/**
	 * 
	 * @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) {
		//      
		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) {
			//         
			//      
			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) {
		//      
		checkValidForParams("disableUser",params);
		
		UserInfo userInfo = new UserInfo();
		
		//       
		String operatorName = (String) request.getSession().getAttribute("username");
		
		//  userInfo  
		Integer userId = (Integer)params.get("userId");
		userInfo.setUserId(userId);
		userInfo.setOperatorName(operatorName);
		//      
		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);
		}		
		
		//    ,      
		//      
		ServletContext servletContext = request.getServletContext();
		GlobalConfigService globalConfigService = (GlobalConfigService)servletContext.getAttribute("GLOBAL_CONFIG_SERVICE");
		//    :bit2
		globalConfigService.getChangeNotifyService().setChangeNotifyInfo(userId, 0x04);				
	}
  본 demo 프로젝트 의 캐릭터 가 상대 적 으로 적 고 사용자 캐릭터 관계 표를 사용 하지 않 고 bitmap 인 코딩 을 사 용 했 습 니 다.캐릭터 ID 의 수 치 는 2^n 이 고 사용자 캐릭터 조합 roles 필드 는 Integer 값 입 니 다.예 를 들 어 roles=7 은 캐릭터 ID 조합=[1,2,4]을 나타 낸다.
  또한 캐릭터 의 기능 권한 집합 을 수정 하면 영향 을 받 은 사용자 ID 목록 을 조회 하고 순서대로 알림 을 보 내 유사 하 게 처리 할 수 있 습 니 다.
3.5.응답 메시지 수정
  Response 응답 메시지 체 는 BaseResponse 이 고 코드 는 다음 과 같 습 니 다.

package com.abc.questInvest.vo.common;

import lombok.Data;

/**
 * @className		: BaseResponse
 * @description		:          
 * @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> {
    //   
    private int code;

    //    
    private String message;
        
    //      
    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;

    //        
    private String notification;
    
    //   token
    private String token;
    
    //        
    private String rights;

}
*8195°추가 정보 류 Additional 에서 각 속성 필드 에 대한 설명:
  • notifycode 는 알림 코드 로 알림 메시지 의 유형 에 대응 할 수 있 습 니 다.현재 한 가지 만 확장 할 수 있 습 니 다
  • 알림 코드 에 대응 하 는 메시지 입 니 다『8195』알림 코드 는 ExceptionCodes 매 거 진 파일 에서 정의 합 니 다.
    
      //      
        USER_RIGHTS_CHANGED(51, "message.USER_RIGHTS_CHANGED", "        "),
    	;  //end enum
    
        ExceptionCodes(int code, String messageId, String message) {
            this.code = code;
            this.messageId = messageId;
            this.message = message;
        }
  • token 은 전단 에 token 을 업데이트 하 라 고 요구 하 는 데 사 용 됩 니 다.token 을 업데이트 하 는 목적 은 전단 에서 권한 변경 통 지 를 받 았 는 지 확인 하 는 것 입 니 다.다음 url 요청 은 새로운 token 을 사용 할 것 입 니 다.전단 이 받 지 않 거나 처리 되 지 않 았 을 때 오래된 token 으로 접근 하면 로그 인 페이지 로 넘 어 갑 니 다
  • rights,기능 트 리 의 문자열 출력 은 트 리 구조의 JSON 문자열 입 니 다
  • 3.6 AOP 감 권 처리
    『8195』AuthorizationAspect 는 감 권 인증 의 절단면 류 이 고 코드 는 다음 과 같다.
    
    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		:          
     * @summary		:   AOP,  token                
     * @history		:
     * ------------------------------------------------------------------------------
     * date			version		modifier		remarks                   
     * ------------------------------------------------------------------------------
     * 2021/06/06	1.0.0		sheng.zheng		  
     * 2021/06/28	1.0.1		sheng.zheng		         ,   afterReturning  
     *
     */
    @Aspect
    @Component
    @Order(2)
    public class AuthorizationAspect {
    	@Autowired
        private UserManDao userManDao;
    	
    	//    
        @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  
    		
    		// header   token 
    		String token = request.getHeader("Authorization");
    		if (null == token || token.equals("")){ 
    			//return;
    			throw new BaseException(ExceptionCodes.TOKEN_IS_NULL); 
    		} 
        	
    		// session   token     
    		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");
    		//      
    		long time = System.currentTimeMillis();
    		if (time > expireTime) {
    			//  token  
    			throw new BaseException(ExceptionCodes.TOKEN_EXPIRED);
    		}else {
    			//token   ,      
    			long newExpiredTime = time + Constants.TOKEN_EXPIRE_TIME * 1000;
    			request.getSession().setAttribute("expireTime", newExpiredTime);
    		}
    		
    		// ============================================================================
    		//       
    		//    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);
    		//           
    		boolean rightsChangedFlag = false;		
    		if (changeNotifyInfo > 0) {
    			//     
    			if ((changeNotifyInfo & 0x09) > 0) {
    				//bit0:          ,        
    				//bit3:      ,          
    				//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) {
      		    		        //        
      		    		        rightsChangedFlag = true;
      		    	        }
    			}else if((changeNotifyInfo & 0x02) > 0) {
    				//bit1:        ,        
    	    		        //        
    	    		      rightsChangedFlag = true;
    			}else if((changeNotifyInfo & 0x04) > 0) {
    				//bit2:    ,        
    				//    token,          
    				request.getSession().setAttribute("token", "");
    				//      ,     :Forbidden  
    				throw new BaseException(ExceptionCodes.ACCESS_FORBIDDEN);
    			}
    			if (rightsChangedFlag == true) {
    				// Session,        afterReturning   
    				request.getSession().setAttribute("rightsChanged", 1);
    			}
    		}
    		//===================        ==============================================
    				
    		// session        
    		Integer roles = (Integer)request.getSession().getAttribute("roles");
    		//      url 
    		String servletPath = request.getServletPath();
    				
    		//      url     
    		Integer rights = globalConfigService.getRoleFuncRightsService().getRoleUrlRights(Utility.parseRoles(roles), servletPath);
    		if (rights == 0) {
    			//          ,    ,     :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) {
        		//         ,                
        		//      
        		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);
        		//          
        		Integer roles = (Integer)request.getSession().getAttribute("roles");
        		ServletContext servletContext = request.getServletContext();
        		GlobalConfigService globalConfigService = (GlobalConfigService)servletContext.getAttribute("GLOBAL_CONFIG_SERVICE");
            	//            
        		List<Integer> roleList = Utility.parseRoles(roles);
            	TreeNode<FunctionInfo> rolesFunctionTree = 
            			globalConfigService.getRoleFuncRightsService().
            			getRoleRights(roleList);
            	additional.setRights(rolesFunctionTree.toString());
        		//  response  
            	result.setAdditional(additional);
        		//  Session rightsChanged 
        		request.getSession().removeAttribute("rightsChanged");
        	}
        }
    }
      AuthorizationAspect 류 는 절 점 verify()를 정 의 했 고@Before 강 화 는 감 권 검증 에 사용 되 며 변경 알림 정보 에 대한 처 리 를 추 가 했 습 니 다.또한 Session 을 이용 하여 rights Changed 속성 필드 로 전단 에 알려 야 할 로 고 를 기록 하고@AfterReturning 후 강화 에서 이 속성 필드 의 값 에 따라 한 단계 처리 합 니 다.
      @Before 강 화 된 doVerify 방법 에서 캐릭터 조합 이 바 뀌 었 지만 이 url 에 접근 할 수 있 는 권한 이 있 을 경우 후속 처 리 를 계속 합 니 다.이렇게 하면 업 무 를 중단 하지 않 습 니 다.이 url 에 접근 할 수 있 는 권한 이 없 으 면 접근 제한 이상 정 보 를 되 돌려 주 고 접근 제한 페이지(403 Forbidden 페이지 와 유사)를 전단 에 표시 합 니 다.
      는 백업 증강@AfterReturning 에서 반환 값 형식 을 제한 합 니 다.이 요청 에 응 하 는 유형 이 BaseResponse 형식 이 라면 reponse 메시지 체 를 수정 하고 알림 정 보 를 추가 합 니 다.그렇지 않 으 면 처리 하지 않 고 다음 url 요청 을 기다 릴 것 입 니 다.형식 이 BaseResponse 형식 일 때 까지 기다 릴 것 입 니 다.사용자 정의 response 의 header 방식 을 사용 할 수도 있 습 니 다.기다 릴 필요 가 없습니다.
      generateToken 방법 은 LoginService 류 의 정적 방법 으로 사용자 token 을 생 성 하 는 데 사 용 됩 니 다.
    유 틸 리 티 의 parseRoles 방법 은 bitmap 인 코딩 의 roles 를 캐릭터 ID 의 목록 으로 해석 하 는 것 입 니 다.코드 는 다음 과 같 습 니 다.
    
    	//=========================         ======================================    	
        /**
         * 
         * @methodName		: parseRoles
         * @description		:        
         * @param roles		:           
         * @return			:   ID  
         * @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 방법 은 캐릭터 기능 권한 서비스 류 RoleFuncRightsService 의 방법 으로 List 유형의 캐릭터 ID 목록 에 따라 기능 권한 트 리 를 빠르게 가 져 오 는 기능 을 제공 합 니 다.
    기능 권한 트 리 TreeNode 유형 에 대해 서 는 다음 을 참조 하 시기 바 랍 니 다《자바 유 니 버 설 트 리 구조 데이터 관리》
    Spring Boot 의 동적 권한 변경 실현 에 관 한 전체 방안 에 관 한 글 은 여기까지 입 니 다.더 많은 Spring Boot 동적 권한 내용 은 이전 글 을 검색 하거나 아래 글 을 계속 찾 아 보 세 요.앞으로 도 많은 응원 부 탁 드 리 겠 습 니 다!

    좋은 웹페이지 즐겨찾기