自定义注解在权限控制中环绕通知的使用

使用aop切面,然后通过环绕通知的形式,对请求的接口做权限控制,区分当前用户的等级权限,根据当前用户等级权限的查询内容。

一、权限参数定义

1、自定义注解

package com.net.microservices.test.project.infrastructure.aspect;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Permissions {
}

 2、权限参数获取方法

package com.net.microservices.test.project.infrastructure.aspect.dto;

import java.util.Set;


public interface BasePermissions {

    /**
     * 获取是否处理
     *
     * @return Boolean
     */
    Boolean getHandle();

    /**
     * 设置是否处理
     *
     * @param handle Boolean
     */
    void setHandle(Boolean handle);

    /**
     * 获取权限等级
     *
     * @return Integer
     */
    Integer getLevel();

    /**
     * 设置权限等级
     *
     * @param level Integer
     */
    void setLevel(Integer level);

    /**
     * 获取地市范围
     *
     * @return Set
     */
    Set<String> getCityList();

    /**
     * 设置地市范围
     *
     * @param cityList Set
     */
    void setCityList(Set<String> cityList);
}

3、自定义权限参数,可以获取当前用户等级权限(这里的参数需要实现BasePermissions接口)

package com.net.microservices.test.project.infrastructure.aspect.dto;

import lombok.Data;

import java.util.Set;


@Data
public class PermissionsDTO implements BasePermissions{

    /**
     * 是否处理权限
     */
    private Boolean handle;
    /**
     * 权限级别
     */
    private Integer level;
    /**
     * 区县范围
     */
    private Set<String> cityList;

}

二、自定义权限切面处理逻辑

1、创建枚举值

package com.net.microservices.test.infrastructure.enums;

import lombok.Getter;

@Getter
public enum NumberEnum {
    /**
     * 正确
     */
    TRUE("true",1),
    /**
     * 错误
     */
    FALSE("false",0),
    /**
     * 零的枚举
     */
    ZERO("0", 0),
    /**
     * 一的枚举
     */
    ONE("1", 1),
    /**
     * 二的枚举
     */
    TWO("2", 2),
    /**
     * 三的枚举
     */
    THREE("3", 3),
    /**
     * 四的枚举
     */
    FOUR("4", 4);

    final String name;
    final Integer code;

    NumberEnum(String name, Integer code) {
        this.name = name;
        this.code = code;
    }
}
package com.net.microservices.test.project.infrastructure.enums;

import lombok.Getter;


@Getter
public enum CityEnum {
    /**
     * 成都
     */
    CHENG_DU(1, "成都"),
    /**
     * 宜宾
     */
    YI_BIN(2, "宜宾"),
    /**
     * 眉山
     */
    MEI_SHAN(3, "眉山"),
    /**
     * 乐山
     */
    LE_SHAN(4, "乐山"),
    /**
     * 攀枝花
     */
    PAN_ZHI_HUA(5, "攀枝花"),
    /**
     * 凉山
     */
    LIANG_SHAN(6, "凉山"),
    /**
     * 资阳
     */
    ZI_YANG(7, "资阳"),
    /**
     * 自贡
     */
    ZI_GONG(8, "自贡"),
    /**
     * 内江
     */
    NEI_JIANG(9, "内江"),
    /**
     * 内江
     */
    LU_ZHOU(10, "泸州"),
    /**
     * 德阳
     */
    DE_YANG(11, "德阳"),
    /**
     * 绵阳
     */
    MIAN_YANG(12, "绵阳"),
    /**
     * 广元
     */
    GUANG_YUAN(13, "广元"),
    /**
     * 巴中
     */
    BA_ZHONG(14, "巴中"),
    /**
     * 遂宁
     */
    SUI_NING(15, "遂宁"),
    /**
     * 南充
     */
    NAN_CHONG(16, "南充"),
    /**
     * 达州
     */
    DA_ZHOU(17, "达州"),
    /**
     * 广安
     */
    GUANG_AN(18, "广安"),
    /**
     * 雅安
     */
    YA_AN(19, "雅安"),
    /**
     * 甘孜
     */
    GAN_ZI(20, "甘孜"),
    /**
     * 阿坝
     */
    A_BA(21, "阿坝"),
    /**
     * 省
     */
    S_GS_BB(22, "省"),
    ;

