前言
最近的项目在用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
- );
然后比较关键的一点就来了,url存的时候将变量全部换成%,为什么这样呢,先看下我判断权限的sql:
- <select id="selectByUrlAndMethod" resultMap="BaseResultMap" parameterType="java.lang.String">
- select *
- from STAFF_POWER
- <where>
- and STP_METHOD = #{method}
- and #{url} like STP_URL
- </where>
- </select>
权限拦截器的核心代码如下:
- StaffPower power=powerSer.selectByUrlAndMethod(url, method);
- if (power!=null) {
- boolean isPass=role.getPowers()!=null && (","+role.getPowers()+",").contains(","+power.getStpId()+",");
- if (isPass==false) {
- Result<String> result=new Result<String>(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<String> result=new Result<String>(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,但最最核心(而且好多人都没想到这种方法)的是使用%,%代表任意。