OpenJWeb基于SpringSecurity框架升级改造说明
OpenJWeb开发平台的安全框架是基于SpringSecurity基础上开发的。为了进一步提高安全性,防止非法注入式攻击和对未授权链接的恶意攻击,需要对OpenJWeb平台的安全框架进行改造。
一、OpenJWeb安全框架的数据库设计
表名 | 中文说明 |
comm_company | 公司基本信息 |
comm_dept | 部门基本信息(含业务组) |
comm_user | 人员基本信息 |
comm_roles | 系统角色 |
comm_auth | 权限基本信息(定义权限ID及URL资源控制表达式) |
comm_user_role | 用户角色关系 |
comm_role_auth | 角色权限关系 |
comm_user_auth | 用户权限关系 |
comm_dept_auth | 部门权限关系 |
comm_com_auth | 单位权限关系 |
comm_org_emp_rel | 业务组-人员关系 |
OpenJWeb通过权限视图来查询用户对应的权限集合,权限视图可以查询用户直接或间接拥有的权限,用户直接或间接拥有的权限包括:
(1)在用户管理中,直接给用户授权
(2)在用户管里中,给用户授予角色,在角色管理中,为角色授权,则用户会拥有他分配的角色权限。
(3)为用户所属部门授权,则用户拥有本部门的权限
(4)将用户加到业务组,可以获得此业务组的权限,业务组和部门不同之处在于,业务组成员可以是跨部门的。
(5)业务组和系统角色的区别,系统角色很多是开发时确定的,业务组可由用户自定义。
(6)公司权限:管理员可以将权限分配给公司,对应公司再将权限授权给本公司用户。
OpenJWeb的权限视图:
createorreplaceviewv_user_authas
selecta.user_id,a.login_id,b.auth_id,b.comm_code,b.auth_name,b.auth_resource,b.pic_file,b.sort_no,b.menu_url
fromcomm_usera,comm_authb,comm_user_authc,comm_com_authd
wherec.user_id=a.user_id
andc.auth_id=b.auth_id
and((a.com_id='C0001'andd.com_id='C0001')or(b.comm_code=d.auth_codeanda.com_id=d.com_id))
anda.is_in_use='Y'
andb.data_flgin('1','Y')
union
selecta.user_id,a.login_id,b.auth_id,b.comm_code,b.auth_name,b.auth_resource,b.pic_file,b.sort_no,b.menu_url
fromcomm_usera,comm_authb,comm_user_roled,comm_role_authe,comm_com_authf
wherea.user_id=d.user_id
andd.role_id=e.role_id
ande.auth_id=b.auth_id
and((a.com_id='C0001'andf.com_id='C0001')or(b.comm_code=f.auth_codeanda.com_id=f.com_id))
anda.is_in_use='Y'
andb.data_flgin('1','Y')
union
selecta.user_id,a.login_id,b.auth_id,b.comm_code,b.auth_name,b.auth_resource,b.pic_file,b.sort_no,b.menu_url
from(
selecta.user_id,a.login_id,b.row_idrow_id,b.tree_code,b.org_type
fromcomm_usera,comm_deptb
wherea.dept_id=b.pk_id
anda.is_in_use='Y'
union
selecta.user_id,c.login_id,a.org_idrow_id,b.tree_code,b.org_type
fromcomm_org_emp_rela,comm_deptb,comm_userc
whereb.row_id=a.org_id
andc.user_id=a.user_id
andc.is_in_use='Y'
)a,
(
selecta.dept_id,c.auth_id,b.tree_code,c.comm_code,c.auth_name,c.auth_resource,b.org_type,b.row_id,c.pic_file,c.sort_no,c.menu_url
fromcomm_dept_autha,comm_deptb,comm_authc,comm_com_authd
wherea.dept_id=b.row_id
and((b.com_id='C0001'andd.com_id='C0001')or(c.comm_code=d.auth_codeandb.com_id=d.com_id))
anda.auth_id=c.auth_id
andc.data_flgin('1','Y')
)b
where((b.tree_codelikea.tree_code||'%'andb.org_typein('ROLES','POSITION','DEPT'))
anda.org_typein('ROLES','POSITION','DEPT'))
andb.row_id=a.row_id;
权限视图的变更:
因为在comm_auth中增加了menu_url字段,所以在v_user_auth中也增加了menu_url,以前使用auth_resource作为菜单url字段,但是由于现在SpringSecurity配置需要增加大量的*通配符做url访问控制,所以单独建立一个menu_url字段存储功能菜单对应的url,这个url是不能带有通配符的。
二、安全配置的修改
为了防止非法注入攻击,可通过SpringSecurity配置未授权的url格式不允许访问,只有授权的才可以访问,操作方式是:
(1)在功能菜单定义中,定义禁止访问某个目录(实际目录和虚拟目录都可以),例如默认禁止comm路径下的所有访问,权限URL可写成/comm/**,并且顺序号设为999999(只要确保这种禁止目录的权限在排序列表中位于最后就可以),这样不管是实际comm目录还是虚拟的comm路径(action的映射路径)都不能访问,这种权限不要分配给任何用户或角色。这样配置后,/comm/xxxx.jsp?a=1&b=2等url连接都不允许访问了。
(2)定义了/comm/**禁止访问后,再单独设置具体的连接时可以访问来的,比如定义一个功能为/comm/listCommCompany*.action*,然后授权给具体用户或角色,这样
/comm/listCommCompany.action
/comm/listCommCompany!edit.action?selId=123456
/comm/listCommCompany!firstPage.action
都符合/comm/listCommCompany*.action*,只要把/comm/listCommCompany*.action*授权给用户,则符合这种通配符规则的是都可以访问的,在定义权限的时候,确保这个权限的排序号要小于/comm/**的排序号。
如果要做非常严格的URL授权,任何程序未指定的URL参数都不允许访问,则需要逐一配置URL权限,这时候不配置/comm/listCommCompany*.action*,而采用下面的配置:
/comm/listCommCompany.action作为默认列表页查询URL
/comm/listCommCompany!firstPage.action跳转到首页
/comm/listCommCompany!lastPage.action跳转到末页
/comm/listCommCompany!nextPage.action下一页
/comm/listCommCompany!previousPage.action上一页
/comm/listCommCompany!gotoPage.action跳转到指定页(页码在form表单里,url地址中不带跳转页面参数)
/comm/listCommCompany!delete.action删除
/comm/listCommCompany!save.action保存
/comm/listCommCompany!edit.action!selId=123456编辑
除了以上固定的连接,还有在开发过程中增加的操作,例如送审:
/comm/listCommCompany!doSendCheck.action送审
/comm/listCommCompany!doCheck.action?opion=agree审核同意
......等等很多增加的操作连接
/comm/listCommCompany.action其实是默认的是执行/comm/listCommCompany!firstPage.action,所以基础查询类的功能连接可以统一写为:
/comm/listCommCompany!*page.action
(如果action后不带*,则http://localhost:8088/portal/comm/listCommCompany!firstPage.action?a=3这种带参数的连接则无权访问)
其他的连接也要定义权限,如果不允许带参数的就不带*,允许带参数的可以带*,参数值是变化的可以带*,例如/comm/listCommCompany!edit.action!selId=*
这种严格的URL控制的优点是权限控制很细致,缺点是增加维护量。而且在增加新的action方法的时候也需要配置url,比如增加一个审批的按钮。
如果需要简化配置,可以采取这种方式:
(1)仍使用通配符模式,例如基本权限仍是/comm/listCommCompany*.action*,这样增删改查和各种业务操作的url连接都匹配这种模式,如果要对编辑权限进行控制,仍然要增加一个编辑权限节点(例如AUTH_EDIT_COM),但是可以不输入url地址匹配字符串,而是在action中的编辑方法的initAction()下面增加一段代码:
publicStringedit()throwsDataAccessException,Exception
{
this.initAction();
//增加判断权限
CommUsertmpUser=SessionUtil.getCommUser(this.getRequest());
if(tmpUser==null)
{
this.addActionError("用户未登录或登录超时!");
return"error";
}
if(!tmpUser.hasAuth("AUTH_EDIT_COM"))//编辑商品权限
{
this.addActionError("当前用户不具有编辑公司基本信息的权限!");
return"error";
}
...
}
三、建议的方案
(1)建议还是要做精确匹配,因为即使不做精确匹配,很有可能某功能模块当前的编辑、保存不需要单独控制(即能查看数据的人也可以维护数据),但很难确保以后不会发生权限控制的需求。所以还是要做精确匹配。
(2)即使不做精确匹配,通过在action中增加控制判断代码,也需要定义一个权限ID
(3)无论是否做精确URL匹配,建议在action中还是要写控制代码,因为很难确保因为工作疏忽的原因漏掉某个关键URL的权限控制
(4)虽然在JSP页面中可以通过<sec:authorizeifAllGranted=”AUTH_EDIT_COM”></sec:authorize>这种权限标签来使按钮置灰或隐藏,但是这种方式也回避不了在url中直接输入按钮对应的URL连接来进行恶意攻击,所以即使通过按钮隐藏的方式控制权限,URL的精确匹配的权限配置也是必须的。
(5)在系统设计时,在webapps可直接访问的目录下的JSP,建议将公共的(无需登录就能访问的JSP页面)和需要登录才能访问的JSP页面分来,这样可以减少JSP权限配置的工作量。
(6)如果一个目录既有无需登录就可访问的,也有必须登录才能访问的情况,这时就不要采用/comm/**这种方式,否则对无需登录的情况会存在无法访问页面的问题,这时建议要么进行目录分离,要么采用通配符方式,但是java代码里对应的调用方法里一定要增加权限控制判断,见上面的代码,一般对于action层、dwr直接调用的工具类中。
(7)编辑页面的数据权限控制
有的记录只能允许记录的拥有者修改。在openjweb中列表页是有数据权限控制的,比如A公司的商品信息只能由A公司查看,但在编辑页里目前没有数据权限判断,需要增加数据权限控制。例如下面的URL是A公司维护自己公司基本信息的编辑页链接:
http://localhost:8088/portal/comm/listCommCompany!edit.action?selId=c081cc499ff249b191898fc988ec6ede
如果selId后面修改为一个另外一个公司的id,则会存在修改非授权数据的问题。虽然selId后面的公司ROWID很长,但是也存在危险性。所以在编辑页中需要判断此数据是否是存在且属于当前用户有权维护,如果没有权限维护则提示用户无权操作。
(8)对于不方便用/comm/**的情况,例如B2C商城前台未登录的连接和后台登录的连接都有/b2c/开头的,而且URL数量比较多的情况,可以采取这种配置:
定义一个/b2c/listB2cProInfo*.action*的权限,这个权限的排序号应仅低于/b2c/**(但因为/b2c下有未授权可以访问的URL,所以不配置/b2c/**)的排序号,现在要增加一个编辑权限,当没有授予编辑权限则不允许访问商品编辑功能,编辑权限URL为:
/b2c/listB2cProInfo!edit.action*这个权限的排序号要小于/b2c/listB2cProInfo*.action*的排序号。不配置/b2c/**的问题在于不能写精确匹配,例如如果配置/b2c/listB2cProInfo!edit.action但后面不带星号,则/b2c/listB2cProInfo!edit.action后面随便加一个参数就可以绕过权限控制,所以需要写成/b2c/listB2cProInfo!edit.action*的模式。