最近的项目在用restful风格在写,果然url都有了意义,功能都可以从url中推测出来,restful的url和非restful的url最大的一个感官区别就是,rest的url可能存在一些变量,比如下面这样:/check/api/user/12345/history,这个url解释起来就是:查看账号为12345的用户的历史资料,而非rest的url是:/check/api/user/history。那么,现在问题就来了,权限控制的核心是判断url,rest的url中却有变量,那么,rest风格的项目如何实现权限控制呢?
其实在我我写这篇文章之前,我的思路是把url的变量去掉,然后存权限表中,然后判断的时候就把访问的url按照相同的规则处理,再在数据库中查,如果查到了代表有权限,反之没有。但事实证明,这种思路只有在变量在url的结尾时才可行。
首先我贴一下权限表的结构,这个是权限控制的核心表。
-- Create table
create table STAFF_POWER
(
stp_id NUMBER not null,
stp_name VARCHAR2(40),
create_time TIMESTAMP(6),
stp_url VARCHAR2(100),
stp_method VARCHAR2(10)
)
tablespace CHECK_ZS
pctfree 10
initrans 1
maxtrans 255
storage
(
initial 64K
next 1M
minextents 1
maxextents unlimited
);
-- Create/Recreate primary, unique and foreign key constraints
alter table STAFF_POWER
add constraint STP_PK primary key (STP_ID)
using index
tablespace CHECK_ZS
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 64K
next 1M
minextents 1
maxextents unlimited
);
alter table STAFF_POWER
add constraint STP_UN unique (STP_URL, STP_METHOD)
using index
tablespace CHECK_ZS
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 64K
next 1M
minextents 1
maxextents unlimited
);
每个字段的意思我解释一下,id是主键,name是这条权限的名字,time是创建时间,由系统自动生成,url是url,method是访问方式。
然后比较关键的一点就来了,url存的时候将变量全部换成%,为什么这样呢,先看下我判断权限的sql:
我用的mybatis。
权限拦截器的核心代码如下:
StaffPower power=powerSer.selectByUrlAndMethod(url, method);
if (power!=null) {
boolean isPass=role.getPowers()!=null && (","+role.getPowers()+",").contains(","+power.getStpId()+",");
if (isPass==false) {
Result result=new Result(BaseRestController.ERROR, Code.ROLE_NO_PERMISSION, "您没有权限,请联系管理员");
PrintWriter pw=resp.getWriter();
pw.print(gson.toJson(result));
pw.flush();
pw.close();
return false;
}
return isPass;
}else{
log.error("没有这个权限 "+url+" "+method);
Result result=new Result(BaseRestController.ERROR, Code.PERMISSION_NO_EXIST, "该模块还没有设计权限,暂时不能操作");
PrintWriter pw=resp.getWriter();
pw.print(gson.toJson(result));
pw.flush();
pw.close();
return false;
}
现在数据库中有这样一条权限:
我现在想看历史,于是访问 /check/api/sourceTp/3803140612558/history,然后sql是怎么执行的呢,下面是执行的结果:
[2017-05-11 10:09:54.400] - [DEBUG] [http-nio-8080-exec-9 :17561] ==> Preparing: select * from STAFF_POWER WHERE STP_METHOD = ? and ? like STP_URL
[2017-05-11 10:09:54.400] - [DEBUG] [http-nio-8080-exec-9 :17561] ==> Parameters: GET(String), /check/api/sourceTp/3803140612558/history(String)
[2017-05-11 10:09:54.403] - [DEBUG] [http-nio-8080-exec-9 :17564] <== Total: 1
看到这里,它的原理应该就很清楚了,权限控制核心是url+method的判断,而rest的url比较特别从而导致无法像原来那样判断,于是我把所有的变量替换为%,而%是sql模糊查询的符号,所以就刚好可以借用sql的模糊查询来完成判断(中间不做任何处理)。
判断的核心是url+method,但最最核心(而且好多人都没想到这种方法)的是使用%,%代表任意。