sshe源码分析——权限管理的后台部分
数据库结构:
用户跟组织:多对对
用户跟角色:多对多
组织:有上级组织
资源跟组织:多对多
资源跟角色:多对多
资源:有上级资源
资源:有资源类型
BaseDaoImpl中用了Hibernate原生的session:
@Repository public class BaseDaoImpl<T> implements BaseDaoI<T> { @Autowired private SessionFactory sessionFactory; /** * 获得当前事物的session * * @return org.hibernate.Session */ public Session getCurrentSession() { return sessionFactory.getCurrentSession(); } @Override public T getByHql(String hql) { Query q = getCurrentSession().createQuery(hql); List<T> l = q.list(); if (l != null && l.size() > 0) { return l.get(0); } return null; } @Override public T getByHql(String hql, Map<String, Object> params) { Query q = getCurrentSession().createQuery(hql); if (params != null && !params.isEmpty()) { for (String key : params.keySet()) { q.setParameter(key, params.get(key)); } } List<T> l = q.list(); if (l != null && l.size() > 0) { return l.get(0); } return null; } @Override public Long count(String hql) { Query q = getCurrentSession().createQuery(hql); return (Long) q.uniqueResult(); } @Override public List<Map> findBySql(String sql) { SQLQuery q = getCurrentSession().createSQLQuery(sql); return q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list(); } |
BaseServiceImpl中有对泛型的处理:
@Service public class BaseServiceImpl<T> implements BaseServiceI<T> { @Autowired private BaseDaoI<T> baseDao; @Override public T getById(Serializable id) { Class<T> c = (Class<T>) ((ParameterizedType) getClass() .getGenericSuperclass()).getActualTypeArguments()[0]; return baseDao.getById(c, id); } @Override public T getByFilter(HqlFilter hqlFilter) { String className = ((Class<T>) ((ParameterizedType) getClass() .getGenericSuperclass()).getActualTypeArguments()[0]).getName(); String hql = "select distinct t from " + className + " t"; return getByHql(hql + hqlFilter.getWhereAndOrderHql(), hqlFilter.getParams()); } @Override public Long countByFilter(HqlFilter hqlFilter) { String className = ((Class<T>) ((ParameterizedType) getClass() .getGenericSuperclass()).getActualTypeArguments()[0]).getName(); String hql = "select count(distinct t) from " + className + " t"; return count(hql + hqlFilter.getWhereHql(), hqlFilter.getParams()); } |
这里有个自定义的HqlFilter,用于添加where条件和排序,过滤结果集
/** * 带参构造 * * @param request */ public HqlFilter(HttpServletRequest request) { this.request = request; addFilter(request); } /** * 添加过滤 * * @param request */ public void addFilter(HttpServletRequest request) { Enumeration<String> names = request.getParameterNames(); while (names.hasMoreElements()) { String name = names.nextElement(); String value = request.getParameter(name); addFilter(name, value); } } /** * 添加过滤 * * 举例,name传递:QUERY_t#id_S_EQ * * 举例,value传递:0 * * @param params */ public void addFilter(String name, String value) { if (name != null && value != null) { if (name.startsWith("QUERY_")) {// 如果有需要过滤的字段 String[] filterParams = StringUtils.split(name, "_"); if (filterParams.length == 4) { String columnName = filterParams[1].replaceAll("#", ".");// 要过滤的字段名称=t.id String columnType = filterParams[2];// 字段类型=S String operator = filterParams[3];// SQL操作符=EQ String placeholder = UUID.randomUUID().toString().replace("-", "");// 生成一个随机的参数名称 if (hql.toString().indexOf(" where 1=1") < 0) { hql.append(" where 1=1 "); } hql.append(" and " + columnName + " " + getSqlOperator(operator) + " :param" + placeholder + " ");// 拼HQL params.put("param" + placeholder, getObjValue(columnType, operator, value));// 添加参数 } } } } |
上面这个方法把 request里的参数全部映射,并且把 QUERY_t#id_S_EQ 拼成HQL
“Where 1=1 and t.id =:paramXXX “
然后准备好paramXXX的参数替换
其中操作符operator 是自定义的,包括EQ,NE,LK等,表示=,!=,like
参数替换时的columnType也是自定义的,包括S,I,D等,表示String,Integer,Date
String的时候如果是like,还要动态组合两边的 ’%’
/** * 获得添加过滤字段后加上排序字段的HQL * * @return */ public String getWhereAndOrderHql() { if (!StringUtils.isBlank(sort) && !StringUtils.isBlank(order)) { if (sort.indexOf(".") < 1) { sort = "t." + sort; } hql.append(" order by " + sort + " " + order + " ");// 添加排序信息 } else { if (request != null) { String s = request.getParameter("sort"); String o = request.getParameter("order"); if (!StringUtils.isBlank(s)) { sort = s; } if (!StringUtils.isBlank(o)) { order = o; } if (!StringUtils.isBlank(sort) && !StringUtils.isBlank(order)) { if (sort.indexOf(".") < 1) { sort = "t." + sort; } hql.append(" order by " + sort + " " + order + " ");// 添加排序信息 } } } return hql.toString(); } |
这样就把Action里的参数完全转换成了HQL
SyuserServiceImpl中:
@Override public void grantRole(String id, String roleIds) { Syuser user = getById(id); if (user != null) { user.setSyroles(new HashSet<Syrole>()); for (String roleId : roleIds.split(",")) { if (!StringUtils.isBlank(roleId)) { Syrole role = roleDao.getById(Syrole.class, roleId); if (role != null) { user.getSyroles().add(role); } } } } } @Override public void grantOrganization(String id, String organizationIds) { Syuser user = getById(id); if (user != null) { user.setSyorganizations(new HashSet<Syorganization>()); for (String organizationId : organizationIds.split(",")) { if (!StringUtils.isBlank(organizationId)) { Syorganization organization = organizationDao.getById( Syorganization.class, organizationId); if (organization != null) { user.getSyorganizations().add(organization); } } } } } |
SyroleServiceImpl中:
@Override public void saveRole(Syrole syrole, String userId) { save(syrole); Syuser user = userDao.getById(Syuser.class, userId); user.getSyroles().add(syrole);// 把新添加的角色与当前用户关联 } |
SyorganizationServiceImpl中:
@Override public void saveOrganization(Syorganization syorganization, String userId) { save(syorganization); Syuser user = userDao.getById(Syuser.class, userId); user.getSyorganizations().add(syorganization); } |
Model中:
Syuser:
@ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "SYUSER_SYORGANIZATION", schema = "", joinColumns = { @JoinColumn(name = "SYUSER_ID", nullable = false, updatable = false) }, inverseJoinColumns = { @JoinColumn(name = "SYORGANIZATION_ID", nullable = false, updatable = false) }) public Set<Syorganization> getSyorganizations() { return this.syorganizations; } public void setSyorganizations(Set<Syorganization> syorganizations) { this.syorganizations = syorganizations; } @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "SYUSER_SYROLE", schema = "", joinColumns = { @JoinColumn(name = "SYUSER_ID", nullable = false, updatable = false) }, inverseJoinColumns = { @JoinColumn(name = "SYROLE_ID", nullable = false, updatable = false) }) public Set<Syrole> getSyroles() { return this.syroles; } public void setSyroles(Set<Syrole> syroles) { this.syroles = syroles; } |
Syrole:
@ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "SYUSER_SYROLE", schema = "", joinColumns = { @JoinColumn(name = "SYROLE_ID", nullable = false, updatable = false) }, inverseJoinColumns = { @JoinColumn(name = "SYUSER_ID", nullable = false, updatable = false) }) public Set<Syuser> getSyusers() { return this.syusers; } public void setSyusers(Set<Syuser> syusers) { this.syusers = syusers; } |
Syorganization:
@OneToMany(fetch = FetchType.LAZY, mappedBy = "syorganization", cascade = CascadeType.ALL) public Set<Syorganization> getSyorganizations() { return this.syorganizations; } public void setSyorganizations(Set<Syorganization> syorganizations) { this.syorganizations = syorganizations; } @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "SYUSER_SYORGANIZATION", schema = "", joinColumns = { @JoinColumn(name = "SYORGANIZATION_ID", nullable = false, updatable = false) }, inverseJoinColumns = { @JoinColumn(name = "SYUSER_ID", nullable = false, updatable = false) }) public Set<Syuser> getSyusers() { return this.syusers; } public void setSyusers(Set<Syuser> syusers) { this.syusers = syusers; } |
这样,权限相关的后台部分就结束了,效果如下:
1.点击登陆,会首先获取菜单((SyresourceServiceI) service).getMainMenu(
先查角色的:
select distinct t from Syresource t join t.syroles role join role.syusers user where 1=1 and user.id = :param49bd757c16594cbe8657b7d88364d81a and t.syresourcetype.id = :paramf778a1257fa64e9ebca2a52bcc9750fc
再查组织的:
select distinct t from Syresource t join t.syorganizations organization join organization.syusers user where 1=1 and user.id = :param49bd757c16594cbe8657b7d88364d81a and t.syresourcetype.id = :paramf778a1257fa64e9ebca2a52bcc9750fc
2.点击用户管理菜单,会通过SecurityUtil查询权限,并动态显示某些功能图标
因为登陆时已经对该用户所有资源都强制加载了,这时候不再查数据库
也就是每次更改了权限后需要重新登陆
3.点击某个用户的用户角色:
首先调用user.getSyroles(),得到当前用户所有角色:当然这里是错的,应该跟用户无关,查询所有角色
Initializing collection [sy.model.base.Syuser.syroles#0]
SELECT syroles0_.SYUSER_ID AS SYUSER1_7_1_, syroles0_.SYROLE_ID AS SYROLE2_9_1_, syrole1_.ID AS ID1_5_0_, syrole1_.CREATEDATETIME AS CREATEDA2_5_0_, syrole1_.DESCRIPTION AS DESCRIPT3_5_0_, syrole1_.ICONCLS AS ICONCLS4_5_0_, syrole1_. NAME AS NAME5_5_0_, syrole1_.SEQ AS SEQ6_5_0_, syrole1_.UPDATEDATETIME AS UPDATEDA7_5_0_ FROM SYUSER_SYROLE syroles0_ INNER JOIN SYROLE syrole1_ ON syroles0_.SYROLE_ID = syrole1_.ID WHERE syroles0_.SYUSER_ID = '0' |
Collection initialized
这里为什么又查一遍数据库??
登陆时不是全都初始化过了??
然后得到所选用户的所有角色,并在页面上勾选:
select distinct t from Syrole t join t.syusers user where 1=1 and user.id = :paramdac50de702204cc6a258b8af78905a65
另外虽然数据库中的多对多并没有方向性,但页面上并不提供双向查询
这里的顺序是:用户->角色/组织->资源
通过后台可以发现,Hibernate总是有不必要的Sql执行,实际项目中肯定要最后优化一遍