新增角色验证与权限验证完全分离

This commit is contained in:
shengzhang
2020-12-28 02:00:32 +08:00
parent 43308bf593
commit 47140cea07
14 changed files with 321 additions and 85 deletions

View File

@@ -22,21 +22,9 @@ public @interface SaCheckPermission {
String [] value() default {}; String [] value() default {};
/** /**
* 需要验证的权限码 (int类型) * 指定验证模式是AND还是OR默认AND
* @return 需要验证的权限码 (int类型) * @return 验证模式
*/ */
int [] valueInt() default {}; SaMode mode() default SaMode.AND;
/**
* 需要验证的权限码 (long类型)
* @return 需要验证的权限码 (long类型)
*/
long [] valueLong() default {};
/**
* 是否属于and型验证true=必须全部具有false=只要具有一个就可以通过
* @return 是否属于and型验证
*/
boolean isAnd() default true;
} }

View File

@@ -0,0 +1,30 @@
package cn.dev33.satoken.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 标注一个路由方法,当前会话必须具有指定角色标识才可以通过
* <p> 可标注在类上,其效果等同于标注在此类的所有方法上
* @author kong
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface SaCheckRole {
/**
* 需要验证的角色标识
* @return 需要验证的权限码
*/
String [] value() default {};
/**
* 指定验证模式是AND还是OR默认AND
* @return 验证模式
*/
SaMode mode() default SaMode.AND;
}

View File

@@ -0,0 +1,20 @@
package cn.dev33.satoken.annotation;
/**
* 指定注解鉴权的验证模式
* @author kong
*
*/
public enum SaMode {
/**
* 必须具有所有的选项
*/
AND,
/**
* 只需具有其中一个选项
*/
OR
}

View File

