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

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 {};
/**
* 需要验证的权限码 (int类型)
* @return 需要验证的权限码 (int类型)
* 指定验证模式是AND还是OR默认AND
* @return 验证模式
*/
int [] valueInt() default {};
/**
* 需要验证的权限码 (long类型)
* @return 需要验证的权限码 (long类型)
*/
long [] valueLong() default {};
/**
* 是否属于and型验证true=必须全部具有false=只要具有一个就可以通过
* @return 是否属于and型验证
*/
boolean isAnd() default true;
SaMode mode() default SaMode.AND;
}

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 {
/**
*
*/
private static final long serialVersionUID = 6806129545290130142L;
/**
* 权限码
*/
private Object code;
/** 权限码 */
private String code;
/**
* @return 获得权限码
*/
public Object getCode() {
public String getCode() {
return code;
}
@@ -38,11 +37,11 @@ public class NotPermissionException extends RuntimeException {
}
public NotPermissionException(Object code) {
public NotPermissionException(String code) {
this(code, StpUtil.stpLogic.loginKey);
}
public NotPermissionException(Object code, String loginKey) {
// 这里到底要不要拼接上login_key呢纠结
public NotPermissionException(String code, String loginKey) {
// 这里到底要不要拼接上loginKey呢纠结
super("无此权限:" + code);
this.code = code;
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标识
* @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 {
@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>();
}

View File

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

View File

@@ -254,19 +254,10 @@ public class StpUtil {
* 获取当前登录者的token剩余有效时间 (单位: 秒)
* @return token剩余有效时间
*/
public static long getTimeout() {
public static long getTokenTimeout() {
return stpLogic.getTokenTimeout();
}
/**
* 获取指定loginId的token剩余有效时间 (单位: 秒)
* @param loginId 指定loginId
* @return token剩余有效时间
*/
public static long getTimeoutByLoginId(Object loginId) {
return stpLogic.getTokenTimeoutByLoginId(loginId);
}
/**
* 获取当前登录者的Session剩余有效时间 (单位: 秒)
* @return token剩余有效时间
@@ -274,14 +265,13 @@ public class StpUtil {
public static long getSessionTimeout() {
return stpLogic.getSessionTimeout();
}
/**
* 获取指定loginId的Session剩余有效时间 (单位: 秒)
* @param loginId 指定loginId
* @return token剩余有效时间
* 获取当前token的专属Session剩余有效时间 (单位: 秒)
* @return token剩余有效时间
*/
public static long getSessionTimeoutByLoginId(Object loginId) {
return stpLogic.getSessionTimeoutByLoginId(loginId);
public static long getTokenSessionTimeout() {
return stpLogic.getTokenSessionTimeout();
}
/**
@@ -292,15 +282,52 @@ public class StpUtil {
return stpLogic.getTokenActivityTimeout();
}
/**
* 获取指定token[临时过期]剩余有效时间 (单位: 秒)
* @param tokenValue 指定token
* @return token[临时过期]剩余有效时间
// =================== 角色验证操作 ===================
/**
* 指定账号id是否含有角色标识
* @param loginId 账号id
* @param role 角色标识
* @return 是否含有指定角色标识
*/
public static long getTokenActivityTimeoutByToken(String tokenValue) {
return stpLogic.getTokenActivityTimeoutByToken(tokenValue);
public static boolean hasRole(Object loginId, String role) {
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;
/**
* 自定义权限验证接口扩展
* 自定义权限验证接口扩展
*/
@Component // 打开此注解保证此类被springboot扫描即可完成sa-token的自定义权限验证扩展
public class StpInterfaceImpl implements StpInterface {
// 返回一个账号所拥有的权限码集合
/**
* 返回一个账号所拥有的权限码集合
*/
@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.add("101");
list.add("user-add");
@@ -26,4 +28,15 @@ public class StpInterfaceImpl implements StpInterface {
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.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
/**
* 全局异常处理
@@ -40,6 +41,9 @@ public class GlobalException {
if (e instanceof NotLoginException) { // 如果是未登录异常
NotLoginException ee = (NotLoginException) e;
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) { // 如果是权限异常
NotPermissionException ee = (NotPermissionException) e;
aj = AjaxJson.getNotJur("无此权限:" + ee.getCode());

View File

@@ -46,9 +46,31 @@ public class TestController {
return AjaxJson.getSuccess();
}
// 测试权限接口, 浏览器访问: http://localhost:8081/test/jur
@RequestMapping("jur")
public AjaxJson jur() {
// 测试角色接口, 浏览器访问: http://localhost:8081/test/testRole
@RequestMapping("testRole")
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("是否具有权限101" + StpUtil.hasPermission("101"));
@@ -69,6 +91,7 @@ public class TestController {
return AjaxJson.getSuccess();
}
// 测试会话session接口 浏览器访问: http://localhost:8081/test/session
@RequestMapping("session")
public AjaxJson session() {

View File

@@ -19,8 +19,12 @@
- 类似API还有
- `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`是否含有指定权限

View File

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