    /**
     * 地市编码
     */
    final int cityCode;
    /**
     * 地市名称
     */
    final String cityName;

    CityEnum(int cityCode, String cityName) {
        this.cityCode = cityCode;
        this.cityName = cityName;
    }

    public static CityEnum getByCityCode(int cityCode) {
        CityEnum[] values = CityEnum.values();
        for (CityEnum value : values) {
            if (value.cityCode == cityCode) {
                return value;
            }
        }
        return null;
    }

    public static CityEnum getByCityCode(Long cityCode) {
        return getByCityCode((int) cityCode.longValue());
    }

    public static String getNameByCode(String cityCode) {
        try {
            int cityId = Integer.parseInt(cityCode);
            CityEnum byRegionCode = getByCityCode(cityId);
            if (null != byRegionCode) {
                return byRegionCode.cityName;
            }
        } catch (NumberFormatException ignored) {
        }
        return null;
    }

    public static String getNameByCode(Long cityCode) {
        return getNameByCode(cityCode.toString());
    }
}

2、通过环绕注解@Around,自定义aop切面定义

package com.net.microservices.test.project.infrastructure.aspect;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import com.net.common.entity.OsUser;
import com.net.common.enums.ResponseData;
import com.net.common.enums.ResponseDataPage;
import com.net.common.util.StringUtil;
import com.net.common.util.TransEnumUtil;
import com.net.microservices.test.infrastructure.enums.NumberEnum;
import com.net.microservices.test.project.infrastructure.aspect.dto.BasePermissions;
import com.net.microservices.test.project.infrastructure.enums.CityEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

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


@Slf4j
@Aspect
@Order(value = 999)
@Component
public class AspectPermissions {

    /**
     * 环绕通知
     *
     * @param joinPoint 连接点
     * @return Object
     * @throws Throwable 异常
     */
    @Around(value = "@annotation(permissions)")
    public Object handleControllerMethod(ProceedingJoinPoint joinPoint, Permissions permissions) throws Throwable {
        StopWatch stopWatch = StopWatch.createStarted();
        log.info("【权限验证】执行接口开始,方法={}", joinPoint.getSignature());
        Object[] args = joinPoint.getArgs();
        BasePermissions basePermissions = null;
        if (null != args) {
            for (Object arg : args) {
                if (arg instanceof BasePermissions) {
                    basePermissions = (BasePermissions) arg;
                }
            }
        }
        if (null == basePermissions) {
            log.info("参数中未发现继承权限dto!");
        } else {
            OsUser user = OsUser.getInstance();
            basePermissions.setHandle(false);
            Integer level = getLevel(user);
            basePermissions.setLevel(level);
            if (NumberEnum.ZERO.getCode().equals(level)) {
                log.info("账号具有省级权限!");
            } else if (NumberEnum.ONE.getCode().equals(level)) {
                log.info("账号具有地市管理员权限!");
                basePermissions.setCityList(CollectionUtil.set(false, CityEnum.getNameByCode(user.getDomainId())));
            } else if (NumberEnum.TWO.getCode().equals(level)) {
                basePermissions.setCityList(CollectionUtil.set(false, CityEnum.getNameByCode(user.getDomainId())));
                log.info("账号具有运营人员权限!");
            } else {
                log.info("账号无权限!");
                return ResponseData.fail("账号无权限查看数据!");
            }
        }
        Object proceed = joinPoint.proceed(joinPoint.getArgs());
        if (null != basePermissions && !basePermissions.getHandle()) {
            log.info("接口未处理权限,权限过滤未生效!");
        }
        stopWatch.stop();
        long watchTime = stopWatch.getTime();
        log.info("【权限验证-结束】执行接口结束,方法={}, 返回值={},耗时={} (毫秒)", joinPoint.getSignature(), proceed, watchTime);
        return proceed;
    }

