分布式自定义权限控制-01 JSP页面权限控制
本文主体概要:通过新建一个自定义JSP标签,完成对于JSP页面权限控制,并已考虑相关的扩展和实用性等问题,包括相关代码和结果展示实例。
如果提起正常一个java EE项目,权限控制是必不可少的一环。无论干嘛都绕不过权限控制一说。比如,没有权限的人不能发起页面请求,ajax请求,比如JSP页面有权限的人和没有权限的用户,是两种展现。有些人有查询按钮,有些人没有查询按钮等。
如上方提到的相关权限控制是在平常不过的要求了。页面请求,和ajax请求一般是可以通过Spring 或者 structs 的拦截器,对相关请求地址进行比对,如果没有相关权限,则抛到没有权限的页面。
但是在动态页面上的权限怎么处理呢,总不可能每个权限的人都有不同的页面吧,或者用js 动态组装jsp页面?这也太蠢了吧。在以前我经历过的做法是用 shiro 可是shiro 也有shiro的问题,第一是,个人觉得在没有一个对一个框架有着详细了解和测评的情况下引入一个不小的框架,不是一个好事情 ,第二是,不同人的学习时间和框架可能出现的坑也是一个要考虑的问题。第三是,如果有一个能简单实现我想要的JSP页面权限控制的方法,为什么要要引入一个厚重的框架呢,不够帅气。
so,记录一下才实践完的一个JSP页权限控制方法
正文
1.数据库建表
对于一个正常的互联网项目,想要控权限,那肯定要有个粒度的,在正常情况下,达到最小粒度为"请求级",我觉得确实也够了。所谓”请求级“ 就是指每一个请求地址(页面跳转或ajax请求)都能进行权限控制,而且我们可以灵活的修改和给用户进行权限分配操作。这种灵活的操作,就需要维护几张表来处理了。其实怎么建表还是要看需求的,但是最简单的情况下,三张表就可以解决问题.
tb_person(用户表)
DROP TABLE IF EXISTS `tb_person`;
CREATE TABLE `tb_person` (
`id` int(50) NOT NULL,
`user_name` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`init_ip` varchar(255) DEFAULT NULL,
`guess_place_info` varchar(255) DEFAULT NULL,
`phone_number` varchar(20) DEFAULT NULL,
`sex` tinyint(2) DEFAULT NULL,
`remake` varchar(500) DEFAULT NULL,
`realName` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
tb_authority_func (权限方法表)
DROP TABLE IF EXISTS `tb_authority_func`;
CREATE TABLE `tb_authority_func` (
`id` int(11) NOT NULL,
`parent_id` int(11) NOT NULL,
`function_code` varchar(255) NOT NULL,
`function_name` varchar(255) NOT NULL,
`function_method` varchar(255) NOT NULL,
`status` varchar(255) NOT NULL,
`detail` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
tb_person_func (用户-权限关系表)
DROP TABLE IF EXISTS `tb_person_func`;
CREATE TABLE `tb_person_func` (
`id` int(11) NOT NULL,
`person_id` int(11) DEFAULT NULL,
`authority_func_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `person_id` (`person_id`),
KEY `authority_func_id` (`authority_func_id`),
CONSTRAINT `authority_func_id` FOREIGN KEY (`authority_func_id`) REFERENCES `tb_authority_func` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `person_id` FOREIGN KEY (`person_id`) REFERENCES `tb_person` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
用户表用来限制用户登陆的账号密码相关,权限方法表用来保存,限制权限的相关信息,parent_id字段是为了方便后期扩展权限树。比如,”报表查询“业务的权限id是1,则”报表查询-报表导出“业务的parent_id是1。function_code为了限定权限的代码,function_name是对该权限的展示名,function_method是对应的请求相对地址,如:”/transfer/queryList“(该字段在jsp权限控制中没用,但在其他交互权限控制中有用)
mapper:
public interface PersonFuncMapper {
public Integer cheakAuthority(@Param(value = "personId") String personId,@Param(value = "functionCode") String functionCode);
}
mapper.xml
<select id="cheakAuthority" resultType="java.lang.Integer">
SELECT count(1) FROM `tb_person_func` pf LEFT JOIN tb_authority_func f ON pf.authority_func_id= f.id
WHERE pf.person_id=#{personId} AND f.function_code=#{functionCode}
</select>
下面就是实现的关键了
其实JSP在到达每个JSP页面的时候,包括每个标签内部内容的展示,有提供相应的扩展接口,BodyTagSupport。
具体关于该类的扩展不说太多,按照此次提起的JSP 按钮相应的权限控制,只用继承BodyTagSupport,重写他的,doStartTag,该方法能决定一个标签内部的内容,是否显现。EVAL_BODY_INCLUDE为包含标签内部,SKIP_BODY为跳过标签内部。对于逻辑来说,就是在jsp页面包装一个标签,内部传递function_code和person_id,去用户-权限关系表中查找,如果没有找到,则认为,该用户,没有该方法权限,那么标签内部的内容将不会显现
该类我的测试代码如下(PersonService的cheak...方法就是mapper.cheakAuthority方法的直接返回,没有别的逻辑,只是为了非接口类不和mapper直接联系):
@Component
public class JspAuthorityTag extends BodyTagSupport {
private static final long serialVersionUID = 5837544810528405265L;
private String personId;
private String code;
public int doStartTag() {
PersonService personService =(PersonService) BeanFactoryUtils.getApplicationContext().getBean("personServiceImpl");
int cheak = personService.cheakPersonAuthority(this.personId, this.code);
if (cheak > 0) {
return EVAL_BODY_INCLUDE;
} else {
return SKIP_BODY;
}
}
public String getPersonId() {
return personId;
}
public void setPersonId(String personId) {
this.personId = personId;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
java代码完成后,现在需要做的就是,设置一个tag配置文件,并在有需要的jsp文件头上设置映射。
在WEB-INF下新建一个tld文件夹,里面新建一个zxyAuthority.tld文件,相关内容如下
<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<description>zxy define tag library</description>
<display-name>zxy tablib</display-name>
<tlib-version>1.1</tlib-version>
<short-name>zxy</short-name>
<uri>http://www.zxy.com.cn/zxyProject</uri>
<tag>
<description>query user auth</description>
<name>authBody</name>
<tag-class>com.ssm.Interceptor.JspAuthorityTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<description>tag code</description>
<name>code</name>
<required>true</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description>tag personId</description>
<name>personId</name>
<required>true</required>
<type>java.lang.Integer</type>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
也不用过多介绍,这个标签内部项一看就懂,其中tag-class要对应处理标签类,rtexprvalue是jsp页面是否能动态传值的设置项,为personId项,因为是每个登陆者ID不一样,所以肯定是要在jsp页面动态传的,此处设为true
最后一步,在想要引用自定义权限标签的jsp头,加上
<%@ taglib uri="http://www.zxy.com.cn/zxyProject" prefix="zxy"%>
uri和配置文件对应。并在想要控权限的地方加上标签就可以达到效果了。
测试
tb_authority_func表新增一条数据
INSERT INTO `tb_authority_func` (`id`, `parent_id`, `function_code`, `function_name`, `function_method`, `status`, `detail`) VALUES ('1', '0', 'RG000001', '注册权限', '/*', '01', NULL);
tb_person和tb_person_func按各自表情况,也添加相应数据,并在tb_person_func建立映射关系
html 部分代码(我的测试项目是登陆成功后,把找到的Person对象,放在session里,保存名字为user,所以这里的user.id,就是登陆成功后的person.id):
<zxy:authBody code="RG000001" personId="${user.id}">
<button class="layui-btn layui-btn-normal layui-btn-radius"
type="button" onclick="registerForm()">注册(权限控制测试按钮)</button>
</zxy:authBody>
tb_person_func 添加一条有效的personId和authority的映射
相关页面展示结果:
再将tb_person_func 的映射删掉,使cheakPersonAuthority方法返回值为0
访问结果为:
这样就完成了JSP页面的权限控制,由于用过继承BodyTagSupport的方法实现JSP页面的权限控制其实改动很小,而且很灵活,我觉得是一种非常好的控制权限的方法。
至于更多的需求,比如,对权限的增删改查,权限树相关的展现,角色的赋予和维护,ajax或者页面跳转的权限控制,那就是按所需需求而言的另外的设计了,不在本次JSP页面权限控制之中。
上一篇: 数据库中的行列转换
下一篇: 2017第八届蓝桥杯决赛 磁砖样式