自定义注解实现动态权限控制
2022年12月7日
前提知识
- 主要的表:
- 部门表、角色表、用户表、用户角色关联表、部门角色关联表
- 角色表的data_scope表示的数据范围:
- 数据范围 1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限 5:只能查看自己的用户信息,不能查看部门信息
- 数据表的关系如下:
案例
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
注解定义
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataScope {
/**
* 部门表别名
* @return
*/
String deptAlias() default "";
/**
* 用户表别名
* @return
*/
String userAlias() default "";
}
控制器定义
@RestController
@RequestMapping("/sys-dept")
public class SysDeptController {
@Autowired
ISysDeptService deptService;
@DataScope(deptAlias = "d")
@GetMapping("/list")
public List<SysDept> getAllDepts(SysDept sysDept) {
return deptService.allDeptList(sysDept);
}
}
@RestController
@RequestMapping("/sys-role")
public class SysRoleController {
@Autowired
ISysRoleService roleService;
@DataScope(deptAlias = "d", userAlias = "u")
@GetMapping("/list")
public List<SysRole> getAllRoleList(SysRole sysRole) {
return roleService.getAllRoleList(sysRole);
}
}
@RestController
@RequestMapping("/sys-user")
public class SysUserController {
@Autowired
ISysUserService userService;
@DataScope(deptAlias = "d",userAlias = "u")
@GetMapping("/list")
public List<SysUser> getAllUser(SysUser sysUser) {
return userService.getAllUser(sysUser);
}
}
切面定义
@Component
@Aspect
public class AspectDataScope {
//查询所有
private static final String DATA_SCOPE_ALL = "1";
//自定义
private static final String DATA_SCOPE_CUSTOM = "2";
//本部门
private static final String DATA_SCOPE_DEPT = "3";
//本部门以及子部门
private static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
//只能查看自己的用户信息,不能查看部门信息
private static final String DATA_SCOPE_SELF = "5";
//动态参数名
private static final String DATA_SCOPE_FIELD = "data_scope";
@Before("@annotation(dataScope)")
public void before(JoinPoint joinPoint, DataScope dataScope) {
//清除前端传过来的动态参数
clearDataScope(joinPoint);
//拿到登录后的用户凭证
SysUser sysUser = (SysUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
//如果为1 则代表为超级管理员 不用进行权限处理
if (sysUser.getUserId() == 1L) {
return;
}
StringBuilder sb = new StringBuilder();
List<SysRole> sysRoleList = sysUser.getSysRoleList();
//获取用户每个角色能够访问的部门信息
for (SysRole sysRole : sysRoleList) {
//查询每个角色的数据范围 1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限
String sysRoleDataScope = sysRole.getDataScope();
if (DATA_SCOPE_ALL.equals(sysRoleDataScope)) {
// 1全部数据权限 什么也不做
return;
} else if (DATA_SCOPE_CUSTOM.equals(sysRoleDataScope)) {
// 2自定义数据权限 也就是直接在角色部门表 查询
sb.append(String.format(" OR %s.dept_id in(SELECT dept_id FROM sys_role_dept where role_id=%d)", dataScope.deptAlias(), sysRole.getRoleId()));
} else if (DATA_SCOPE_DEPT.equals(sysRoleDataScope)) {
// 3当前用户所在部门数据权限
sb.append(String.format(" OR %s.dept_id=%d", dataScope.deptAlias(), sysUser.getDeptId()));
} else if (DATA_SCOPE_DEPT_AND_CHILD.equals(sysRoleDataScope)) {
// 4当前用户对应的部门以及用户的子部门
sb.append(String.format(" OR %s.dept_id in(SELECT dept_id FROM sys_dept where dept_id=%d or FIND_IN_SET(%d,ancestors))", dataScope.deptAlias(), sysUser.getDeptId(), sysUser.getDeptId()));
}else if (DATA_SCOPE_SELF.equals(sysRoleDataScope)) {
// 5只能查看当前用户信息,不能查看此角色部门信息和当前用户下的部门信息
if (StringUtils.isEmpty(dataScope.userAlias())) {
sb.append(" OR 1=0");
}else{
sb.append(String.format(" OR %s.user_id=%d", dataScope.userAlias(), sysUser.getUserId()));
}
}
}
Object arg = joinPoint.getArgs()[0];
if (arg != null && arg instanceof BaseEntity) {
BaseEntity baseEntity = (BaseEntity) arg;
//截取开始的 "or "
baseEntity.getParams().put(DATA_SCOPE_FIELD, " and (" + sb.substring(4) + ")");
}
}
private void clearDataScope(JoinPoint joinPoint) {
//获取第一个参数
Object arg = joinPoint.getArgs()[0];
if (arg != null && arg instanceof BaseEntity) {
BaseEntity baseEntity = (BaseEntity) arg;
baseEntity.getParams().put(DATA_SCOPE_FIELD, "");
}
}
}
实体定义
public class BaseEntity {
@TableField(exist = false)
private Map<String,String> params=new HashMap<>();
public Map<String, String> getParams() {
return params;
}
public void setParams(Map<String, String> params) {
this.params = params;
}
}
@TableName("sys_dept")
public class SysDept extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 部门id
*/
@TableId(value = "dept_id", type = IdType.AUTO)
private Long deptId;
/**
* 父部门id
*/
private Long parentId;
/**
* 祖级列表
*/
private String ancestors;
/**
* 部门名称
*/
private String deptName;
/**
* 显示顺序
*/
private Integer orderNum;
/**
* 负责人
*/
private String leader;
/**
* 联系电话
*/
private String phone;
/**
* 邮箱
*/
private String email;
/**
* 部门状态(0正常 1停用)
*/
private String status;
/**
* 删除标志(0代表存在 2代表删除)
*/
private String delFlag;
/**
* 创建者
*/
private String createBy;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新者
*/
private String updateBy;
/**
* 更新时间
*/
private LocalDateTime updateTime;
public Long getDeptId() {
return deptId;
}
public void setDeptId(Long deptId) {
this.deptId = deptId;
}
public Long getParentId() {
return parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
public String getAncestors() {
return ancestors;
}
public void setAncestors(String ancestors) {
this.ancestors = ancestors;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public Integer getOrderNum() {
return orderNum;
}
public void setOrderNum(Integer orderNum) {
this.orderNum = orderNum;
}
public String getLeader() {
return leader;
}
public void setLeader(String leader) {
this.leader = leader;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getDelFlag() {
return delFlag;
}
public void setDelFlag(String delFlag) {
this.delFlag = delFlag;
}
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public String getUpdateBy() {
return updateBy;
}
public void setUpdateBy(String updateBy) {
this.updateBy = updateBy;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
@Override
public String toString() {
return "SysDept{" +
"deptId=" + deptId +
", parentId=" + parentId +
", ancestors=" + ancestors +
", deptName=" + deptName +
", orderNum=" + orderNum +
", leader=" + leader +
", phone=" + phone +
", email=" + email +
", status=" + status +
", delFlag=" + delFlag +
", createBy=" + createBy +
", createTime=" + createTime +
", updateBy=" + updateBy +
", updateTime=" + updateTime +
"}";
}
}
@TableName("sys_role")
public class SysRole extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 角色ID
*/
@TableId(value = "role_id", type = IdType.AUTO)
private Long roleId;
/**
* 角色名称
*/
private String roleName;
/**
* 角色权限字符串
*/
private String roleKey;
/**
* 显示顺序
*/
private Integer roleSort;
/**
* 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)
*/
private String dataScope;
/**
* 菜单树选择项是否关联显示
*/
private Boolean menuCheckStrictly;
/**
* 部门树选择项是否关联显示
*/
private Boolean deptCheckStrictly;
/**
* 角色状态(0正常 1停用)
*/
private String status;
/**
* 删除标志(0代表存在 2代表删除)
*/
private String delFlag;
/**
* 创建者
*/
private String createBy;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新者
*/
private String updateBy;
/**
* 更新时间
*/
private LocalDateTime updateTime;
/**
* 备注
*/
private String remark;
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleKey() {
return roleKey;
}
public void setRoleKey(String roleKey) {
this.roleKey = roleKey;
}
public Integer getRoleSort() {
return roleSort;
}
public void setRoleSort(Integer roleSort) {
this.roleSort = roleSort;
}
public String getDataScope() {
return dataScope;
}
public void setDataScope(String dataScope) {
this.dataScope = dataScope;
}
public Boolean getMenuCheckStrictly() {
return menuCheckStrictly;
}
public void setMenuCheckStrictly(Boolean menuCheckStrictly) {
this.menuCheckStrictly = menuCheckStrictly;
}
public Boolean getDeptCheckStrictly() {
return deptCheckStrictly;
}
public void setDeptCheckStrictly(Boolean deptCheckStrictly) {
this.deptCheckStrictly = deptCheckStrictly;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getDelFlag() {
return delFlag;
}
public void setDelFlag(String delFlag) {
this.delFlag = delFlag;
}
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public String getUpdateBy() {
return updateBy;
}
public void setUpdateBy(String updateBy) {
this.updateBy = updateBy;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
@Override
public String toString() {
return "SysRole{" +
"roleId=" + roleId +
", roleName=" + roleName +
", roleKey=" + roleKey +
", roleSort=" + roleSort +
", dataScope=" + dataScope +
", menuCheckStrictly=" + menuCheckStrictly +
", deptCheckStrictly=" + deptCheckStrictly +
", status=" + status +
", delFlag=" + delFlag +
", createBy=" + createBy +
", createTime=" + createTime +
", updateBy=" + updateBy +
", updateTime=" + updateTime +
", remark=" + remark +
"}";
}
}
@TableName("sys_user")
public class SysUser extends BaseEntity implements Serializable, UserDetails {
private static final long serialVersionUID = 1L;
/**
* 用户ID
*/
@TableId(value = "user_id", type = IdType.AUTO)
private Long userId;
/**
* 部门ID
*/
private Long deptId;
/**
* 用户账号
*/
private String userName;
/**
* 用户昵称
*/
private String nickName;
/**
* 用户类型(00系统用户)
*/
private String userType;
/**
* 用户邮箱
*/
private String email;
/**
* 手机号码
*/
private String phonenumber;
/**
* 用户性别(0男 1女 2未知)
*/
private String sex;
/**
* 头像地址
*/
private String avatar;
/**
* 密码
*/
private String password;
/**
* 帐号状态(0正常 1停用)
*/
private String status;
/**
* 删除标志(0代表存在 2代表删除)
*/
private String delFlag;
/**
* 最后登录IP
*/
private String loginIp;
/**
* 最后登录时间
*/
private LocalDateTime loginDate;
/**
* 创建者
*/
private String createBy;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新者
*/
private String updateBy;
/**
* 更新时间
*/
private LocalDateTime updateTime;
/**
* 备注
*/
private String remark;
/**
* 角色
*/
@JsonIgnore
@TableField(exist = false)
private List<SysRole> sysRoleList;
public List<SysRole> getSysRoleList() {
return sysRoleList;
}
public void setSysRoleList(List<SysRole> sysRoleList) {
this.sysRoleList = sysRoleList;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public Long getDeptId() {
return deptId;
}
public void setDeptId(Long deptId) {
this.deptId = deptId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getUserType() {
return userType;
}
public void setUserType(String userType) {
this.userType = userType;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhonenumber() {
return phonenumber;
}
public void setPhonenumber(String phonenumber) {
this.phonenumber = phonenumber;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
@Override
@JsonIgnore
public Collection<? extends GrantedAuthority> getAuthorities() {
return sysRoleList.stream().map(r->new SimpleGrantedAuthority(r.getRoleKey())).collect(Collectors.toList());
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return userName;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public void setPassword(String password) {
this.password = password;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getDelFlag() {
return delFlag;
}
public void setDelFlag(String delFlag) {
this.delFlag = delFlag;
}
public String getLoginIp() {
return loginIp;
}
public void setLoginIp(String loginIp) {
this.loginIp = loginIp;
}
public LocalDateTime getLoginDate() {
return loginDate;
}
public void setLoginDate(LocalDateTime loginDate) {
this.loginDate = loginDate;
}
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public String getUpdateBy() {
return updateBy;
}
public void setUpdateBy(String updateBy) {
this.updateBy = updateBy;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
@Override
public String toString() {
return "SysUser{" +
"userId=" + userId +
", deptId=" + deptId +
", userName='" + userName + '\'' +
", nickName='" + nickName + '\'' +
", userType='" + userType + '\'' +
", email='" + email + '\'' +
", phonenumber='" + phonenumber + '\'' +
", sex='" + sex + '\'' +
", avatar='" + avatar + '\'' +
", password='" + password + '\'' +
", status='" + status + '\'' +
", delFlag='" + delFlag + '\'' +
", loginIp='" + loginIp + '\'' +
", loginDate=" + loginDate +
", createBy='" + createBy + '\'' +
", createTime=" + createTime +
", updateBy='" + updateBy + '\'' +
", updateTime=" + updateTime +
", remark='" + remark + '\'' +
", sysRoleList=" + sysRoleList +
'}';
}
}
mapper定义
public interface SysDeptMapper extends BaseMapper<SysDept> {
List<SysDept> allDeptList(SysDept sysDept);
}
public interface SysRoleMapper extends BaseMapper<SysRole> {
List<SysRole> getAllRoleList(SysRole sysRole);
}
public interface SysUserMapper extends BaseMapper<SysUser> {
List<SysRole> selectRoleByUserId(Long userId);
List<SysUser> getAllUser(SysUser sysUser);
}
Service定义
public interface ISysDeptService extends IService<SysDept> {
List<SysDept> allDeptList(SysDept sysDept);
}
public interface ISysRoleService extends IService<SysRole> {
List<SysRole> getAllRoleList(SysRole sysRole);
}
public interface ISysUserService extends IService<SysUser> {
List<SysUser> getAllUser(SysUser sysUser);
}
Service实现类定义
@Service
public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> implements ISysDeptService {
@Autowired
SysDeptMapper deptMapper;
@Override
public List<SysDept> allDeptList(SysDept sysDept) {
return deptMapper.allDeptList(sysDept);
}
}
@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements ISysRoleService {
@Autowired
SysRoleMapper sysRoleMapper;
@Override
public List<SysRole> getAllRoleList(SysRole sysRole) {
return sysRoleMapper.getAllRoleList(sysRole);
}
}
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService, UserDetailsService {
@Autowired
SysUserMapper sysUserMapper;
/**
* 根据用户名查用户
* @param username
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
QueryWrapper<SysUser> queryWrapper=new QueryWrapper<>();
queryWrapper.lambda().eq(SysUser::getUserName, username);
SysUser sysUser = getOne(queryWrapper);
if (sysUser == null) {
throw new UsernameNotFoundException("用户不存在");
}
//用户登录之后设置角色
List<SysRole> sysRoleList = sysUserMapper.selectRoleByUserId(sysUser.getUserId());
sysUser.setSysRoleList(sysRoleList);
return sysUser;
}
@Override
public List<SysUser> getAllUser(SysUser sysUser) {
return sysUserMapper.getAllUser(sysUser);
}
}
xml定义
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.lc.ds.mapper.SysDeptMapper">
<!--查询某个用户根据的用户角色能够查看的部门
根据部门表中的data_scope字段的不同值即对应不同的查询语句,将动态语句放入map中即可。
1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限
-->
<select id="allDeptList" resultType="org.lc.ds.entity.SysDept">
SELECT
*
FROM
sys_dept d
WHERE
d.del_flag = '0'
${params.data_scope}
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.lc.ds.mapper.SysRoleMapper">
<select id="getAllRoleList" resultType="org.lc.ds.entity.SysRole">
SELECT
sr.*
FROM
sys_role sr
LEFT JOIN sys_user_role sur ON sr.role_id = sur.role_id
LEFT JOIN sys_user u ON sur.user_id = u.user_id
LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
WHERE
sr.del_flag = '0'
${params.data_scope}
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.lc.ds.mapper.SysUserMapper">
<!--根据用户id查询用户角色信息-->
<select id="selectRoleByUserId" resultType="org.lc.ds.entity.SysRole">
SELECT
sr.*
FROM
sys_role sr,
sys_user_role sur
WHERE
sr.role_id = sur.role_id
AND sur.user_id = #{userId}
</select>
<select id="getAllUser" resultType="org.lc.ds.entity.SysUser">
SELECT
u.*
FROM
sys_user u
LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
WHERE
u.del_flag = '0'
${params.data_scope}
</select>
</mapper>
application.properties
# 应用名称
spring.application.name=data_scope
# 应用服务 WEB 访问端口
server.port=8080
#下面这些内容是为了让MyBatis映射
#指定Mybatis的Mapper文件
mybatis.mapper-locations=classpath:mapper/*.xml
#指定Mybatis的实体目录
mybatis.type-aliases-package=org.lc.ds.entity
# 数据库驱动:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据源名称
spring.datasource.name=defaultDataSource
# 数据库连接地址
spring.datasource.url=jdbc:mysql://ip:3306/tienchin?serverTimezone=Asia/Shanghai&useSSL=false
# 数据库用户名&密码:
spring.datasource.username=xxxx
spring.datasource.password=xxxx
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
启动类
@MapperScan(basePackages = "org.lc.ds.mapper")
@SpringBootApplication
public class DataScopeApplication {
public static void main(String[] args) {
SpringApplication.run(DataScopeApplication.class, args);
}
}