I just started a new company recently. Maintain an old system. Then I optimized the privilege logic based on AOP. Then use annotations. As a custom comment + AOP actual combat record.
1. Provides a comment: @CheckPermission
/** * @author zhangjiahui01 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Documented public @interface CheckPermission { /** * Permission Type Enum * * @return */ PermissionTypeEnum type() default PermissionTypeEnum.ONLY_CHECK_PATH; /** * Check if additional parameters in the parameter list are required, use this field to specify the parameter name * * 1.If it's directly a parameter in the parameter list: parameter name * 2.If the required value is the object's attribute: parameter name. attribute name * * Note: Currently only two-tier nested data structure parameters are supported * eg: * * @CheckPermission(param ="param") * public void getTempCardList(String param, HttpServletRequest request, HttpServletResponse response){ * ...... * } * * @CheckPermission(param ="eduExperienceAddRequest.number") * public void getTempCardList(EduExperienceAddRequest eduExperienceAddRequest, HttpServletRequest request, HttpServletResponse response){ * ...... * } * * @return */ String param() default "";
2. Enumeration of PermissionTypeEnum permission types
/** * @author zhangjiahui01 */ public enum PermissionTypeEnum { EMPLOY_INFO_ACTION("Employ Info Action", "Is there any right to operate employee information?(inspect URL+Superior-subordinate relationship with operating staff)"), EMPLOY_PAY_ACTION("Employ Pay Action", "Is there any operating authority for employee compensation?(inspect URL+Superior-subordinate relationship with operating staff)"), ONLY_CHECK_PATH("Only Check Path", "Check only for permission access URL"); private String code; private String desc; PermissionTypeEnum(String code, String desc) { this.code = code; this.desc = desc; } }
3. Handler of Analytical Annotations Implemented by AOP
/** * @author zhangjiahui01 */ @Aspect @Component public class PermissionAspectHandler { private static final Logger LOG = LoggerFactory.getLogger(PermissionAspectHandler.class); private static final String SIGN = "\\."; private static final String PARAM_SIGN = ","; @Autowired private PermissionService permissionService; @Before("@annotation(com.bjhl.erp.annotation.CheckPermission)") public void doBefore(JoinPoint joinPoint) throws Exception{ // get Login UserName ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); String loginUserName = RequestUtil.getUserFromSession(request); // get target method MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); // get Uri from @RequestMapping.value RequestMapping requestMapping = method.getAnnotation(RequestMapping.class); String[] requestMappingUri = requestMapping.value(); // parse @CheckPermission annotation CheckPermission checkPermissionAnnotation = method.getAnnotation(CheckPermission.class); PermissionTypeEnum permissionTypeEnum = checkPermissionAnnotation.type(); List<Long> variableValues = new ArrayList<>(); String param = checkPermissionAnnotation.param(); if(!StringUtils.isEmpty(param)){ String[] paramSplit = param.split(SIGN); Map<String, Object> mappingParamters = mappingParamter(joinPoint); if(mappingParamters != null && mappingParamters.containsKey(paramSplit[0])) { Object targetParamInstance = mappingParamters.get(paramSplit[0]); // the parameter values is field of object if (paramSplit.length > 1) { Field variableValueField = targetParamInstance.getClass().getDeclaredField(paramSplit[1]); variableValueField.setAccessible(true); targetParamInstance = variableValueField.get(paramSplit[1]); } if(targetParamInstance instanceof Number){ variableValues.add((Long)targetParamInstance); } if(targetParamInstance instanceof String){ String variableListStr = (String)targetParamInstance; if(variableListStr.contains(PARAM_SIGN)){ variableValues = Arrays.stream(variableListStr.split(PARAM_SIGN)).map(e->Long.valueOf(e)).collect(Collectors.toList()); } } } } if(!checkPermission(permissionTypeEnum,loginUserName,requestMappingUri[0],variableValues)){ LOG.info("checkPermission result: no permissio userName:{} uri:{}",loginUserName,requestMappingUri[0]); throw new NoPermissionException("Insufficient authority"); } } /** * check Permission main function * * @param type * @param userName * @param requestMappingUri * @param params * @return */ private Boolean checkPermission(PermissionTypeEnum type, String userName, String requestMappingUri, List<Long> params){ LOG.info("checkPermission main function param.permissionTypeEnum:{} UserName:{},Uri:{},params:{}", type,userName,requestMappingUri, JSON.toJSONString(params)); // check paramter if(null == type || StringUtils.isEmpty(userName) || StringUtils.isEmpty(requestMappingUri)){ LOG.error("checkPermission param error,permissionType or userName or requestMapping uri is null"); return false; } switch (type){ case EMPLOY_INFO_ACTION: if(!CollectionUtils.isEmpty(params)){ return permissionService.hasOptAuthority(userName, params.get(0), requestMappingUri); }else{ return permissionService.hasOptAuthority(userName,null, requestMappingUri); } case EMPLOY_PAY_ACTION: return permissionService.hasStructAuthority(userName,params,requestMappingUri); case ONLY_CHECK_PATH: return permissionService.onlyCheckPath(userName,requestMappingUri); } return false; } /** * mapping method paramter list * * @param joinPoint * @return fieldName : value */ public Map<String, Object> mappingParamter(JoinPoint joinPoint){ Object[] args = joinPoint.getArgs(); String[] parameterNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames(); if(args.length != 0 && parameterNames.length != 0 && args.length == parameterNames.length){ for (int i = 0; i < args.length-1; i++) { Map<String,Object> argMapping = new HashMap<>(); argMapping.put(parameterNames[i],args[i]); LOG.info("request handler mappingParamter result:{}",JSON.toJSONString(argMapping)); return argMapping; } } return null; } }
4. Unified exception handling of Controller:
@RestControllerAdvice public class ExceptionHandler { @ExceptionHandler({ NoPermissionException.class }) @ResponseStatus(HttpStatus.OK) public WebResponse doNoPermissionException(Exception e) { log.error(e.getMessage(), e); WebResponse<String> webResponse = new WebResponse(); webResponse.setStatus(WebResponseStatus.WEB_STATUS_NO_PERMISSION); webResponse.setError(new ErrorCode(1, e.getMessage())); return webResponse; } }
Finish, this is a more general template, complete solution. The privilege checking logic can be customized according to the rules of its own system.