重构注解鉴权底层,可以方便的自定义注解了

This commit is contained in:
click33 2024-08-03 18:50:11 +08:00
parent a3746878de
commit 834e1d5b34
26 changed files with 210 additions and 560 deletions

View File

@ -39,13 +39,6 @@ public @interface SaCheckOr {
*/
SaCheckLogin[] login() default {};
/**
* 设定 @SaCheckPermission参考 {@link SaCheckPermission}
*
* @return /
*/
SaCheckPermission[] permission() default {};
/**
* 设定 @SaCheckRole参考 {@link SaCheckRole}
*
@ -53,6 +46,13 @@ public @interface SaCheckOr {
*/
SaCheckRole[] role() default {};
/**
* 设定 @SaCheckPermission参考 {@link SaCheckPermission}
*
* @return /
*/
SaCheckPermission[] permission() default {};
/**
* 设定 @SaCheckSafe参考 {@link SaCheckSafe}
*

View File

@ -1,99 +0,0 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.basic;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.error.SaErrorCode;
import cn.dev33.satoken.exception.NotBasicAuthException;
import cn.dev33.satoken.secure.SaBase64Util;
import cn.dev33.satoken.util.SaFoxUtil;
/**
* <h2> 已更换至包cn.dev33.satoken.httpauth.basic </h2>
* <h2> 已更换名称SaHttpBasicTemplate </h2>
*
* Sa-Token Http Basic 认证模块
*
* @author click33
* @since 1.26.0
*/
@Deprecated
public class SaBasicTemplate {
/**
* 默认的 Realm 领域名称
*/
public static final String DEFAULT_REALM = "Sa-Token";
/**
* 在校验失败时设置响应头并抛出异常
* @param realm 领域
*/
public void throwNotBasicAuthException(String realm) {
SaHolder.getResponse().setStatus(401).setHeader("WWW-Authenticate", "Basic Realm=" + realm);
throw new NotBasicAuthException().setCode(SaErrorCode.CODE_10311);
}
/**
* 获取浏览器提交的 Basic 参数 裁剪掉前缀并解码
* @return
*/
public String getAuthorizationValue() {
// 获取前端提交的请求头 Authorization 参数
String authorization = SaHolder.getRequest().getHeader("Authorization");
// 如果不是以 Basic 作为前缀则视为无效
if(authorization == null || ! authorization.startsWith("Basic ")) {
return null;
}
// 裁剪前缀并解码
return SaBase64Util.decode(authorization.substring(6));
}
/**
* 对当前会话进行 Basic 校验使用全局配置的账号密码校验不通过则抛出异常
*/
public void check() {
check(DEFAULT_REALM, SaManager.getConfig().getBasic());
}
/**
* 对当前会话进行 Basic 校验手动设置账号密码校验不通过则抛出异常
* @param account 账号格式为 user:password
*/
public void check(String account) {
check(DEFAULT_REALM, account);
}
/**
* 对当前会话进行 Basic 校验手动设置 Realm 账号密码校验不通过则抛出异常
* @param realm 领域
* @param account 账号格式为 user:password
*/
public void check(String realm, String account) {
if(SaFoxUtil.isEmpty(account)) {
account = SaManager.getConfig().getBasic();
}
String authorization = getAuthorizationValue();
if(SaFoxUtil.isEmpty(authorization) || ! authorization.equals(account)) {
throwNotBasicAuthException(realm);
}
}
}

View File

@ -1,70 +0,0 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.basic;
/**
* <h2> 已更换至包cn.dev33.satoken.httpauth.basic </h2>
* <h2> 已更换名称SaHttpBasicUtil </h2>
*
* Sa-Token Http Basic 认证模块Util 工具类
*
* @author click33
* @since 1.26.0
*/
@Deprecated
public class SaBasicUtil {
private SaBasicUtil() {
}
/**
* 底层使用的 SaBasicTemplate 对象
*/
public static SaBasicTemplate saBasicTemplate = new SaBasicTemplate();
/**
* 获取浏览器提交的 Basic 参数 裁剪掉前缀并解码
* @return
*/
public static String getAuthorizationValue() {
return saBasicTemplate.getAuthorizationValue();
}
/**
* 对当前会话进行 Basic 校验使用全局配置的账号密码校验不通过则抛出异常
*/
public static void check() {
saBasicTemplate.check();
}
/**
* 对当前会话进行 Basic 校验手动设置账号密码校验不通过则抛出异常
* @param account 账号格式为 user:password
*/
public static void check(String account) {
saBasicTemplate.check(account);
}
/**
* 对当前会话进行 Basic 校验手动设置 Realm 账号密码校验不通过则抛出异常
* @param realm 领域
* @param account 账号格式为 user:password
*/
public static void check(String realm, String account) {
saBasicTemplate.check(realm, account);
}
}

View File

@ -21,7 +21,7 @@ package cn.dev33.satoken.exception;
* @author click33
* @since 1.26.0
*/
public class NotBasicAuthException extends SaTokenException {
public class NotHttpBasicAuthException extends SaTokenException {
/**
* 序列化版本号
@ -34,7 +34,7 @@ public class NotBasicAuthException extends SaTokenException {
/**
* 一个异常代表会话未通过 Http Basic 认证
*/
public NotBasicAuthException() {
public NotHttpBasicAuthException() {
super(BE_MESSAGE);
}

View File

@ -29,6 +29,6 @@ import java.util.function.BiFunction;
* @since 1.35.0
*/
@FunctionalInterface
public interface SaGetAnnotationFunction extends BiFunction<AnnotatedElement, Class<? extends Annotation> , Annotation> {
public interface SaGetAnnotationFunction extends BiFunction<AnnotatedElement, Class<? extends Annotation>, Annotation> {
}

View File

@ -18,7 +18,7 @@ package cn.dev33.satoken.httpauth.basic;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.error.SaErrorCode;
import cn.dev33.satoken.exception.NotBasicAuthException;
import cn.dev33.satoken.exception.NotHttpBasicAuthException;
import cn.dev33.satoken.secure.SaBase64Util;
import cn.dev33.satoken.util.SaFoxUtil;
@ -41,7 +41,7 @@ public class SaHttpBasicTemplate {
*/
public void throwNotBasicAuthException(String realm) {
SaHolder.getResponse().setStatus(401).setHeader("WWW-Authenticate", "Basic Realm=" + realm);
throw new NotBasicAuthException().setCode(SaErrorCode.CODE_10311);
throw new NotHttpBasicAuthException().setCode(SaErrorCode.CODE_10311);
}
/**

View File

@ -263,11 +263,16 @@ public class SaHttpDigestTemplate {
check(arr[0], arr[1]);
}
// ----------------- 过期方法 -----------------
/**
* 根据注解 ( @SaCheckHttpDigest ) 鉴权
*
* @param at 注解对象
*/
@Deprecated
public void checkByAnnotation(SaCheckHttpDigest at) {
// 如果配置了 value则以 value 优先

View File

@ -90,11 +90,16 @@ public class SaHttpDigestUtil {
saHttpDigestTemplate.check();
}
// ----------------- 过期方法 -----------------
/**
* 根据注解 ( @SaCheckHttpDigest ) 鉴权
*
* @param at 注解对象
*/
@Deprecated
public static void checkByAnnotation(SaCheckHttpDigest at) {
saHttpDigestTemplate.checkByAnnotation(at);
}

View File

@ -18,6 +18,7 @@ package cn.dev33.satoken.listener;
import java.util.ArrayList;
import java.util.List;
import cn.dev33.satoken.annotation.handler.SaAnnotationAbstractHandler;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.error.SaErrorCode;
import cn.dev33.satoken.exception.SaTokenException;
@ -287,6 +288,16 @@ public class SaTokenEventCenter {
}
}
/**
* 事件发布有新的注解处理器载入到框架中
* @param handler 注解处理器
*/
public static void doRegisterAnnotationHandler(SaAnnotationAbstractHandler<?> handler) {
for (SaTokenListener listener : listenerList) {
listener.doRegisterAnnotationHandler(handler);
}
}
/**
* 事件发布有新的 StpLogic 载入到框架中
* @param stpLogic /

View File

@ -15,6 +15,7 @@
*/
package cn.dev33.satoken.listener;
import cn.dev33.satoken.annotation.handler.SaAnnotationAbstractHandler;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpLogic;
@ -125,6 +126,12 @@ public interface SaTokenListener {
*/
default void doRegisterComponent(String compName, Object compObj) {}
/**
* 注册了自定义注解处理器
* @param handler 注解处理器
*/
default void doRegisterAnnotationHandler(SaAnnotationAbstractHandler<?> handler) {}
/**
* StpLogic 对象替换
* @param stpLogic /

View File

@ -15,13 +15,14 @@
*/
package cn.dev33.satoken.listener;
import static cn.dev33.satoken.SaManager.log;
import cn.dev33.satoken.annotation.handler.SaAnnotationAbstractHandler;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.util.SaFoxUtil;
import static cn.dev33.satoken.SaManager.log;
/**
* Sa-Token 侦听器的一个实现Log 打印
*
@ -130,6 +131,17 @@ public class SaTokenListenerForLog implements SaTokenListener {
log.info("全局组件 {} 载入成功: {}", compName, canonicalName);
}
/**
* 注册了自定义注解处理器
* @param handler 注解处理器
*/
@Override
public void doRegisterAnnotationHandler(SaAnnotationAbstractHandler<?> handler) {
if(handler != null) {
log.info("注解扩展 @{} (处理器: {})", handler.getHandlerAnnotationClass().getSimpleName(), handler.getClass().getCanonicalName());
}
}
/**
* StpLogic 对象替换
* @param stpLogic /

View File

@ -2233,79 +2233,6 @@ public class StpLogic {
public List<String> searchTokenSessionId(String keyword, int start, int size, boolean sortType) {
return getSaTokenDao().searchData(splicingKeyTokenSession(""), keyword, start, size, sortType);
}
// ------------------- 注解鉴权 -------------------
/**
* 根据注解 ( @SaCheckLogin ) 鉴权
*
* @param at 注解对象
*/
public void checkByAnnotation(SaCheckLogin at) {
this.checkLogin();
}
/**
* 根据注解 ( @SaCheckRole ) 鉴权
*
* @param at 注解对象
*/
public void checkByAnnotation(SaCheckRole at) {
String[] roleArray = at.value();
if(at.mode() == SaMode.AND) {
this.checkRoleAnd(roleArray);
} else {
this.checkRoleOr(roleArray);
}
}
/**
* 根据注解 ( @SaCheckPermission ) 鉴权
*
* @param at 注解对象
*/
public void checkByAnnotation(SaCheckPermission at) {
String[] permissionArray = at.value();
try {
if(at.mode() == SaMode.AND) {
this.checkPermissionAnd(permissionArray);
} else {
this.checkPermissionOr(permissionArray);
}
} catch (NotPermissionException e) {
// 权限认证校验未通过再开始角色认证校验
for (String role : at.orRole()) {
String[] rArr = SaFoxUtil.convertStringToArray(role);
// 某一项 role 认证通过则可以提前退出了代表通过
if (hasRoleAnd(rArr)) {
return;
}
}
throw e;
}
}
/**
* 根据注解 ( @SaCheckSafe ) 鉴权
*
* @param at 注解对象
*/
public void checkByAnnotation(SaCheckSafe at) {
this.checkSafe(at.value());
}
/**
* 根据注解 ( @SaCheckDisable ) 鉴权
*
* @param at 注解对象
*/
public void checkByAnnotation(SaCheckDisable at) {
Object loginId = getLoginId();
for (String service : at.value()) {
this.checkDisableLevel(loginId, service, at.level());
}
}
// ------------------- 账号封禁 -------------------
@ -2937,4 +2864,84 @@ public class StpLogic {
return false;
}
// ------------------- 过期方法 -------------------
/**
* 根据注解 ( @SaCheckLogin ) 鉴权
*
* @param at 注解对象
*/
@Deprecated
public void checkByAnnotation(SaCheckLogin at) {
this.checkLogin();
}
/**
* 根据注解 ( @SaCheckRole ) 鉴权
*
* @param at 注解对象
*/
@Deprecated
public void checkByAnnotation(SaCheckRole at) {
String[] roleArray = at.value();
if(at.mode() == SaMode.AND) {
this.checkRoleAnd(roleArray);
} else {
this.checkRoleOr(roleArray);
}
}
/**
* 根据注解 ( @SaCheckPermission ) 鉴权
*
* @param at 注解对象
*/
@Deprecated
public void checkByAnnotation(SaCheckPermission at) {
String[] permissionArray = at.value();
try {
if(at.mode() == SaMode.AND) {
this.checkPermissionAnd(permissionArray);
} else {
this.checkPermissionOr(permissionArray);
}
} catch (NotPermissionException e) {
// 权限认证校验未通过再开始角色认证校验
for (String role : at.orRole()) {
String[] rArr = SaFoxUtil.convertStringToArray(role);
// 某一项 role 认证通过则可以提前退出了代表通过
if (hasRoleAnd(rArr)) {
return;
}
}
throw e;
}
}
/**
* 根据注解 ( @SaCheckSafe ) 鉴权
*
* @param at 注解对象
*/
@Deprecated
public void checkByAnnotation(SaCheckSafe at) {
this.checkSafe(at.value());
}
/**
* 根据注解 ( @SaCheckDisable ) 鉴权
*
* @param at 注解对象
*/
@Deprecated
public void checkByAnnotation(SaCheckDisable at) {
Object loginId = getLoginId();
for (String service : at.value()) {
this.checkDisableLevel(loginId, service, at.level());
}
}
}

View File

@ -16,19 +16,14 @@
package cn.dev33.satoken.strategy;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.annotation.*;
import cn.dev33.satoken.exception.RequestPathInvalidException;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.fun.strategy.*;
import cn.dev33.satoken.httpauth.basic.SaHttpBasicUtil;
import cn.dev33.satoken.httpauth.digest.SaHttpDigestUtil;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.dev33.satoken.util.SaTokenConsts;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
@ -133,185 +128,6 @@ public final class SaStrategy {
return false;
};
/**
* 对一个 [Method] 对象进行注解校验 注解鉴权内部实现
*/
public SaCheckMethodAnnotationFunction checkMethodAnnotation = (method) -> {
// 先校验 Method 所属 Class 上的注解
instance.checkElementAnnotation.accept(method.getDeclaringClass());
// 再校验 Method 上的注解
instance.checkElementAnnotation.accept(method);
};
/**
* 对一个 [元素] 对象进行注解校验 注解鉴权内部实现
*/
public SaCheckElementAnnotationFunction checkElementAnnotation = (element) -> {
// 校验 @SaCheckLogin 注解
SaCheckLogin checkLogin = (SaCheckLogin) SaStrategy.instance.getAnnotation.apply(element, SaCheckLogin.class);
if(checkLogin != null) {
SaManager.getStpLogic(checkLogin.type(), false).checkByAnnotation(checkLogin);
}
// 校验 @SaCheckRole 注解
SaCheckRole checkRole = (SaCheckRole) SaStrategy.instance.getAnnotation.apply(element, SaCheckRole.class);
if(checkRole != null) {
SaManager.getStpLogic(checkRole.type(), false).checkByAnnotation(checkRole);
}
// 校验 @SaCheckPermission 注解
SaCheckPermission checkPermission = (SaCheckPermission) SaStrategy.instance.getAnnotation.apply(element, SaCheckPermission.class);
if(checkPermission != null) {
SaManager.getStpLogic(checkPermission.type(), false).checkByAnnotation(checkPermission);
}
// 校验 @SaCheckSafe 注解
SaCheckSafe checkSafe = (SaCheckSafe) SaStrategy.instance.getAnnotation.apply(element, SaCheckSafe.class);
if(checkSafe != null) {
SaManager.getStpLogic(checkSafe.type(), false).checkByAnnotation(checkSafe);
}
// 校验 @SaCheckDisable 注解
SaCheckDisable checkDisable = (SaCheckDisable) SaStrategy.instance.getAnnotation.apply(element, SaCheckDisable.class);
if(checkDisable != null) {
SaManager.getStpLogic(checkDisable.type(), false).checkByAnnotation(checkDisable);
}
// 校验 @SaCheckHttpBasic 注解
SaCheckHttpBasic checkHttpBasic = (SaCheckHttpBasic) SaStrategy.instance.getAnnotation.apply(element, SaCheckHttpBasic.class);
if(checkHttpBasic != null) {
SaHttpBasicUtil.check(checkHttpBasic.realm(), checkHttpBasic.account());
}
// 校验 @SaCheckHttpDigest 注解
SaCheckHttpDigest checkHttpDigest = (SaCheckHttpDigest) SaStrategy.instance.getAnnotation.apply(element, SaCheckHttpDigest.class);
if(checkHttpDigest != null) {
SaHttpDigestUtil.checkByAnnotation(checkHttpDigest);
}
// 校验 @SaCheckOr 注解
SaCheckOr checkOr = (SaCheckOr) SaStrategy.instance.getAnnotation.apply(element, SaCheckOr.class);
if(checkOr != null) {
SaStrategy.instance.checkOrAnnotation.accept(checkOr);
}
};
/**
* 对一个 @SaCheckOr 进行注解校验
*/
public SaCheckOrAnnotationFunction checkOrAnnotation = (at) -> {
// 记录校验过程中所有的异常
List<SaTokenException> errorList = new ArrayList<>();
// 逐个开始校验 >>>
// 1校验注解@SaCheckLogin
SaCheckLogin[] checkLoginArray = at.login();
for (SaCheckLogin item : checkLoginArray) {
try {
SaManager.getStpLogic(item.type(), false).checkByAnnotation(item);
return;
} catch (SaTokenException e) {
errorList.add(e);
}
}
// 2校验注解@SaCheckRole
SaCheckRole[] checkRoleArray = at.role();
for (SaCheckRole item : checkRoleArray) {
try {
SaManager.getStpLogic(item.type(), false).checkByAnnotation(item);
return;
} catch (SaTokenException e) {
errorList.add(e);
}
}
// 3校验注解@SaCheckPermission
SaCheckPermission[] checkPermissionArray = at.permission();
for (SaCheckPermission item : checkPermissionArray) {
try {
SaManager.getStpLogic(item.type(), false).checkByAnnotation(item);
return;
} catch (SaTokenException e) {
errorList.add(e);
}
}
// 4校验注解@SaCheckSafe
SaCheckSafe[] checkSafeArray = at.safe();
for (SaCheckSafe item : checkSafeArray) {
try {
SaManager.getStpLogic(item.type(), false).checkByAnnotation(item);
return;
} catch (SaTokenException e) {
errorList.add(e);
}
}
// 5校验注解@SaCheckDisable
SaCheckDisable[] checkDisableArray = at.disable();
for (SaCheckDisable item : checkDisableArray) {
try {
SaManager.getStpLogic(item.type(), false).checkByAnnotation(item);
return;
} catch (SaTokenException e) {
errorList.add(e);
}
}
// 6校验注解@SaCheckBasic
SaCheckHttpBasic[] checkHttpBasicArray = at.httpBasic();
for (SaCheckHttpBasic item : checkHttpBasicArray) {
try {
SaHttpBasicUtil.check(item.realm(), item.account());
return;
} catch (SaTokenException e) {
errorList.add(e);
}
}
// 7校验注解@SaCheckDigest
SaCheckHttpDigest[] checkHttpDigestArray = at.httpDigest();
for (SaCheckHttpDigest item : checkHttpDigestArray) {
try {
SaHttpDigestUtil.checkByAnnotation(item);
return;
} catch (SaTokenException e) {
errorList.add(e);
}
}
// 如果执行到这里有两种可能
// 可能 1. SaCheckOr 注解上不包含任何注解校验此时 errorList 里面一个异常都没有我们直接跳过即可
// 可能 2. 所有注解校验都通过不了此时 errorList 里面会有多个异常我们随便抛出一个即可
if(errorList.size() == 0) {
// return;
} else {
throw errorList.get(0);
}
};
/**
* 从元素上获取注解
*/
public SaGetAnnotationFunction getAnnotation = (element, annotationClass)->{
// 默认使用jdk的注解处理器
return element.getAnnotation(annotationClass);
};
/**
* 判断一个 Method 或其所属 Class 是否包含指定注解
*/
public SaIsAnnotationPresentFunction isAnnotationPresent = (method, annotationClass) -> {
return instance.getAnnotation.apply(method, annotationClass) != null ||
instance.getAnnotation.apply(method.getDeclaringClass(), annotationClass) != null;
};
/**
* 生成唯一式 token 的算法
*/
@ -427,62 +243,6 @@ public final class SaStrategy {
return this;
}
/**
* 对一个 [Method] 对象进行注解校验 注解鉴权内部实现
*
* @param checkMethodAnnotation /
* @return /
*/
public SaStrategy setCheckMethodAnnotation(SaCheckMethodAnnotationFunction checkMethodAnnotation) {
this.checkMethodAnnotation = checkMethodAnnotation;
return this;
}
/**
* 对一个 [元素] 对象进行注解校验 注解鉴权内部实现
*
* @param checkElementAnnotation /
* @return /
*/
public SaStrategy setCheckElementAnnotation(SaCheckElementAnnotationFunction checkElementAnnotation) {
this.checkElementAnnotation = checkElementAnnotation;
return this;
}
/**
* 对一个 @SaCheckOr 进行注解校验
* <p> 参数 [SaCheckOr 注解的实例]
*
* @param checkOrAnnotation /
* @return /
*/
public SaStrategy setCheckOrAnnotation(SaCheckOrAnnotationFunction checkOrAnnotation) {
this.checkOrAnnotation = checkOrAnnotation;
return this;
}
/**
* 从元素上获取注解
*
* @param getAnnotation /
* @return /
*/
public SaStrategy setGetAnnotation(SaGetAnnotationFunction getAnnotation) {
this.getAnnotation = getAnnotation;
return this;
}
/**
* 判断一个 Method 或其所属 Class 是否包含指定注解
*
* @param isAnnotationPresent /
* @return /
*/
public SaStrategy setIsAnnotationPresent(SaIsAnnotationPresentFunction isAnnotationPresent) {
this.isAnnotationPresent = isAnnotationPresent;
return this;
}
/**
* 生成唯一式 token 的算法
*

View File

@ -1,10 +1,9 @@
package com.pj;
import cn.dev33.satoken.SaManager;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import cn.dev33.satoken.SaManager;
/**
* Sa-Token 示例
* @author click33

View File

@ -1,10 +1,9 @@
package com.pj.cases.test;
import cn.dev33.satoken.util.SaResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.dev33.satoken.util.SaResult;
/**
* 测试专用 Controller
* @author click33
@ -17,14 +16,14 @@ public class TestController {
// 测试 浏览器访问 http://localhost:8081/test/test
@RequestMapping("test")
public SaResult test() {
System.out.println("------------进来了");
System.out.println("------------进来了");
return SaResult.ok();
}
// 测试 浏览器访问 http://localhost:8081/test/test2
@RequestMapping("test2")
public SaResult test2() {
System.out.println("------------进来了");
System.out.println("------------进来了");
return SaResult.ok();
}

View File

@ -4,7 +4,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import cn.dev33.satoken.exception.DisableServiceException;
import cn.dev33.satoken.exception.NotBasicAuthException;
import cn.dev33.satoken.exception.NotHttpBasicAuthException;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
@ -57,8 +57,8 @@ public class GlobalException {
}
// 拦截Http Basic 校验失败异常
@ExceptionHandler(NotBasicAuthException.class)
public SaResult handlerException(NotBasicAuthException e) {
@ExceptionHandler(NotHttpBasicAuthException.class)
public SaResult handlerException(NotHttpBasicAuthException e) {
e.printStackTrace();
return SaResult.error(e.getMessage());
}

View File

@ -5,6 +5,7 @@ import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.strategy.SaAnnotationStrategy;
import cn.dev33.satoken.strategy.SaStrategy;
import cn.dev33.satoken.util.SaResult;
import org.springframework.context.annotation.Bean;
@ -117,9 +118,9 @@ public class SaTokenConfigure implements WebMvcConfigurer {
@PostConstruct
public void rewriteSaStrategy() {
// 重写Sa-Token的注解处理器增加注解合并功能
SaStrategy.instance.getAnnotation = (element, annotationClass) -> {
return AnnotatedElementUtils.getMergedAnnotation(element, annotationClass);
};
// SaAnnotationStrategy.instance.getAnnotation = (element, annotationClass) -> {
// return AnnotatedElementUtils.getMergedAnnotation(element, annotationClass);
// };
}
}

View File

@ -1,20 +1,16 @@
package com.pj.satoken.at;
package com.pj.satoken.custom_annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import cn.dev33.satoken.annotation.SaCheckLogin;
import com.pj.satoken.StpUserUtil;
/**
* 登录认证(User版)只有登录之后才能进入该方法
* <p> 可标注在函数类上效果等同于标注在此类的所有方法上
* @author click33
* <p> 可标注在函数类上效果等同于标注在此类的所有方法上
*
* @author click33
*/
@SaCheckLogin(type = StpUserUtil.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE})
public @interface SaUserCheckLogin {

View File

@ -1,23 +1,19 @@
package com.pj.satoken.at;
package com.pj.satoken.custom_annotation;
import cn.dev33.satoken.annotation.SaMode;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.pj.satoken.StpUserUtil;
import org.springframework.core.annotation.AliasFor;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
/**
* 权限认证(User版)必须具有指定权限才能进入该方法
* <p> 可标注在函数类上效果等同于标注在此类的所有方法上
* <p> 可标注在函数类上效果等同于标注在此类的所有方法上
*
* @author click33
*
*/
@SaCheckPermission(type = StpUserUtil.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE})
public @interface SaUserCheckPermission {
@ -26,14 +22,29 @@ public @interface SaUserCheckPermission {
* 需要校验的权限码
* @return 需要校验的权限码
*/
@AliasFor(annotation = SaCheckPermission.class)
String [] value() default {};
/**
* 验证模式AND | OR默认AND
* @return 验证模式
*/
@AliasFor(annotation = SaCheckPermission.class)
SaMode mode() default SaMode.AND;
/**
* 在权限校验不通过时的次要选择两者只要其一校验成功即可通过校验
*
* <p>
* 例1@SaCheckPermission(value="user-add", orRole="admin")
* 代表本次请求只要具有 user-add权限 admin角色 其一即可通过校验
* </p>
*
* <p>
* 例2 orRole = {"admin", "manager", "staff"}具有三个角色其一即可 <br>
* 例3 orRole = {"admin, manager, staff"}必须三个角色同时具备
* </p>
*
* @return /
*/
String[] orRole() default {};
}

View File

@ -1,23 +1,18 @@
package com.pj.satoken.at;
package com.pj.satoken.custom_annotation;
import cn.dev33.satoken.annotation.SaMode;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.pj.satoken.StpUserUtil;
import org.springframework.core.annotation.AliasFor;
import cn.dev33.satoken.annotation.SaCheckRole;
import cn.dev33.satoken.annotation.SaMode;
/**
* 角色认证(User版)必须具有指定角色标识才能进入该方法
* <p> 可标注在函数类上效果等同于标注在此类的所有方法上
* @author click33
*
*/
@SaCheckRole(type = StpUserUtil.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE})
public @interface SaUserCheckRole {
@ -26,14 +21,12 @@ public @interface SaUserCheckRole {
* 需要校验的角色标识
* @return 需要校验的角色标识
*/
@AliasFor(annotation = SaCheckRole.class)
String [] value() default {};
/**
* 验证模式AND | OR默认AND
* @return 验证模式
*/
@AliasFor(annotation = SaCheckRole.class)
SaMode mode() default SaMode.AND;
}

View File

@ -108,6 +108,7 @@
- [TokenInfo参数详解](/fun/token-info)
- [异常细分状态码](/fun/exception-code)
- [数据结构](/fun/data-structure)
- [自定义注解](/fun/custom-annotations)
- [参考:把权限放在缓存里](/fun/jur-cache)
- [参考:把路由拦截鉴权动态化](/fun/dynamic-router-check)
- [解决反向代理 uri 丢失的问题](/fun/curr-domain)

View File

@ -184,8 +184,11 @@ public String info() {
}
```
注:其它注解 `@SaCheckRole("xxx")`、`@SaCheckPermission("xxx")`同理,
完整示例参考:[码云:自定义注解](https://gitee.com/dromara/sa-token/tree/dev/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/at)。
注:其它注解 `@SaCheckRole("xxx")`、`@SaCheckPermission("xxx")`同理, 完整示例参考 Gitee 代码:
[注解合并](https://gitee.com/dromara/sa-token/tree/master/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/merge_annotation)。
> [!TIP| label:自定义注解方案]
> 除了注解合并方案,这里还有一份自定义注解方案,参考:[自定义注解](/fun/custom-annotations)

View File

@ -213,10 +213,16 @@ public SaResult test() {
### 7、在业务逻辑层使用注解鉴权
疑问:我能否将注解写在其它架构层呢,比如业务逻辑层?
### 7、扩展阅读
使用拦截器模式,只能在`Controller层`进行注解鉴权,如需在任意层级使用注解鉴权,请参考:[AOP注解鉴权](/plugin/aop-at)
- 在业务逻辑层使用鉴权注解:[AOP注解鉴权](/plugin/aop-at)
- 制作自定义鉴权注解注入到框架:[自定义注解](/fun/custom-annotations)
<!-- 疑问:我能否将注解写在其它架构层呢,比如业务逻辑层?
使用拦截器模式,只能在`Controller层`进行注解鉴权,如需在任意层级使用注解鉴权,请参考:[AOP注解鉴权](/plugin/aop-at) -->
---

View File

@ -16,6 +16,7 @@
package cn.dev33.satoken.spring;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.annotation.handler.SaAnnotationAbstractHandler;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.context.SaTokenContext;
import cn.dev33.satoken.context.second.SaTokenSecondContextCreator;
@ -34,6 +35,7 @@ import cn.dev33.satoken.spring.pathmatch.SaPathMatcherHolder;
import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.strategy.SaAnnotationStrategy;
import cn.dev33.satoken.temp.SaTempInterface;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@ -118,6 +120,18 @@ public class SaBeanInject {
SaTokenEventCenter.registerListenerList(listenerList);
}
/**
* 注入自定义注解处理器
*
* @param handlerList 自定义注解处理器集合
*/
@Autowired(required = false)
public void setSaAnnotationHandler(List<SaAnnotationAbstractHandler<?>> handlerList) {
for (SaAnnotationAbstractHandler<?> handler : handlerList) {
SaAnnotationStrategy.instance.registerAnnotationHandler(handler);
}
}
/**
* 注入临时令牌验证模块 Bean
*
@ -149,12 +163,12 @@ public class SaBeanInject {
}
/**
* 注入 Sa-Token Digest Basic 认证模块
* 注入 Sa-Token Http Digest 认证模块
*
* @param saHttpDigestTemplate saHttpDigestTemplate 对象
*/
@Autowired(required = false)
public void setSaHttpBasicTemplate(SaHttpDigestTemplate saHttpDigestTemplate) {
public void setSaHttpDigestTemplate(SaHttpDigestTemplate saHttpDigestTemplate) {
SaHttpDigestUtil.saHttpDigestTemplate = saHttpDigestTemplate;
}

View File

@ -15,11 +15,10 @@
*/
package cn.dev33.satoken.interceptor;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.exception.BackResultException;
import cn.dev33.satoken.exception.StopMatchException;
import cn.dev33.satoken.fun.SaParamFunction;
import cn.dev33.satoken.strategy.SaStrategy;
import cn.dev33.satoken.strategy.SaAnnotationStrategy;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
@ -95,18 +94,8 @@ public class SaInterceptor implements HandlerInterceptor {
// 这里必须确保 handler HandlerMethod 类型时才能进行注解鉴权
if(isAnnotation && handler instanceof HandlerMethod) {
// 获取此请求对应的 Method 处理函数
Method method = ((HandlerMethod) handler).getMethod();
// 如果此 Method 或其所属 Class 标注了 @SaIgnore则忽略掉鉴权
if(SaStrategy.instance.isAnnotationPresent.apply(method, SaIgnore.class)) {
// 注意这里直接就退出整个鉴权了最底部的 auth.run() 路由拦截鉴权也被跳出了
return true;
}
// 执行注解鉴权
SaStrategy.instance.checkMethodAnnotation.accept(method);
SaAnnotationStrategy.instance.checkMethodAnnotation.accept(method);
}
// Auth 路由拦截鉴权校验

View File

@ -19,7 +19,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import cn.dev33.satoken.exception.DisableServiceException;
import cn.dev33.satoken.exception.NotBasicAuthException;
import cn.dev33.satoken.exception.NotHttpBasicAuthException;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
@ -66,8 +66,8 @@ public class HandlerException {
}
// Http Basic 校验失败code=903
@ExceptionHandler(NotBasicAuthException.class)
public SaResult handlerNotBasicAuthException(NotBasicAuthException e) {
@ExceptionHandler(NotHttpBasicAuthException.class)
public SaResult handlerNotBasicAuthException(NotHttpBasicAuthException e) {
return SaResult.error().setCode(903);
}