目錄
SpringSecurity權限管理系統實戰—一、專案簡介和開發環境準備
SpringSecurity權限管理系統實戰—二、日志、介面檔案等實作
SpringSecurity權限管理系統實戰—三、主要頁面及介面實作
SpringSecurity權限管理系統實戰—四、整合SpringSecurity(上)
SpringSecurity權限管理系統實戰—五、整合SpringSecurity(下)
SpringSecurity權限管理系統實戰—六、SpringSecurity整合jwt
SpringSecurity權限管理系統實戰—七、處理一些問題
SpringSecurity權限管理系統實戰—八、AOP 記錄用戶日志、例外日志
SpringSecurity權限管理系統實戰—九、資料權限的配置
前言
這一章的部分是我寫到現在最累的一部分,累就累在邏輯的處理上,因為涉及到很多多表的操作,要處理好這部分的邏輯很麻煩
也讓我發現了資料庫設計之初的一些命名問題(之后再解決這個問題),
就像是用戶表中的id最好還是用user_id,而我之前用的卻是id,這會導致什么問題呢?比如說你在role_user這張關聯表中存的肯定是user_id吧,那么你在關聯這兩個表寫sql陳述句是就要不停的轉換id和user_id,腦殼子都給你繞暈了,血淋淋的教訓,大家要注意了,
這篇文章只是給一個思路,內容和邏輯都太復雜,還要對原有的部分進行修改,不能再像之前那樣一步一步貼代碼了
希望各位小伙伴能夠多多star支持,您的點贊就是我維護的動力
gitee和github中同步更新原始碼
附:阿里巴巴資料庫設計規范
一、什么是資料權限
權限設計具體來說可以分為功能權限和資料權限,功能權限就是角色能操作哪些介面,而資料權限就是角色能夠獲取到的哪些資料,
形象點來說,如果現在有一個公司,公司上下有很多部門,部門里有很多員工,而資料權限就是為了讓某個部門的人只能獲取到自己部門或著是指定部門的員工資訊,
二、新建如下表
分別是崗位表,部門表,用戶崗位關聯表和角色部門關聯表
my_user表中添加dept_id欄位,my_role表中添加data_scpoe欄位,前一個很好理解,就是部門的id,后一個代表的就是角色的資料權限范圍
(這篇文章很難寫,文章所代表的代碼內容也很難寫,大家想要理解透徹還是要從原始碼里看,自己做一遍才能真正的理解)
三、效果
| 效果 | |
|---|---|
效果就是這樣,代碼也不貼了,這部分邏輯有點復雜,代碼量很大,可以自行去原始碼中查看,
四、實作
這里只是介紹下如何實作資料權限,參考了若依專案中的實作方法
首先我們自定義一個注解
/**
* 資料權限過濾注解
* @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中
然后我們只需要在對于需要配置資料權限的mapper.xml中添加${params.dataScope}
再在呼叫此方法的service方法上添加注解@DataPermission(deptAlias = "d", userAlias = "u")
即可
這里我在BaseEntity中添加了一個params屬性來用于存放資料權限的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中同步更新原始碼
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/51210.html
標籤:Java
