Realization of Privilege Verification by Custom Annotation

Keywords: Attribute JSON

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.

Posted by dannel77 on Tue, 01 Oct 2019 22:40:30 -0700