Каталог серий
Боевая система управления правами SpringSecurity — три, главная страница и реализация интерфейса
Боевая система управления правами SpringSecurity — четыре, интеграция SpringSecurity (включена)
Борьба с системой управления правами SpringSecurity — 5. Интеграция SpringSecurity (ниже)
Боевая система управления правами SpringSecurity — 6. SpringSecurity интегрирует JWT
Борьба с системой управления правами Spring Security — семь, разбираемся с некоторыми проблемами
Боевая система управления разрешениями Spring Security — 9. Настройка разрешений данных
предисловие
Часть этой главы — самая утомительная часть, которую я написал до сих пор.Усталость заключается в обработке логики.Поскольку есть много операций с несколькими таблицами, очень сложно разобраться с логикой этой части.
Это также привело меня к обнаружению некоторых проблем с именами в начале проектирования базы данных (которые будут рассмотрены позже).
Это как id в пользовательской таблице лучше использовать user_id, но я раньше использовал id. Какую проблему это вызовет?Например, вы должны хранить user_id в таблице ассоциации role_user, поэтому, когда вы пишете sql-операторы в ассоциации между этими двумя таблицами, вы должны постоянно преобразовывать id и user_id, а мозги дают У вас кружится голова . Кровавые уроки, все должны обратить внимание.
Эта статья просто для того, чтобы дать представление, содержание и логика слишком сложны, и исходные части должны быть изменены, и код уже не может быть размещен пошагово, как раньше.
Я надеюсь, что вы можете дать больше звездной поддержки, ваши лайки - мотивация для моего обслуживания.
giteeиgithubСинхронное обновление исходного кода
Прикрепил:Спецификация дизайна базы данных Alibaba
1. Что такое разрешение данных
В частности, дизайн разрешений можно разделить на функциональные разрешения и разрешения на данные. Функциональные разрешения относятся к тому, с какими интерфейсами может работать роль, а разрешения к данным относятся к тем данным, которые может получить роль.
С визуальной точки зрения, если сейчас есть компания, в компании есть много отделов вверх и вниз, и в отделе много сотрудников, а разрешение данных позволяет людям в определенном отделе получать только сотрудника. информацию своего отдела или назначенного отдела.
2. Создайте следующую таблицу
Это таблица должностей, таблица отделов, таблица ассоциаций должностей пользователей и таблица ассоциаций ролевых отделов.
Добавьте поле dept_id в таблицу my_user. Добавьте поле data_scpoe в таблицу my_role. Первое хорошо понятно, это идентификатор отдела, а второе представляет собой область разрешений данных роли. (Эту статью сложно написать, и содержание кода, представленное в статье, также сложно написать. Если вы хотите досконально разобраться, вам все равно придется посмотреть исходный код и сделать это самостоятельно, чтобы по-настоящему понять его.)
3. Эффект
Эффект | |
---|---|
Эффект такой, а код больше не выкладывается.Эта часть логики немного сложная и объем кода большой.Можете сами проверить в исходниках.
4. Реализация
Вот лишь введение в то, как реализовать права доступа к данным, со ссылкой на метод реализации в проекте Ruoyi. Сначала мы определяем аннотацию
/**
* 数据权限过滤注解
* @author codermy
* @createTime 2020/8/22
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataPermission {
/**
* 部门表的别名
*/
public String deptAlias() default "";
/**
* 用户表的别名
*/
public String userAlias() default "";
}
Определите другой класс аспекта
/**
* 数据过滤处理
* @author codermy
* @createTime 2020/8/22
*/
@Aspect
@Component
public class DataScopeAspect {
@Autowired
public RoleUserService roleUserService;
/**
* 全部数据权限
*/
public static final String DATA_SCOPE_ALL = "1";
/**
* 自定数据权限
*/
public static final String DATA_SCOPE_CUSTOM = "2";
/**
* 部门数据权限
*/
public static final String DATA_SCOPE_DEPT = "3";
/**
* 部门及以下数据权限
*/
public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
/**
* 仅本人数据权限
*/
public static final String DATA_SCOPE_SELF = "5";
/**
* 数据权限过滤关键字
*/
public static final String DATA_SCOPE = "dataScope";
/**
* 配置织入点
*/
@Pointcut("@annotation(com.codermy.myspringsecurityplus.admin.annotation.DataPermission)")
public void dataScopePointCut()
{
}
@Before("dataScopePointCut()")
public void doBefore(JoinPoint point) throws Throwable
{
handleDataScope(point);
}
protected void handleDataScope(final JoinPoint joinPoint)
{
// 获得注解
DataPermission controllerDataScope = getAnnotationLog(joinPoint);
if (controllerDataScope == null)
{
return;
}
// 获取当前的用户
JwtUserDto currentUser = SecurityUtils.getCurrentUser();
if (currentUser != null)
{
// 如果是超级管理员,则不过滤数据
if (!currentUser.isAdmin())
{
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
controllerDataScope.userAlias());
}
}
}
/**
* 数据范围过滤
*
* @param joinPoint 切点
* @param user 用户
* @param deptAlias 部门别名
* @param userAlias 用户别名
*/
public static void dataScopeFilter(JoinPoint joinPoint, JwtUserDto user, String deptAlias, String userAlias)
{
StringBuilder sqlString = new StringBuilder();
for (MyRole role : user.getRoleInfo())
{
String dataScope = role.getDataScope();
if (DATA_SCOPE_ALL.equals(dataScope))
{
sqlString = new StringBuilder();
break;
}
else if (DATA_SCOPE_CUSTOM.equals(dataScope))
{
sqlString.append(StrUtil.format(
" OR {}.id IN ( SELECT dept_id FROM my_role_dept WHERE role_id = {} ) ", deptAlias,
role.getId()));
}
else if (DATA_SCOPE_DEPT.equals(dataScope))
{
sqlString.append(StrUtil.format(" OR {}.id = {} ", deptAlias, user.getMyUser().getDeptId()));
}
else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
{
sqlString.append(StrUtil.format(
" OR {}.id IN ( SELECT id FROM my_dept WHERE id = {} or find_in_set( {} , ancestors ) )",
deptAlias, user.getMyUser().getDeptId(), user.getMyUser().getDeptId()));
}
else if (DATA_SCOPE_SELF.equals(dataScope))
{
if (StrUtil.isNotBlank(userAlias))
{
sqlString.append(StrUtil.format(" OR {}.id = {} ", userAlias, user.getMyUser().getId()));
}
else
{
// 数据权限为仅本人且没有userAlias别名不查询任何数据
sqlString.append(" OR 1=0 ");
}
}
}
if (StrUtil.isNotBlank(sqlString.toString()))
{
BaseEntity baseEntity;
for (int i = 0;i < joinPoint.getArgs().length ;i++ ){
if (joinPoint.getArgs()[i] instanceof BaseEntity){
baseEntity= (BaseEntity) joinPoint.getArgs()[i];
baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
}
}
}
}
/**
* 是否存在注解,如果存在就获取
*/
private DataPermission getAnnotationLog(JoinPoint joinPoint)
{
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null)
{
return method.getAnnotation(DataPermission.class);
}
return null;
}
}
Объясните роль этого аспектного класса: Когда вы определяете эту аннотацию в методе ServiceImpl, она получает dataScope (то есть область данных) роли текущего пользователя, вошедшего в систему, затем сравнивает ее значения и сохраняет соответствующий оператор sql в параметрах baseEntity
Затем нам просто нужно добавить ${params.dataScope} в mapper.xml для разрешения на настройку данных.Добавьте аннотацию @DataPermission(deptAlias="d", userAlias="u") к сервисному методу, который вызывает этот метод ПростоЗдесь я добавил атрибут params в BaseEntity для хранения sql разрешений данных.Итак, когда нам нужно вызвать этот sql, если разрешения не фильтруются, sql выглядит так
SELECT u.id, u.dept_id, u.user_name, u.password, u.nick_name
, u.phone, u.email, u.status, u.create_time, u.update_time
FROM my_user u
LEFT JOIN my_dept d ON u.dept_id = d.id
WHERE u.dept_id = ?
OR u.dept_id IN (
SELECT e.id
FROM my_dept e
WHERE FIND_IN_SET(?, ancestors)
)
ORDER BY u.id
Если тип разрешения данных, который мы даем этой роли, является настраиваемым разрешением данных, sql будет следующим
SELECT u.id, u.dept_id, u.user_name, u.password, u.nick_name
, u.phone, u.email, u.status, u.create_time, u.update_time
FROM my_user u
LEFT JOIN my_dept d ON u.dept_id = d.id
WHERE (u.dept_id = ?
OR u.dept_id IN (
SELECT e.id
FROM my_dept e
WHERE FIND_IN_SET(?, ancestors)
))
AND d.id IN (
SELECT dept_id
FROM my_role_dept
WHERE role_id = 2
)
ORDER BY u.id
Затем, если мы дадим роли «обычный пользователь» следующие разрешения данныхЗатем, когда мы входим в систему под пользователем этой роли, мы можем получить доступ только к информации о пользователе в соответствующем отделе. Когда мы фильтруем оператор, когда добавляем аннотации к соответствующему методу отдела и sql, то эффект такой giteeиgithubСинхронное обновление исходного кода