@@ -8,20 +8,19 @@ import cn.dev33.satoken.stp.StpUtil;
* *
*/ */
public class NotPermissionException extends RuntimeException { public class NotPermissionException extends RuntimeException {
/** /**
* *
*/ */
private static final long serialVersionUID = 6806129545290130142L; private static final long serialVersionUID = 6806129545290130142L;
/** /** 权限码 */
* 权限码 private String code;
*/
private Object code;
/** /**
* @return 获得权限码 * @return 获得权限码
*/ */
public Object getCode() { public String getCode() {
return code; return code;
} }
@@ -38,11 +37,11 @@ public class NotPermissionException extends RuntimeException {
} }
public NotPermissionException(Object code) { public NotPermissionException(String code) {
this(code, StpUtil.stpLogic.loginKey); this(code, StpUtil.stpLogic.loginKey);
} }
public NotPermissionException(Object code, String loginKey) { public NotPermissionException(String code, String loginKey) {
// 这里到底要不要拼接上login_key呢纠结 // 这里到底要不要拼接上loginKey呢纠结
super("无此权限:" + code); super("无此权限:" + code);
this.code = code; this.code = code;
this.loginKey = loginKey; this.loginKey = loginKey;

View File

@@ -0,0 +1,51 @@
package cn.dev33.satoken.exception;
import cn.dev33.satoken.stp.StpUtil;
/**
* 没有指定角色标识,抛出的异常
* @author kong
*
*/
public class NotRoleException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 8243974276159004739L;
/** 角色标识 */
private String role;
/**
* @return 获得角色标识
*/
public String getRole() {
return role;
}
/**
* loginKey
*/
private String loginKey;
/**
* 获得loginKey
* @return loginKey
*/
public String getLoginKey() {
return loginKey;
}
public NotRoleException(String role) {
this(role, StpUtil.stpLogic.loginKey);
}
public NotRoleException(String role, String loginKey) {
// 这里到底要不要拼接上loginKey呢纠结
super("无此角色:" + role);
this.role = role;
this.loginKey = loginKey;
}
}

View File

@@ -14,7 +14,15 @@ public interface StpInterface {
* @param loginKey 具体的stp标识 * @param loginKey 具体的stp标识
* @return 该账号id具有的权限码集合 * @return 该账号id具有的权限码集合
*/ */
public List<String> getPermissionCodeList(Object loginId, String loginKey); public List<String> getPermissionList(Object loginId, String loginKey);
/**
* 返回指定loginId所拥有的角色标识集合
* @param loginId 账号id
* @param loginKey 具体的stp标识
* @return 该账号id具有的角色标识集合
*/
public List<String> getRoleList(Object loginId, String loginKey);
} }

View File

@@ -10,7 +10,12 @@ import java.util.List;
public class StpInterfaceDefaultImpl implements StpInterface { public class StpInterfaceDefaultImpl implements StpInterface {
@Override @Override
public List<String> getPermissionCodeList(Object loginId, String loginKey) { public List<String> getPermissionList(Object loginId, String loginKey) {
return new ArrayList<String>();
}
@Override
public List<String> getRoleList(Object loginId, String loginKey) {
return new ArrayList<String>(); return new ArrayList<String>();
} }

View File

@@ -11,6 +11,7 @@ import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException; import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.util.SaTokenInsideUtil; import cn.dev33.satoken.util.SaTokenInsideUtil;
@@ -626,7 +627,6 @@ public class StpLogic {
return SaTokenManager.getSaTokenDao().getSessionTimeout(getKeyTokenSession(tokenValue)); return SaTokenManager.getSaTokenDao().getSessionTimeout(getKeyTokenSession(tokenValue));
} }
/** /**
* 获取当前token[临时过期]剩余有效时间 (单位: 秒) * 获取当前token[临时过期]剩余有效时间 (单位: 秒)
* @return token[临时过期]剩余有效时间 * @return token[临时过期]剩余有效时间
@@ -667,73 +667,136 @@ public class StpLogic {
} }
return timeout; return timeout;
} }
// =================== 角色验证操作 ===================
/**
* 指定账号id是否含有角色标识
* @param loginId 账号id
* @param role 角色标识
* @return 是否含有指定角色标识
*/
public boolean hasRole(Object loginId, String role) {
List<String> roleList = SaTokenManager.getStpInterface().getRoleList(loginId, loginKey);
return !(roleList == null || roleList.contains(role) == false);
}
/**
* 当前账号id是否含有指定角色标识
* @param role 角色标识
* @return 是否含有指定角色标识
*/
public boolean hasRole(String role) {
return hasRole(getLoginId(), role);
}
/**
* 当前账号是否含有指定角色标识,没有就抛出异常
* @param role 角色标识
*/
public void checkRole(String role) {
if(hasRole(role) == false) {
throw new NotRoleException(role, this.loginKey);
}
}
/**
* 当前账号是否含有指定角色标识, [指定多个,必须全都有]
* @param roleArray 角色标识数组
*/
public void checkRoleAnd(String... roleArray){
Object loginId = getLoginId();
List<String> roleList = SaTokenManager.getStpInterface().getRoleList(loginId, loginKey);
for (String role : roleArray) {
if(roleList.contains(role) == false) {
throw new NotRoleException(role, this.loginKey); // 没有权限抛出异常
}
}
}
/**
* 当前账号是否含有指定角色标识, [指定多个,有一个就可以通过]
* @param roleArray 角色标识数组
*/
public void checkRoleOr(String... roleArray){
Object loginId = getLoginId();
List<String> roleList = SaTokenManager.getStpInterface().getRoleList(loginId, loginKey);
for (String role : roleArray) {
if(roleList.contains(role) == true) {
return; // 有的话提前退出
}
}
if(roleArray.length > 0) {
throw new NotRoleException(roleArray[0], this.loginKey); // 没有权限抛出异常
}
}
// =================== 权限验证操作 =================== // =================== 权限验证操作 ===================
/** /**
* 指定账号id是否含有指定权限 * 指定账号id是否含有指定权限
* @param loginId 账号id * @param loginId 账号id
* @param permissionCode 权限码 * @param permission 权限码
* @return 是否含有指定权限 * @return 是否含有指定权限
*/ */
public boolean hasPermission(Object loginId, String permissionCode) { public boolean hasPermission(Object loginId, String permission) {
List<String> permissionCodeList = SaTokenManager.getStpInterface().getPermissionCodeList(loginId, loginKey); List<String> permissionList = SaTokenManager.getStpInterface().getPermissionList(loginId, loginKey);
return !(permissionCodeList == null || permissionCodeList.contains(permissionCode) == false); return !(permissionList == null || permissionList.contains(permission) == false);
} }
/** /**
* 当前账号id是否含有指定权限 * 当前账号id是否含有指定权限
* @param permissionCode 权限码 * @param permission 权限码
* @return 是否含有指定权限 * @return 是否含有指定权限
*/ */
public boolean hasPermission(String permissionCode) { public boolean hasPermission(String permission) {
return hasPermission(getLoginId(), permissionCode); return hasPermission(getLoginId(), permission);
} }
/** /**
* 当前账号是否含有指定权限, 没有就抛出异常 * 当前账号是否含有指定权限, 没有就抛出异常
* @param permissionCode 权限码 * @param permission 权限码
*/ */
public void checkPermission(String permissionCode) { public void checkPermission(String permission) {
if(hasPermission(permissionCode) == false) { if(hasPermission(permission) == false) {
throw new NotPermissionException(permissionCode, this.loginKey); throw new NotPermissionException(permission, this.loginKey);
} }
} }
/** /**
* 当前账号是否含有指定权限, [指定多个,必须全都有] * 当前账号是否含有指定权限, [指定多个,必须全都有]
* @param permissionCodeArray 权限码数组 * @param permissionArray 权限码数组
*/ */
public void checkPermissionAnd(String... permissionCodeArray){ public void checkPermissionAnd(String... permissionArray){
Object loginId = getLoginId(); Object loginId = getLoginId();
List<String> permissionCodeList = SaTokenManager.getStpInterface().getPermissionCodeList(loginId, loginKey); List<String> permissionList = SaTokenManager.getStpInterface().getPermissionList(loginId, loginKey);
for (String pcode : permissionCodeArray) { for (String permission : permissionArray) {
if(permissionCodeList.contains(pcode) == false) { if(permissionList.contains(permission) == false) {
throw new NotPermissionException(pcode, this.loginKey); // 没有权限抛出异常 throw new NotPermissionException(permission, this.loginKey); // 没有权限抛出异常
} }
} }
} }
/** /**
* 当前账号是否含有指定权限, [指定多个,有一个就可以通过] * 当前账号是否含有指定权限, [指定多个,有一个就可以通过]
* @param permissionCodeArray 权限码数组 * @param permissionArray 权限码数组
*/ */
public void checkPermissionOr(String... permissionCodeArray){ public void checkPermissionOr(String... permissionArray){
Object loginId = getLoginId(); Object loginId = getLoginId();
List<String> permissionCodeList = SaTokenManager.getStpInterface().getPermissionCodeList(loginId, loginKey); List<String> permissionList = SaTokenManager.getStpInterface().getPermissionList(loginId, loginKey);
for (String permissionCode : permissionCodeArray) { for (String permission : permissionArray) {
if(permissionCodeList.contains(permissionCode) == true) { if(permissionList.contains(permission) == true) {
return; // 有的话提前退出 return; // 有的话提前退出
} }
} }
if(permissionCodeArray.length > 0) { if(permissionArray.length > 0) {
throw new NotPermissionException(permissionCodeArray[0], this.loginKey); // 没有权限抛出异常 throw new NotPermissionException(permissionArray[0], this.loginKey); // 没有权限抛出异常
} }
} }
// =================== 返回相应key =================== // =================== 返回相应key ===================
/** /**

View File

@@ -254,19 +254,10 @@ public class StpUtil {
* 获取当前登录者的token剩余有效时间 (单位: 秒) * 获取当前登录者的token剩余有效时间 (单位: 秒)
* @return token剩余有效时间 * @return token剩余有效时间
*/ */
public static long getTimeout() { public static long getTokenTimeout() {
return stpLogic.getTokenTimeout(); return stpLogic.getTokenTimeout();
} }
/**
* 获取指定loginId的token剩余有效时间 (单位: 秒)
* @param loginId 指定loginId
* @return token剩余有效时间
*/
public static long getTimeoutByLoginId(Object loginId) {
return stpLogic.getTokenTimeoutByLoginId(loginId);
}
/** /**
* 获取当前登录者的Session剩余有效时间 (单位: 秒) * 获取当前登录者的Session剩余有效时间 (单位: 秒)
* @return token剩余有效时间 * @return token剩余有效时间
@@ -274,14 +265,13 @@ public class StpUtil {
public static long getSessionTimeout() { public static long getSessionTimeout() {
return stpLogic.getSessionTimeout(); return stpLogic.getSessionTimeout();
} }
/** /**
* 获取指定loginId的Session剩余有效时间 (单位: 秒) * 获取当前token的专属Session剩余有效时间 (单位: 秒)
* @param loginId 指定loginId * @return token剩余有效时间
* @return token剩余有效时间
*/ */
public static long getSessionTimeoutByLoginId(Object loginId) { public static long getTokenSessionTimeout() {
return stpLogic.getSessionTimeoutByLoginId(loginId); return stpLogic.getTokenSessionTimeout();
} }
/** /**
@@ -292,15 +282,52 @@ public class StpUtil {
return stpLogic.getTokenActivityTimeout(); return stpLogic.getTokenActivityTimeout();
} }
/**
* 获取指定token[临时过期]剩余有效时间 (单位: 秒)
* @param tokenValue 指定token // =================== 角色验证操作 ===================
* @return token[临时过期]剩余有效时间
/**
* 指定账号id是否含有角色标识
* @param loginId 账号id
* @param role 角色标识
* @return 是否含有指定角色标识
*/ */
public static long getTokenActivityTimeoutByToken(String tokenValue) { public static boolean hasRole(Object loginId, String role) {
return stpLogic.getTokenActivityTimeoutByToken(tokenValue); return stpLogic.hasRole(loginId, role);
} }
/**
* 当前账号id是否含有指定角色标识
* @param role 角色标识
* @return 是否含有指定角色标识
*/
public static boolean hasRole(String role) {
return stpLogic.hasRole(role);
}
/**
* 当前账号是否含有指定角色标识,没有就抛出异常
* @param role 角色标识
*/
public static void checkRole(String role) {
stpLogic.checkRole(role);
}
/**
* 当前账号是否含有指定角色标识, [指定多个,必须全都有]
* @param roleArray 角色标识数组
*/
public static void checkRoleAnd(String... roleArray){
stpLogic.checkRoleAnd(roleArray);
}
/**
* 当前账号是否含有指定角色标识, [指定多个,有一个就可以通过]
* @param roleArray 角色标识数组
*/
public static void checkRoleOr(String... roleArray){
stpLogic.checkRoleOr(roleArray);
}
// =================== 权限验证操作 =================== // =================== 权限验证操作 ===================

View File

@@ -8,14 +8,16 @@ import org.springframework.stereotype.Component;
import cn.dev33.satoken.stp.StpInterface; import cn.dev33.satoken.stp.StpInterface;
/** /**
* 自定义权限验证接口扩展 * 自定义权限验证接口扩展
*/ */
@Component // 打开此注解保证此类被springboot扫描即可完成sa-token的自定义权限验证扩展 @Component // 打开此注解保证此类被springboot扫描即可完成sa-token的自定义权限验证扩展
public class StpInterfaceImpl implements StpInterface { public class StpInterfaceImpl implements StpInterface {
// 返回一个账号所拥有的权限码集合 /**
* 返回一个账号所拥有的权限码集合
*/
@Override @Override
public List<String> getPermissionCodeList(Object login_id, String login_key) { public List<String> getPermissionList(Object login_id, String login_key) {
List<String> list = new ArrayList<String>(); // 本list仅做模拟实际项目中要根据具体业务逻辑来查询权限 List<String> list = new ArrayList<String>(); // 本list仅做模拟实际项目中要根据具体业务逻辑来查询权限
list.add("101"); list.add("101");
list.add("user-add"); list.add("user-add");
@@ -26,4 +28,15 @@ public class StpInterfaceImpl implements StpInterface {
return list; return list;
} }
/**
* 返回一个账号所拥有的角色标识集合
*/
@Override
public List<String> getRoleList(Object loginId, String loginKey) {
List<String> list = new ArrayList<String>(); // 本list仅做模拟实际项目中要根据具体业务逻辑来查询角色
list.add("admin");
list.add("super-admin");
return list;
}
} }

View File

@@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException; import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
/** /**
* 全局异常处理 * 全局异常处理
@@ -40,6 +41,9 @@ public class GlobalException {
if (e instanceof NotLoginException) { // 如果是未登录异常 if (e instanceof NotLoginException) { // 如果是未登录异常
NotLoginException ee = (NotLoginException) e; NotLoginException ee = (NotLoginException) e;
aj = AjaxJson.getNotLogin().setMsg(ee.getMessage()); aj = AjaxJson.getNotLogin().setMsg(ee.getMessage());
} else if(e instanceof NotRoleException) { // 如果是角色异常
NotRoleException ee = (NotRoleException) e;
aj = AjaxJson.getNotJur("无此角色:" + ee.getRole());
} else if(e instanceof NotPermissionException) { // 如果是权限异常 } else if(e instanceof NotPermissionException) { // 如果是权限异常
NotPermissionException ee = (NotPermissionException) e; NotPermissionException ee = (NotPermissionException) e;
aj = AjaxJson.getNotJur("无此权限:" + ee.getCode()); aj = AjaxJson.getNotJur("无此权限:" + ee.getCode());

View File

@@ -46,9 +46,31 @@ public class TestController {
return AjaxJson.getSuccess(); return AjaxJson.getSuccess();
} }
// 测试权限接口, 浏览器访问: http://localhost:8081/test/jur // 测试角色接口, 浏览器访问: http://localhost:8081/test/testRole
@RequestMapping("jur") @RequestMapping("testRole")
public AjaxJson jur() { public AjaxJson testRole() {
System.out.println("======================= 进入方法,测试角色接口 ========================= ");
System.out.println("是否具有角色标识 user " + StpUtil.hasRole("user"));
System.out.println("是否具有角色标识 admin " + StpUtil.hasRole("admin"));
System.out.println("没有admin权限就抛出异常");
StpUtil.checkRole("admin");
System.out.println("在【admin、user】中只要拥有一个就不会抛出异常");
StpUtil.checkRoleOr("admin", "user");
System.out.println("在【admin、user】中必须全部拥有才不会抛出异常");
StpUtil.checkRoleAnd("admin", "user");
System.out.println("角色测试通过");
return AjaxJson.getSuccess();
}
// 测试权限接口, 浏览器访问: http://localhost:8081/test/testJur
@RequestMapping("testJur")
public AjaxJson testJur() {
System.out.println("======================= 进入方法,测试权限接口 ========================= "); System.out.println("======================= 进入方法,测试权限接口 ========================= ");
System.out.println("是否具有权限101" + StpUtil.hasPermission("101")); System.out.println("是否具有权限101" + StpUtil.hasPermission("101"));
@@ -69,6 +91,7 @@ public class TestController {
return AjaxJson.getSuccess(); return AjaxJson.getSuccess();
} }
// 测试会话session接口 浏览器访问: http://localhost:8081/test/session // 测试会话session接口 浏览器访问: http://localhost:8081/test/session
@RequestMapping("session") @RequestMapping("session")
public AjaxJson session() { public AjaxJson session() {

View File

@@ -19,8 +19,12 @@
- 类似API还有 - 类似API还有
- `StpUtil.getSessionByLoginId(Object loginId, boolean isCreate)` 获取当前会话登录id, `isCreate`代表指定是否在无`session`的情况下新建并返回 - `StpUtil.getSessionByLoginId(Object loginId, boolean isCreate)` 获取当前会话登录id, `isCreate`代表指定是否在无`session`的情况下新建并返回
#### StpUtil.hasPermission(Object loginId, Object pcode) #### StpUtil.hasRole(Object loginId, Object role)
- 指定`loginId`是否含有指定角色
#### StpUtil.hasPermission(Object loginId, Object permission)
- 指定`loginId`是否含有指定权限 - 指定`loginId`是否含有指定权限

View File

@@ -8,6 +8,7 @@ import org.springframework.web.servlet.HandlerInterceptor;
import cn.dev33.satoken.annotation.SaCheckLogin; import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.dev33.satoken.stp.StpLogic; import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
@@ -62,7 +63,7 @@ public class SaCheckInterceptor implements HandlerInterceptor {
SaCheckPermission scp = method.getMethodAnnotation(SaCheckPermission.class); SaCheckPermission scp = method.getMethodAnnotation(SaCheckPermission.class);
if(scp != null) { if(scp != null) {
String[] permissionCodeArray = scp.value(); String[] permissionCodeArray = scp.value();
if(scp.isAnd()) { if(scp.mode() == SaMode.AND) {
stpLogic.checkPermissionAnd(permissionCodeArray); // 必须全部都有 stpLogic.checkPermissionAnd(permissionCodeArray); // 必须全部都有
} else { } else {
stpLogic.checkPermissionOr(permissionCodeArray); // 有一个就行了 stpLogic.checkPermissionOr(permissionCodeArray); // 有一个就行了
@@ -72,7 +73,7 @@ public class SaCheckInterceptor implements HandlerInterceptor {
scp = method.getBeanType().getAnnotation(SaCheckPermission.class); scp = method.getBeanType().getAnnotation(SaCheckPermission.class);
if(scp != null) { if(scp != null) {
String[] permissionCodeArray = scp.value(); String[] permissionCodeArray = scp.value();
if(scp.isAnd()) { if(scp.mode() == SaMode.AND) {
stpLogic.checkPermissionAnd(permissionCodeArray); // 必须全部都有 stpLogic.checkPermissionAnd(permissionCodeArray); // 必须全部都有
} else { } else {
stpLogic.checkPermissionOr(permissionCodeArray); // 有一个就行了 stpLogic.checkPermissionOr(permissionCodeArray); // 有一个就行了