// 从配置文件中读取permission层次范围
@Configuration
@ConfigurationProperties(prefix = "permission")
@EnableConfigurationProperties(PermissionConfig.class)
public class PermissionConfig {
private Map<String, String> permissionMap = new HashMap<>();
public Map<String, String> getPermissionMap() {
return permissionMap;
public void setPermissionMap(Map<String, String> permissionMap) {
this.permissionMap = permissionMap;
自定义注解
注解只是为了判断是否需要鉴权,不需要特殊的成员。同时设置 retention 为 runtime ,并设置作用对象为方法 method 。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
// 指名数据库查询方法需要和权限挂钩
public @interface Permission {}
Mybatis拦截器实现
主要的实现集中在 PermissionInterceptor 中,主要可分为几个部分:拦截配置,元数据获取,自定义注解判断,权限获取与sql修改,反射注入。主要逻辑集中在 intercept 方法中。
主要是通过 @Intercepts 注解对拦截器类需要拦截的Handler和方法进行设置,方便之后反射获取对应的类。我们这边是对语句的最终sql进行处理,选择StatementHandler中的 prepare 方法进行拦截,args中为方法参数类型来判断重载。如果是对query进行拦截,后续注入时其实已经执行了,新的sql并不会被调用。
@Intercepts({
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
@Component
public class PermissionInterceptor implements Interceptor {
元数据获取
用的Mybatis给的元数据类MetaObject进行获取。
private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
private static final ReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory();
@Autowired
private PermissionConfig permissionConfig;
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 获取sql信息
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = statementHandler.getBoundSql();
String sql = boundSql.getSql();
System.out.println("原sql为: " + sql);
// 获取元数据
MetaObject metaResultSetHandler = MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, REFLECTOR_FACTORY);
MappedStatement mappedStatement = (MappedStatement) metaResultSetHandler.getValue("delegate.mappedStatement");
MappedStatement 在 对应的Handler 的 delegate.mappedStatement 属性对象中,包含元数据信息。 获取类和方法信息。
// 获取调用方法
String id = mappedStatement.getId();
String className = id.substring(0, id.lastIndexOf("."));
String methodName = id.substring(id.lastIndexOf(".") + 1);
System.out.println("调用方法为: " + id);
反射获取对应方法的注解列表即可。
// 注解查询
Class clazz = Class.forName(className);
Method method = clazz.getDeclaredMethod(methodName);
boolean needPermission = method.isAnnotationPresent(Permission.class);
// 对注解方法进行权限处理
if(needPermission){
System.out.println("需要进行sql权限变化");
获取权限信息并进行sql修改
配置文件中获取权限范围信息,增加到原sql的条件判断中。
// 获取权限信息
HttpSession session = HttpUtil.getSession();
String permission = (String) session.getAttribute("permission");
Map<String, String> map = permissionConfig.getPermissionMap();
for(String key:map.keySet()){
System.out.println(key);
String canSelectPermission = null;
if(map.containsKey(permission)){
canSelectPermission = map.get(permission);
System.out.printf("当前权限:%s, 可查询范围:%s%n", permission, canSelectPermission);
// 修改sql
String newSql = String.format("select * from (%s) `range` where `range`.permission in (%s)", sql, canSelectPermission);
// String newSql = "select * from account where permission in (\"advertise\")";
System.out.println("修改后的sql为: " + newSql);
反射注入并执行
注入到BoundSql类中,替换原sql。
// 反射修改handler中的sql以执行
Class boundClass = boundSql.getClass();
Field field = boundClass.getDeclaredField("sql");
field.setAccessible(true);
field.set(boundSql, newSql);
数据库简单数据
包括一个查询权限字段
给予权限信息
Session中写入权限。
不同权限下获取的信息结果
Finance:
Develop:
一个简单的Mybatis的拦截器尝试,用于对sql依靠查询权限进行动态修改。
主要就是Mybatis这个MetaObject需要知道对应的statement的value才能反射拿到,查了好久才发现是delegate.mappedStatement,很神秘。
到此这篇关于使用MyBatis拦截器实现sql查询权限动态修改代码实例的文章就介绍到这了,更多相关MyBatis拦截器动态修改sql内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
您可能感兴趣的文章:
2006-2023 脚本之家 JB51.Net , All Rights Reserved.
苏ICP备14036222号