mirror of
https://gitee.com/dromara/sa-token.git
synced 2025-04-05 17:37:53 +08:00
使 sa-token-spring-aop 插件支持自定义注解鉴权
This commit is contained in:
parent
c38eb0c68c
commit
aa2e9a5c50
@ -51,7 +51,7 @@
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- @ConfigurationProperties -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
@ -51,25 +51,25 @@ public class SecureController {
|
||||
}
|
||||
|
||||
// RSA加密 ---- http://localhost:8081/secure/rsa
|
||||
@RequestMapping("rsa")
|
||||
public SaResult rsa() {
|
||||
// 定义私钥和公钥
|
||||
String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAO+wmt01pwm9lHMdq7A8gkEigk0XKMfjv+4IjAFhWCSiTeP7dtlnceFJbkWxvbc7Qo3fCOpwmfcskwUc3VSgyiJkNJDs9ivPbvlt8IU2bZ+PBDxYxSCJFrgouVOpAr8ar/b6gNuYTi1vt3FkGtSjACFb002/68RKUTye8/tdcVilAgMBAAECgYA1COmrSqTUJeuD8Su9ChZ0HROhxR8T45PjMmbwIz7ilDsR1+E7R4VOKPZKW4Kz2VvnklMhtJqMs4MwXWunvxAaUFzQTTg2Fu/WU8Y9ha14OaWZABfChMZlpkmpJW9arKmI22ZuxCEsFGxghTiJQ3tK8npj5IZq5vk+6mFHQ6aJAQJBAPghz91Dpuj+0bOUfOUmzi22obWCBncAD/0CqCLnJlpfOoa9bOcXSusGuSPuKy5KiGyblHMgKI6bq7gcM2DWrGUCQQD3SkOcmia2s/6i7DUEzMKaB0bkkX4Ela/xrfV+A3GzTPv9bIBamu0VIHznuiZbeNeyw7sVo4/GTItq/zn2QJdBAkEA8xHsVoyXTVeShaDIWJKTFyT5dJ1TR++/udKIcuiNIap34tZdgGPI+EM1yoTduBM7YWlnGwA9urW0mj7F9e9WIQJAFjxqSfmeg40512KP/ed/lCQVXtYqU7U2BfBTg8pBfhLtEcOg4wTNTroGITwe2NjL5HovJ2n2sqkNXEio6Ji0QQJAFLW1Kt80qypMqot+mHhS+0KfdOpaKeMWMSR4Ij5VfE63WzETEeWAMQESxzhavN1WOTb3/p6icgcVbgPQBaWhGg==";
|
||||
String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDvsJrdNacJvZRzHauwPIJBIoJNFyjH47/uCIwBYVgkok3j+3bZZ3HhSW5Fsb23O0KN3wjqcJn3LJMFHN1UoMoiZDSQ7PYrz275bfCFNm2fjwQ8WMUgiRa4KLlTqQK/Gq/2+oDbmE4tb7dxZBrUowAhW9NNv+vESlE8nvP7XXFYpQIDAQAB";
|
||||
|
||||
// 文本
|
||||
String text = "Sa-Token 一个轻量级java权限认证框架";
|
||||
|
||||
// 使用公钥加密
|
||||
String ciphertext = SaSecureUtil.rsaEncryptByPublic(publicKey, text);
|
||||
System.out.println("公钥加密后:" + ciphertext);
|
||||
|
||||
// 使用私钥解密
|
||||
String text2 = SaSecureUtil.rsaDecryptByPrivate(privateKey, ciphertext);
|
||||
System.out.println("私钥解密后:" + text2);
|
||||
|
||||
return SaResult.ok();
|
||||
}
|
||||
// @RequestMapping("rsa")
|
||||
// public SaResult rsa() {
|
||||
// // 定义私钥和公钥
|
||||
// String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAO+wmt01pwm9lHMdq7A8gkEigk0XKMfjv+4IjAFhWCSiTeP7dtlnceFJbkWxvbc7Qo3fCOpwmfcskwUc3VSgyiJkNJDs9ivPbvlt8IU2bZ+PBDxYxSCJFrgouVOpAr8ar/b6gNuYTi1vt3FkGtSjACFb002/68RKUTye8/tdcVilAgMBAAECgYA1COmrSqTUJeuD8Su9ChZ0HROhxR8T45PjMmbwIz7ilDsR1+E7R4VOKPZKW4Kz2VvnklMhtJqMs4MwXWunvxAaUFzQTTg2Fu/WU8Y9ha14OaWZABfChMZlpkmpJW9arKmI22ZuxCEsFGxghTiJQ3tK8npj5IZq5vk+6mFHQ6aJAQJBAPghz91Dpuj+0bOUfOUmzi22obWCBncAD/0CqCLnJlpfOoa9bOcXSusGuSPuKy5KiGyblHMgKI6bq7gcM2DWrGUCQQD3SkOcmia2s/6i7DUEzMKaB0bkkX4Ela/xrfV+A3GzTPv9bIBamu0VIHznuiZbeNeyw7sVo4/GTItq/zn2QJdBAkEA8xHsVoyXTVeShaDIWJKTFyT5dJ1TR++/udKIcuiNIap34tZdgGPI+EM1yoTduBM7YWlnGwA9urW0mj7F9e9WIQJAFjxqSfmeg40512KP/ed/lCQVXtYqU7U2BfBTg8pBfhLtEcOg4wTNTroGITwe2NjL5HovJ2n2sqkNXEio6Ji0QQJAFLW1Kt80qypMqot+mHhS+0KfdOpaKeMWMSR4Ij5VfE63WzETEeWAMQESxzhavN1WOTb3/p6icgcVbgPQBaWhGg==";
|
||||
// String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDvsJrdNacJvZRzHauwPIJBIoJNFyjH47/uCIwBYVgkok3j+3bZZ3HhSW5Fsb23O0KN3wjqcJn3LJMFHN1UoMoiZDSQ7PYrz275bfCFNm2fjwQ8WMUgiRa4KLlTqQK/Gq/2+oDbmE4tb7dxZBrUowAhW9NNv+vESlE8nvP7XXFYpQIDAQAB";
|
||||
//
|
||||
// // 文本
|
||||
// String text = "Sa-Token 一个轻量级java权限认证框架";
|
||||
//
|
||||
// // 使用公钥加密
|
||||
// String ciphertext = SaSecureUtil.rsaEncryptByPublic(publicKey, text);
|
||||
// System.out.println("公钥加密后:" + ciphertext);
|
||||
//
|
||||
// // 使用私钥解密
|
||||
// String text2 = SaSecureUtil.rsaDecryptByPrivate(privateKey, ciphertext);
|
||||
// System.out.println("私钥解密后:" + text2);
|
||||
//
|
||||
// return SaResult.ok();
|
||||
// }
|
||||
|
||||
// Base64 编码 ---- http://localhost:8081/secure/base64
|
||||
@RequestMapping("base64")
|
||||
|
@ -32,14 +32,14 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
// 注册 Sa-Token 拦截器打开注解鉴权功能
|
||||
registry.addInterceptor(new SaInterceptor(handle -> {
|
||||
// SaManager.getLog().debug("----- 请求path={} 提交token={}", SaHolder.getRequest().getRequestPath(), StpUtil.getTokenValue());
|
||||
|
||||
|
||||
// 指定一条 match 规则
|
||||
SaRouter
|
||||
.match("/user/**") // 拦截的 path 列表,可以写多个
|
||||
.notMatch("/user/doLogin", "/user/doLogin2") // 排除掉的 path 列表,可以写多个
|
||||
.check(r -> StpUtil.checkLogin()); // 要执行的校验动作,可以写完整的 lambda 表达式
|
||||
.match("/user/**") // 拦截的 path 列表,可以写多个
|
||||
.notMatch("/user/doLogin", "/user/doLogin2") // 排除掉的 path 列表,可以写多个
|
||||
.check(r -> StpUtil.checkLogin()); // 要执行的校验动作,可以写完整的 lambda 表达式
|
||||
|
||||
// 权限校验 -- 不同模块认证不同权限
|
||||
// 权限校验 -- 不同模块认证不同权限
|
||||
SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin"));
|
||||
SaRouter.match("/goods/**", r -> StpUtil.checkPermission("goods"));
|
||||
SaRouter.match("/orders/**", r -> StpUtil.checkPermission("orders"));
|
||||
@ -49,16 +49,16 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
// 甚至你可以随意的写一个打印语句
|
||||
SaRouter.match("/router/print", r -> System.out.println("----啦啦啦----"));
|
||||
|
||||
// 写一个完整的 lambda
|
||||
// 写一个完整的 lambda
|
||||
SaRouter.match("/router/print2", r -> {
|
||||
System.out.println("----啦啦啦2----");
|
||||
// ... 其它代码
|
||||
// ... 其它代码
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* 相关路由都定义在 com.pj.cases.use.RouterCheckController 中
|
||||
* 相关路由都定义在 com.pj.cases.use.RouterCheckController 中
|
||||
*/
|
||||
|
||||
|
||||
})).addPathPatterns("/**");
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,98 @@
|
||||
package cn.dev33.satoken.aop;
|
||||
|
||||
import cn.dev33.satoken.annotation.handler.SaAnnotationAbstractHandler;
|
||||
import cn.dev33.satoken.strategy.SaAnnotationStrategy;
|
||||
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Sa-Token AOP 环绕切入 Bean 注册
|
||||
* <p>
|
||||
* 参考资料:<br>
|
||||
* https://www.jb51.net/program/297714rev.htm <br>
|
||||
* https://www.bilibili.com/video/BV1WZ421W7Qx <br>
|
||||
* https://blog.csdn.net/Tomwildboar/article/details/139199801 <br>
|
||||
* </p>
|
||||
*
|
||||
* @author click33
|
||||
* @since 2024/8/3
|
||||
*/
|
||||
@Configuration
|
||||
public class SaAopPointcutAdvisorBeanRegister {
|
||||
|
||||
/**
|
||||
* Advisor 静态全局引用
|
||||
*/
|
||||
public static SaAroundAnnotationPointcutAdvisor saAroundAnnoAdvisor;
|
||||
|
||||
@Bean
|
||||
public SaAroundAnnotationPointcutAdvisor saAroundAnnotationHandlePointcutAdvisor (List<SaAnnotationAbstractHandler<?>> handlerList) {
|
||||
SaAroundAnnotationPointcutAdvisor advisor = new SaAroundAnnotationPointcutAdvisor();
|
||||
|
||||
// 定义切入规则
|
||||
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
|
||||
String expression = calcExpression(handlerList);
|
||||
pointcut.setExpression(expression);
|
||||
advisor.setPointcut(pointcut);
|
||||
|
||||
// 定义执行的方法
|
||||
advisor.setAdvice(new SaAroundAnnotationMethodInterceptor());
|
||||
|
||||
// 保存全局引用
|
||||
SaAopPointcutAdvisorBeanRegister.saAroundAnnoAdvisor = advisor;
|
||||
return advisor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算切入表达式
|
||||
* @param appendHandlerList 追加的 SaAnnotationAbstractHandler 处理器
|
||||
* @return /
|
||||
*/
|
||||
public static String calcExpression(List<SaAnnotationAbstractHandler<?>> appendHandlerList) {
|
||||
|
||||
// 框架内置的
|
||||
List<Class<?>> list = new ArrayList<>(SaAnnotationStrategy.instance.annotationHandlerMap.keySet());
|
||||
|
||||
// 额外追加的
|
||||
if(appendHandlerList != null) {
|
||||
for (SaAnnotationAbstractHandler<?> handler : appendHandlerList) {
|
||||
Class<?> cls = handler.getHandlerAnnotationClass();
|
||||
if(!list.contains(cls)) {
|
||||
list.add(handler.getHandlerAnnotationClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 计算
|
||||
return calcClassListExpression(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算 class 列表的切入表达式,
|
||||
* 最终样例形如:
|
||||
<pre>
|
||||
public static final String POINTCUT_SIGN =
|
||||
"@within(cn.dev33.satoken.annotation.SaCheckLogin) || @annotation(cn.dev33.satoken.annotation.SaCheckLogin) || "
|
||||
+ "@within(cn.dev33.satoken.annotation.SaCheckRole) || @annotation(cn.dev33.satoken.annotation.SaCheckRole) || "
|
||||
+ "@within(cn.dev33.satoken.annotation.SaCheckPermission) || @annotation(cn.dev33.satoken.annotation.SaCheckPermission)";
|
||||
</pre>
|
||||
* @param list /
|
||||
* @return /
|
||||
*/
|
||||
public static String calcClassListExpression(List<Class<?>> list) {
|
||||
String pointcutExpression = "";
|
||||
for (Class<?> cls : list) {
|
||||
if(!pointcutExpression.isEmpty()) {
|
||||
pointcutExpression += " || ";
|
||||
}
|
||||
pointcutExpression += "@within(" + cls.getName() + ") || @annotation(" + cls.getName() + ")";
|
||||
}
|
||||
return pointcutExpression;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.aop;
|
||||
|
||||
import cn.dev33.satoken.exception.StopMatchException;
|
||||
import cn.dev33.satoken.strategy.SaAnnotationStrategy;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Sa-Token 注解方法拦截器 AOP环绕切入
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.39.0
|
||||
*/
|
||||
public class SaAroundAnnotationMethodInterceptor implements MethodInterceptor {
|
||||
|
||||
@Override
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
// 注解鉴权
|
||||
try{
|
||||
Method method = invocation.getMethod();
|
||||
SaAnnotationStrategy.instance.checkMethodAnnotation.accept(method);
|
||||
} catch (StopMatchException ignored) {
|
||||
}
|
||||
// 执行原有防范
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.aop;
|
||||
|
||||
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||
|
||||
/**
|
||||
* Sa-Token 注解方法 Advisor AOP环绕切入
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.39.0
|
||||
*/
|
||||
public class SaAroundAnnotationPointcutAdvisor extends DefaultPointcutAdvisor {
|
||||
|
||||
}
|
@ -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.aop;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
|
||||
/**
|
||||
* Sa-Token 基于 Spring Aop 的注解鉴权
|
||||
*
|
||||
* <p>
|
||||
* 注意:在打开 注解鉴权 时,AOP 模式与拦截器模式不可同时使用,否则会出现在 Controller 层重复鉴权两次的问题
|
||||
* </p>
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.19.0
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
@Order(SaTokenConsts.ASSEMBLY_ORDER)
|
||||
public class SaCheckAspect {
|
||||
|
||||
/**
|
||||
* 构建
|
||||
*/
|
||||
public SaCheckAspect() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义AOP签名 (切入所有使用 Sa-Token 鉴权注解的方法)
|
||||
*/
|
||||
public static final String POINTCUT_SIGN =
|
||||
"@within(cn.dev33.satoken.annotation.SaCheckLogin) || @annotation(cn.dev33.satoken.annotation.SaCheckLogin) || "
|
||||
+ "@within(cn.dev33.satoken.annotation.SaCheckRole) || @annotation(cn.dev33.satoken.annotation.SaCheckRole) || "
|
||||
+ "@within(cn.dev33.satoken.annotation.SaCheckPermission) || @annotation(cn.dev33.satoken.annotation.SaCheckPermission) || "
|
||||
+ "@within(cn.dev33.satoken.annotation.SaCheckSafe) || @annotation(cn.dev33.satoken.annotation.SaCheckSafe) || "
|
||||
+ "@within(cn.dev33.satoken.annotation.SaCheckDisable) || @annotation(cn.dev33.satoken.annotation.SaCheckDisable) || "
|
||||
+ "@within(cn.dev33.satoken.annotation.SaCheckHttpDigest) || @annotation(cn.dev33.satoken.annotation.SaCheckHttpDigest) || "
|
||||
+ "@within(cn.dev33.satoken.annotation.SaCheckOr) || @annotation(cn.dev33.satoken.annotation.SaCheckOr) || "
|
||||
+ "@within(cn.dev33.satoken.annotation.SaCheckHttpBasic) || @annotation(cn.dev33.satoken.annotation.SaCheckHttpBasic)";
|
||||
|
||||
/**
|
||||
* 声明AOP签名
|
||||
*/
|
||||
@Pointcut(POINTCUT_SIGN)
|
||||
public void pointcut() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 环绕切入
|
||||
*
|
||||
* @param joinPoint 切面对象
|
||||
* @return 底层方法执行后的返回值
|
||||
* @throws Throwable 底层方法抛出的异常
|
||||
*/
|
||||
@Around("pointcut()")
|
||||
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
|
||||
// 获取对应的 Method 处理函数
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
|
||||
// 如果此 Method 或其所属 Class 标注了 @SaIgnore,则忽略掉鉴权
|
||||
if(SaStrategy.instance.isAnnotationPresent.apply(method, SaIgnore.class)) {
|
||||
// ...
|
||||
} else {
|
||||
// 注解鉴权
|
||||
SaStrategy.instance.checkMethodAnnotation.accept(method);
|
||||
}
|
||||
|
||||
// 执行原有逻辑
|
||||
return joinPoint.proceed();
|
||||
}
|
||||
|
||||
}
|
@ -1 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.dev33.satoken.aop.SaCheckAspect
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
cn.dev33.satoken.aop.SaAopPointcutAdvisorBeanRegister
|
@ -1 +1 @@
|
||||
cn.dev33.satoken.aop.SaCheckAspect
|
||||
cn.dev33.satoken.aop.SaAopPointcutAdvisorBeanRegister
|
Loading…
Reference in New Issue
Block a user