    /**
     * 获取权限等级 , 99 代表无配置
     *
     * @param user 用户信息
     * @return 等级
     */
    private Integer getLevel(OsUser user) {
        Map<String, String> enumMap = TransEnumUtil.getEnumMap("test_permissions_project");
        Map<String, Integer> perMapperMap = new HashMap<>(3);
        for (String s : enumMap.keySet()) {
            String value = enumMap.get(s);
            if (StringUtil.checkStr(value)) {
                String[] split = value.split(";");
                for (String v : split) {
                    perMapperMap.putIfAbsent(v, Integer.parseInt(s));
                }
            }
        }
        String roleIds = user.getRoleIds();
        int level = 99;
        if (StringUtil.checkStr(roleIds)) {
            String[] roleIdItems = roleIds.split(",");
            for (String roleIdItem : roleIdItems) {
                // 对比配置用户id的权限等级
                level = Math.min(level, MapUtil.getInt(perMapperMap, roleIdItem, 99));
            }
        }
        return level;
    }

}

3、权限验证方法:这里需要构造,然后获取用户等级,所属城市,权限(这里的权限对应自己配置的枚举值)

package com.net.microservices.test.project.infrastructure.aspect;


public interface LevelHandle {

    /**
     * 进行处理
     */
    void handle();
}
package com.net.microservices.test.project.infrastructure.aspect;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.net.common.entity.OsUser;
import com.net.microservices.test.infrastructure.enums.NumberEnum;
import com.net.microservices.test.project.infrastructure.aspect.dto.BasePermissions;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import lombok.Getter;
import lombok.Setter;

import java.util.Set;


@Getter
@Setter
public class PermissionsHandle<R> {

    SFunction<R, ?> cityName;

    SFunction<R, ?> staffNo;

    LevelHandle twoLevel;

    LevelHandle oneLevel;

    LevelHandle zeroLevel;

    OsUser user;

    public static <R> PermissionsHandle<R> build(SFunction<R, ?> cityName, LevelHandle twoLevel) {
        PermissionsHandle<R> handle = new PermissionsHandle<>();
        handle.setCityName(cityName);
        handle.setTwoLevel(twoLevel);
        return handle;
    }

    public static <R> PermissionsHandle<R> build(SFunction<R, ?> cityName, LevelHandle twoLevel, OsUser user) {
        PermissionsHandle<R> build = PermissionsHandle.build(cityName, twoLevel);
        build.setUser(user);
        return build;
    }

    public static <R> PermissionsHandle<R> build(SFunction<R, ?> cityName, SFunction<R, ?> staffNo, OsUser user) {
        PermissionsHandle<R> handle = new PermissionsHandle<>();
        handle.setCityName(cityName);
        handle.setUser(user);
        handle.setStaffNo(staffNo);
        return handle;
    }

    public void handle(BasePermissions basePermissions) {
        Integer level = basePermissions.getLevel();
        if (NumberEnum.ZERO.getCode().equals(level)) {
            if (null != zeroLevel) {
                zeroLevel.handle();
            }
        } else if (NumberEnum.ONE.getCode().equals(level)) {
            if (null != oneLevel) {
                oneLevel.handle();
            }
        } else if (NumberEnum.TWO.getCode().equals(level)) {
            if (null != twoLevel) {
                twoLevel.handle();
            }
        }
        basePermissions.setHandle(true);
    }

    public void handleDefault(BasePermissions basePermissions, MPJLambdaWrapper<R> lqw) {
        setOneLevel(() -> {
            addCityCondition(basePermissions, lqw);
        });
        handle(basePermissions);
    }


