比如说权限控制时,角色role1只能访问数据表table1的部分字段。我的想法是将建立一个role-数据表-字段的表table2,然后在查询table1后会根据orm返回一个对象,然后再查询table2, 返回json时只对允许访问的字段进行映射。
但是我只知道怎么建立静态的映射以及过滤,不知道如何动态地进行映射?
除了这种方式,还能如何控制字段的访问权限?
其实呢,控制字段权限有没有啥好东西可用,之前我没遇到过类似需求,没太关注,但是根据跟题主的评论来看,其实就是动态的序列化。其他不说Jackson
我记得就是可以做到动态序列化的
例如如下,这样就只有id
被序列化出来了
@Data
@AllArgsConstructor
@JsonFilter("user")
public class User {
private Long id;
private String name;
public static void main(String[] args) throws JsonProcessingException {
User user = new User(1l, "haha");
ObjectMapper objectMapper = new ObjectMapper();
SimpleFilterProvider simpleFilterProvider = new SimpleFilterProvider();
simpleFilterProvider.addFilter("user", SimpleBeanPropertyFilter.filterOutAllExcept("id"));
objectMapper.setFilterProvider(simpleFilterProvider);
String s = objectMapper.writeValueAsString(user);
System.out.println(s);
}
}
如果仅仅只是为了达到这样的效果,其实也就够了叭。仅供参考嗷
2020-03-27更新
其实吧,之前的那种通过查询table1
出来数据后再根据table2
的记录选择字段序列化的方式感觉还是不够好,原因显而易见,那就是table1
的数据是完整查出来了啊
个人观点,正确的模式理应来说应该先是根据table2
的记录查询出要使用哪些字段,然后再根据这些字段去查询table1
,对于数据库来说就是select colum1,colum2 ...
但是由于运用了orm
的一些工具,所以本质来说我们是排斥直接面向数据库编程的,面向JPA
编程才是关键,虽说大多数orm
肯定也是支持native query
,咱们也还是要回归面向对象去处理数据库的。
所以说结合我之前的经验,这里的问题我感觉就转变为:如何根据某些特定的字段通过JPA api
做出select colum1,colum2 ...
的效果。
回到JPA
以及现在Spring
的生态,肯定不得不使用到Spring Data JPA
对于JPA
的再封装。所以上面的问题又再变为:如何根据某些特定的字段通过Spring Data JPA api
做出select colum1,colum2 ...
的效果。
而我们平常使用Spring Data JPA
都会使用起Respository
,但是普通咱们的Respository
还是满足不了我们的需求的,所以我们需要定制Respository
比如我们有个Entity
叫Student
,里面3个字段,还有个对应的StudentRepository
@Data
@Entity
@Table(name = "student")
public class Student {
@Id
@Column
private Long id;
@Column
private String name;
@Column(name = "nick_name")
private String nickName;
}
public interface StudentRepository extends JpaRepository<Student, Long> {
}
由于StudentRepository
满足不了我们的需求,所以我们需要定制一下,或者按照官方说法叫扩展一些StudentRepository
的功能,参考Spring Data JPA文档
我们创建一个新接口CustomStudentRepository
,我们就是要根据一些字段名然后查询出来后直接转换为JSON
字符串(不转为一个POJO
,主要就是因为本身需要动态,POJO
里的字段是不固定的,干脆就直接到最后的JSON
字符串)
public interface CustomStudentRepository {
String listToJSONString(List<String> fieldNames);
}
然后创建新接口的实现CustomStudentRepositoryImpl
去完成逻辑,大多数逻辑其实就是用JPA api
去操作数据库,最后结果用fastjson
直接转成字符串
@RequiredArgsConstructor
public class CustomStudentRepositoryImpl implements CustomStudentRepository {
@Autowired
@NonNull
private final EntityManager em;
@Override
public String listToJSONString(List<String> fieldNames) {
CriteriaQuery<Tuple> criteriaQuery = em.getCriteriaBuilder().createTupleQuery();
Root<Student> root = criteriaQuery.from(Student.class);
List paths = fieldNames.stream().map(fieldName -> root.get(fieldName))
.collect(Collectors.toList());
criteriaQuery.multiselect(paths);
List<Tuple> tupleResult = em.createQuery(criteriaQuery).getResultList();
List<Map<String, Object>> list = new ArrayList<>();
for (Tuple tuple : tupleResult) {
Map<String, Object> map = new HashMap<>();
for (int i = 0; i < fieldNames.size(); i++) {
map.put(fieldNames.get(i), tuple.get(i));
}
list.add(map);
}
return JSONObject.toJSONString(list);
}
}
当然最后再修改一下我们的StudentRepository
,毕竟CustomStudentRepository
是StudentRepository
的扩展,因此StudentRepository
是要继承CustomStudentRepository
public interface StudentRepository extends JpaRepository<Student, Long>, CustomStudentRepository {
}
这样使用StudentRepository
就可以调用listToJSONString
方法达到效果了
String students = studentRepository.listToJSONString(Arrays.asList("id", "nickName"));
System.out.println(students);
当然我这里listToJSONString
的方法入参和出参,仅仅是为了解决我最开始提到的问题,实际题主自己的情况还可以再做调整,比如去掉入参List<String> fieldNames
,这里fieldNames
的来源直接在listToJSONString
查询table2
获取到,或者自己搞一个StudentService
把fieldNames
的来源在Service
中读到,Service
对外暴露的方法可以再业务化一点,这些都是看题主实际项目设计了,最后如果在listToJSONString
中还有更多的查询条件的需求,简单了解一下JPA
的语法,其实也不算太难。
就酱~拜(??????)??