我正在构建支持多租户授权模型的REST API的概念验证。此模型不仅可以控制用户可以访问的对象,还可以访问对象中的字段。此模型的目标是确保租户管理员只能修改其租户,并且只能查看允许的对象属性。
我有一个我正在处理的现有代码库,可以在https://github.com/cypherkey/multi-tenant-rest-api公开使用。它基于示例Spring OAUTH2资源服务器项目。我编写了自己的字段级安全性实现,它使用分析DTO和模型中字段的注释,如果用户有足够的权限,它使用反射将数据从一个类复制到另一个类。虽然这似乎有效,但我想确保我走上正确的道路。是否有更标准的Spring方法或可能实现此目的的另一个框架?
我一直在研究JSONViews。它们看起来像是用于序列化。我将为SUPERADMIN,TENANTADMIN和USER级角色创建不同的视图。控制器将负责确定客户端是否可以访问该对象,JSONView将负责过滤字段/属性。问题是我找到了一些支持序列化的示例,但没有在控制器级别的POST / PUT上进行反序列化。例如:
答案 0 :(得分:1)
我发现了这个问题,因为我偶然发现了同样的问题。我知道这是一个老问题,但是我发布了解决方案,因此也许处于相同情况的任何人都可以找到帮助。我最终使用了基于反射(针对更新请求)和Genson API方法(针对读取请求)的混合解决方案。
首先,我定义了一个类,其中包含每个实体和每种角色的List
禁止字段。当应用程序启动时,它将读取包含此信息的配置文件,并将其存储在此类的对象中。该对象包含在Context
中,因此可以从服务请求中访问此信息。
调用GET请求时,将获得实体,但是此后,将创建一个Genson
对象,如下所示:
protected Genson buildRestrictedGenson(String rolename, ElementExcludedFields ef) {
List<String> excludedFields = null;
if(rolename.equals(Utils.ROL_ADMIN))
excludedFields = ef.getAdminUser();
else if(rolename.equals(Utils.ROL_BASIC))
excludedFields = ef.getBasicUser();
else
return new Genson();
GensonBuilder gb = new GensonBuilder();
for(String field : excludedFields)
gb.exclude(field);
return gb.create();
}
使用exclude
方法意味着序列化对象时,获得的String
将不包含禁止字段。因此,对于每个实体的每个GET,我只需要调用此方法,序列化通过此Genson
对象获得的对象,并将其包括在响应中即可。
用于更新有点复杂,因为我使用Hibernate进行数据持久化,并且它需要完整的对象来执行更新。因此,第一步将是从数据库中获取原始对象。当我拥有它时,我可以使用反射来忽略禁止的字段,将原始值分配给“更新”对象的此字段。像这样:
protected void excludeUpdateFields(T entity, int id, String rolename, ElementExcludedFields ef) {
T t = getService().get(id);
Iterable<Field> fields = ReflectionUtils.getAllFields(entity.getClass());
List<String> fieldList;
if(rolename.equals(Utils.ROL_ADMIN))
fieldList = ef.getAdminUser();
else if(rolename.equals(Utils.ROL_BASIC))
fieldList = ef.getBasicUser();
else
return;
for(Field field : fields) {
if(fieldList.contains(field.getName())) {
try {
field.setAccessible(true);
Object value = field.get(t);
field.set(entity, value);
} catch (IllegalArgumentException ex) {
Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
调用此函数后,禁止字段的更新将没有任何效果,因为“正在更新”的对象将具有这些字段中的原始值。