    public void addCityCondition(BasePermissions basePermissions, MPJLambdaWrapper<R> lqw) {
        if (null == user) {
            user = OsUser.getInstance();
        }
        Set<String> cityList = basePermissions.getCityList();
        if (CollectionUtil.isNotEmpty(cityList)) {
            lqw.in(cityName, basePermissions.getCityList());
        }
    }

}

三、用户权限的使用方式

例子1:

1.1、接口调用通过注解获取用户等级

    @ApiOperation(value = "查询项目统计")
    @Permissions
    @PostMapping(value = {"/project/stat"})
    public ResponseData<ProjectStatRespDTO> projectStat(@Validated @RequestBody List<String> projectStatusList, PermissionsDTO permissionsDTO) {
        // 通过注解@Permissions获取permissionsDTO参数
        return ResponseData.ok(projectInfoService.queryProjectStat(projectStatusList, permissionsDTO));
    }

1.2、业务代码处理

@Override
    public ProjectStatRespDTO queryProjectStat(List<String> projectStatusList, PermissionsDTO permissionsDTO) {
        OsUser osUser = OsUser.getInstance();
        ProjectStatRespDTO dto = new ProjectStatRespDTO();

        // 通过获取permissionsDTO等到用户权限等级
        List<Map<String, Object>> projectLevelStatList = projectInfoDao.getBaseMapper().getProjectLevelSum(osUser, permissionsDTO, projectStatusList);
        Map<String, List<Map<String, Object>>> mapProjectLevelStatList = projectLevelStatList.stream().collect(Collectors.groupingBy(e -> e.get("level").toString()));

        List<Map<String, Object>> projectLevelList = new ArrayList<>();
        Map<String, String> projectLevelEnumMap = TransEnumUtil.getEnumMap("test_s_project_level");
        for ( Map.Entry<String,String> projectLevel : projectLevelEnumMap.entrySet()) {
            if (!CollectionUtils.isEmpty(mapProjectLevelStatList.get(projectLevel.getValue()))) {
                projectLevelList.add(mapProjectLevelStatList.get(projectLevel.getValue()).get(0));
            } else {
                Map<String, Object> mapStats = new HashMap<>(2);
                mapStats.put("levelCount", 0);
                mapStats.put("level", projectLevel.getValue());
                projectLevelList.add(mapStats);
            }
        }
        // 表示已经处理了权限
        permissionsDTO.setHandle(true);
        dto.setProjectLevelList(projectLevelList);
        return dto;
    }

例子2:

2.1 调用接口

@ApiOperation(value = "查询项目分页信息")
    @Permissions
    @PostMapping(value = {"/project/queryProject/page"})
    public ResponseData<PageDataEntity<ProjectInfoRespDTO>> queryProjectPage(@Validated @RequestBody ProjectInfoReqDTO req) {
        return ResponseData.ok(projectInfoService.queryProjectPage(req));
    }

参数需要继承PermissionsPageDTO.class获取父类方法

package com.net.microservices.test.project.domain.dto.request;

import com.net.microservices.test.project.infrastructure.aspect.dto.PermissionsPageDTO;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serializable;
import java.util.List;


@EqualsAndHashCode(callSuper = true)
@Data
public class ProjectInfoReqDTO extends PermissionsPageDTO implements Serializable{

    /**
     * 项目级别
     */
    @ApiModelProperty(value = "项目级别",example = "S")
    private String projectLevel;

    /**
     * 项目名称
     */
    @ApiModelProperty(value = "项目名称",example = "客流统计监控项目")
    private String projectName;

    /**
     * 项目编码
     */
    @ApiModelProperty(value = "项目编码",example = "4100020")
    private String projectCode;

}

继承PageParamDTO.class和实现 BasePermissions接口

package com.net.microservices.test.project.infrastructure.aspect.dto;

import com.cttnet.microservices.test.infrastructure.common.dto.PageParamDTO;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.util.Set;


