之前琴海森林在JFinal分享他的脚手架项目JFinal-Layui(http://www.jfinal.com/project/258),强烈安利给准备做企业中小型项目的同学们尝试。本人已在公司实际项目中运用,开发体验非常不错,方便快捷。特别是经典的功能-用户-角色的权限管理功能做的非常好,十分灵活,即可以为单一用户直接分配角色,又可以逆向为角色指定用户。
为用户分配角色
为角色分配用户
使用了这套脚手架做常规企业内部系统是非常方便的,节省了自行开发系统管理模块的大量时间。不过其中有一个比较常见的业务场景就是企业一般有自己OA系统,可能是自己开发的,也可能是第三方外包开发的。OA负责管理企业的用户、组织数据,并在OA平台上对各个内部系统集成了单点登录功能。比如:
这样,通常会要求各子系统在实现登录和权限控制时,需要获取OA数据库的用户、组织数据,而不能使用脚手架自带的用户组织表中的数据。另外企业级OA所使用的数据库很有可能不是mysql,而是sqlserver或者oracle。
那么如果我想用一套已经实现了权限控制功能的脚手架去进行二次开发做企业内部系统,如何在不变更业务系统数据库的前提下,实现对OA数据库中的用户进行子系统的权限分配呢,这里我分享一下我的实现思路。
首先既然要对企业OA数据库用户进行权限配置,那么我们就要获取到OA的用户信息,意味着我们需要在系统中配置多数据源。当然,在JFinal项目中配置多数据源是再简单不过的,如图:
在自己系统的配置文件中,写上OA数据库的链接参数,然后按照JFinal的文档,在主配置类中的configPlugin中配置OA数据源和对应的ActiveRecord
DruidPlugin oaDbPlugin = new DruidPlugin(p.get("oa_jdbcUrl"), p.get("oa_user"), p.get("oa_password").trim()); ActiveRecordPlugin oaArp=new ActiveRecordPlugin("oa",oaDbPlugin); oaArp.setDialect(new SqlServerDialect()); Engine oaEngine = oaArp.getEngine(); oaEngine.setSourceFactory(new ClassPathSourceFactory()); oaEngine.addSharedStaticMethod(StrKit.class); oaArp.addSqlTemplate(WebContant.oaSqlTemplate); me.add(oaDbPlugin); me.add(oaArp);
注意上图第二行代码,我们为OA的ActiveRecord定义了别名"oa",所以以后我们就可以使用Db.use("oa")来切换到OA数据库取进行数据查询等操作。
并且,因为子系统中,一般不需要对OA的用户和组织数据进行增删改的操作,仅仅用于获取用户数据,以方便集成单点登录和子系统权限分配就可以了。所以在这里,我并没有为OA的用户表生成对应的JFinal Model以及MappingKit文件,使用通用的Record对象足矣。
现在回到JFinal-Layui的脚手架的用户管理界面,如图:
其实上图中我们只需要拿到OA的用户以列表页进行展示,然后有一个角色分配的按钮,和若干查询条件就搞定了,所以改造以后页面可以简化为:
这个页面,无非就是吧之前查询系统的sys_user表切换为查询OA的用户表,所以后台改起来很简单,在脚手架中写一个自己用的OAUserService和OAUserController
在Controller类中实现获取用户数据的方法
/** * 分页查询OA人员信息 */ public void list() { JSONObject params = getAllParamsToJson(); Grid grid = oaUserService.queryForListBySqlTemplate("oa",getParaToInt("pageNumber",1), getParaToInt("pageSize",15), params,"synchro.queryOaUserByConditions",null); renderJson(grid); }
其中queryForListBySqlTemplate方法是写在BaseService中的扩展方法,用于通过执行sql模板中的sql语句,查找无而对应实体的数据列表,返回layui table 所需要的json格式数据,用于前端列表页渲染
代码实现如下:
/** * 自定义sql查询,sql定义在sql模板文件中 * @param dbName 数据库配置名称,用于切换多数据源查询,为null时查询主数据源 * @param params 查询参数(JSONObject) * @param sqlTemplateName sql唯一名称(命名空间.sql语句名称) * @param orderBygroupBySql 排序语句 * @return */ public Grid queryForListBySqlTemplate(String dbName,Integer pageNumber,Integer pageSize,JSONObject params,String sqlTemplateName,String orderBygroupBySql) { DbPro dbPro = Db.use(); //切换数据源 if(StrKit.notBlank(dbName)) { dbPro = Db.use(dbName); } SqlPara sqlPara = dbPro.getSqlPara(sqlTemplateName,params); if(StrKit.notBlank(orderBygroupBySql)) {//如果有排序语句,则追加 sqlPara.setSql(sqlPara.getSql() + " " + orderBygroupBySql); } Page<Record> page = dbPro.paginate(pageNumber,pageSize, sqlPara); return new Grid(page.getList(),pageNumber,pageSize,page.getTotalRow()); }
该方法提供通用的执行sql模板中定义的sql获取表格数据的方法,其中dbName参数用于切换不同数据源,sqlTemplateName用于指定具体sql id。
接着,在功能管理中,把用户管理的链接做切换即可。
这样关于用户列表中的数据切换我们就做完了。
然后,用户的角色分配界面,不需要做任何变更
从系统的表设计中,我们可以知道角色分配功能(即菜单),用户分配角色,所以不管用户数据来源是什么,只要拿到用户id,我们就可以对该用于进行角色授权操作。用户-角色关系由中间表保存,这个界面的前端与后台代码几乎不需要做任何变更。
这样我们已经实现了给OA中的用户分配应用系统的角色。如果对权限配置需求不高,这样已经可以满足系统的使用。而且不受业务系统数据库与OA数据库之间的差异,也不需要考虑OA方提供接口或者做成微服务来调用,一个多数据源配置轻松搞定。
关于如何使用JFinal SQL动态管理,请移步https://www.jfinal.com/doc/5-13 学习
关于JFinal-layui的BaseService,参见 https://gitee.com/QinHaiSenLin/Jfinal-layui/blob/master/src/main/java/com/qinhailin/common/base/service/BaseService.java?oid=d1f7a3727d67f85e29af621197c448225e2424ff
不过,如果我们想做的更加完善,我们还想反过来,在角色管理页面,我们直接为该角色指定用户,功能界面如下:
可以看到,对于单一角色的用户分配,这个界面操作非常流畅,从左边可以筛选用户赋予该角色权限,从右边可以将已经拥有该角色权限的用户剔除。
那么很明显的,左右两边的数据来源呢,自然应该是来源我们OA数据库了。这里,如果OA数据库与系统数据库类型一致,都是mysql,我们自然会想到
左边的sql写法类似为 select * from sys_user where user_id not in (select user_id from sys_user_role where role_id = ?)
右边的sql写法类似为 select * from sys_user where user_id in (select user_id from sys_user_role where role_id = ?)
也就是说左右两边的用户的查询sql仅仅只有in和not in的区别。
假如用户数据库与业务数据库不是同一类型数据库,比如我这个项目,系统用脚手架开发,自然业务数据都放在了mysql中,而公司的用户数据,都在OA数据库,也就是sqlserver中。
上面那两条sql的写法显然不能够使用。也就是我们无法在跨数据库数据表之间进行关联查询,虽然Sqlserver提供了链接对象查询机制去查询其他数据库数据的功能,有兴趣可以查看下面的博文:
https://blog.csdn.net/jk1992jk/article/details/80220863
可是并不是我们现在想要的,因为我们业务系统主数据库是mysql,自然不会在OA数据库里去查子系统的数据。
仔细查看上述的两条sql,我们应该能察觉到这种关系
select 用户数据 from OA where 用户id in (select 用户id from 系统数据库 where 角色id = ?)
主sql部分我们查询的OA数据库,而子句部分查询的是系统数据库。OA用户数据数据范围受业务系统用户角色表的制约,那么~~
没错,我们可以把这种类型的关联sql拆分成两条sql查询,就完美的避开了跨库跨表关联查询的需求。
伪代码如下:
List<String> userIdList = Db.find("select user_id from sys_user_role where role_id = ?");
List<User> userList = Db.use("oa").find("select * from oa_user where ...",userIdList);
通过拆分,我们就完成了配置角色用用户界面的数据展示。以上图左侧角色可选用户数据查询方法为例,完整代码如下:
Controller层:
/** * 查询角色可选的用户列表 */ public void queryUserListNotInRoleCode(){ int pageNumber=getParaToInt("pageNumber",1); int pageSize=getParaToInt("pageSize",10); String roleCode=getPara("roleCode"); List<Record> userRoleList = sysUserRoleService.queryForList("select user_code from sys_user_role where role_code = ?",roleCode); JSONObject params=new JSONObject(); params.put("userName", getPara("userName")); params.put("orgName", getPara("orgName")); params.put("loginId", getPara("loginId")); params.put("itemList",userRoleList); //Grid grid=sysUserRoleService.queryUserListNotInRoleCode(pageNumber, pageSize,roleCode,record); Grid grid=sysUserRoleService.queryOAUserNotInRoleList(pageNumber, pageSize,params); renderJson(grid); }
其中itemList就是我们从业务系统数据库里得到的用户id集合,后面需要用到
Service层:
/** * 查询OA数据库中未配置具体角色的用户 * @param pageNumber * @param pageSize * @param roleCode * @param record * @return */ public Grid queryOAUserNotInRoleList(int pageNumber,int pageSize,JSONObject params){ Grid grid = queryForListBySqlTemplate("oa", pageNumber, pageSize, params,"synchro.queryOAUserNotInRoleList",null); return grid; }
sql模板(使用jfinal的enjoy引擎编写):
根据条件查询某个角色可选用户 #sql("queryOAUserNotInRoleList") select t1.id,t1.loginid,t1.lastname,t2.departmentname from HrmResource t1 left join HrmDepartment t2 on t1.departmentid = t2.id where t1.loginid is not null and t1.loginid != '' #if(itemList.size()>0) and t1.loginid not in (#for(item : itemList) #para(item.user_code) #(for.last ? "": ",") #end) #end #if(notBlank(loginId)) and t1.loginid like '%'+ #para(loginId) + '%' #end #if(notBlank(userName)) and t1.lastname like '%'+ #para(userName) + '%' #end #if(notBlank(orgName)) and t2.departmentname like '%'+ #para(orgName) + '%' #end #end
注意一下怎么进行for循环的迭代itemList,以及OA数据库对应的使用模糊查询的写法即可。
接着,我们还需要改造系统的登录代码,这里就不详细解释了,参考脚手架自带的登录功能,自己写一个新的登录方法,切换到OA数据库取匹配用户和密码即可,其余操昨,比如往session里存放数据之类的,照搬就行了。
好了,说到这里,关于怎么在不使用JFinal-layui自带的用户组织表进行业务系统开发并进行权限分配的思路就很清晰了。剩下的就等各位自己去摸索了~~~
再次放上脚手架分享地址:http://www.jfinal.com/project/258