@EqualsAndHashCode(callSuper = true)
@Data
public class PermissionsPageDTO extends PageParamDTO implements BasePermissions {

    /**
     * 是否处理权限
     */
    private Boolean handle;
    /**
     * 权限级别
     */
    private Integer level;
    /**
     * 区县范围
     */
    private Set<String> cityList;
}
package com.net.microservices.test.infrastructure.common.dto;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;

/**
 * @author Yuzuru
 * 统一分页参数对象
 */
@Data
@ApiModel("统一分页参数对象")
@Valid
public class PageParamDTO {
    @NotNull
    @ApiModelProperty(value = "当前页码", example = "1", required = true)
    Integer currentPage;
    @ApiModelProperty(value = "页面数据条数", example = "20", required = true)
    @NotNull
    Integer pageSize;
}

2.2业务代码处理

权限验证

         

@DS("testDB")
@Service
public class ProjectInfoServiceImpl extends ServiceImpl<ProjectInfoMapper, ProjectInfo>
    implements ProjectInfoService{

// --------权限验证 String admin = "系统管理员";
// 1、先通过PermissionsHandle构造方法,生产获取用户的城市,再获取用户的等级
        MPJLambdaWrapper<ProjectInfo> lqw = new MPJLambdaWrapper<>();
        lqw.selectAs(ProjectInfo::getProjectType, ProjectInfoRespDTO::getProjectType);
        PermissionsHandle<ProjectInfo> permissionsHandle = PermissionsHandle.build(ProjectInfo::getCity, () -> {
            lqw.eq(ProjectUndertake::getUndertakenByStaffNo, osUser.getStaffNo());
        }, osUser);
        permissionsHandle.handleDefault(dto, lqw);

        IPage<ProjectInfoRespDTO> projectPage = projectInfoDao.selectJoinListPage(new Page<ProjectInfo>(req.getCurrentPage(), req.getPageSize()), ProjectInfoRespDTO.class, lqw);

}

2.2.1、通过PermissionsHandle的构造方法,设置用户的等级、所属城市、权限。

2.2.2、然后再通过handle()方法进行处理

2.2.3、通过basePermissions.setHandle(true);代码表示权限设置成功,环绕通知不会报错。

public static <R> PermissionsHandle<R> build(SFunction<R, ?> cityName, SFunction<R, ?> staffNo, OsUser user) {
        PermissionsHandle<R> handle = new PermissionsHandle<>();
        handle.setCityName(cityName);
        handle.setUser(user);
        handle.setStaffNo(staffNo);
        return handle;
    }

    public void handle(BasePermissions basePermissions) {
        Integer level = basePermissions.getLevel();
        if (NumberEnum.ZERO.getCode().equals(level)) {
            if (null != zeroLevel) {
                zeroLevel.handle();
            }
        } else if (NumberEnum.ONE.getCode().equals(level)) {
            if (null != oneLevel) {
                oneLevel.handle();
            }
        } else if (NumberEnum.TWO.getCode().equals(level)) {
            if (null != twoLevel) {
                twoLevel.handle();
            }
        }
        basePermissions.setHandle(true);
    }

    public void handleDefault(BasePermissions basePermissions, MPJLambdaWrapper<R> lqw) {
        setOneLevel(() -> {
            addCityCondition(basePermissions, lqw);
        });
        handle(basePermissions);
    }


    public void addCityCondition(BasePermissions basePermissions, MPJLambdaWrapper<R> lqw) {
        if (null == user) {
            user = OsUser.getInstance();
        }
        Set<String> cityList = basePermissions.getCityList();
        if (CollectionUtil.isNotEmpty(cityList)) {
            lqw.in(cityName, basePermissions.getCityList());
        }
    }

上述代码,是处理接口调用的时候,通过环绕通知的模式,查询当前用户账号的权限,然后根据权限查询所属数据,数据分权处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值