目录
前言
数据接口推送是开发中最常见的问题。也有很多实现手段,比如基于webservice,如果实时性要求很高的也可以使用MQ等。当然也可以使用传统的基于http请求的。而这里采用基于http请求的,数据返回格式统一采用restful形式
整体设计
为了承接job功能利用httpClient拉取接口,这里简化需求,模型和其他干扰因素。即按照分页的模式读取表中的符合的数据并实现接口暴露给外部,同时每一次拉取数据都要校验其合法性。不妨先鸟瞅整体一下,如图所示
表设计
类OAuth2的用户名密码模式,我们需要一个用户认证的信息记录表,如果合法,每个用户获取其有效的access_token
api_user(用户表)
CREATE TABLE `api_user` (
`user_id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户id',
`user_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户名',
`pass_word` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '密码',
`salt` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '盐值',
`status` int(4) NULL DEFAULT NULL,
PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;
除此之外,我们还需要对每个用户的token信息进行记录,并且约定每个用户只会有一个token记录,token只会对当日有效
api_user_token(用户token记录表)
CREATE TABLE `test`.`api_user_token` (
`user_id` varchar(36) NOT NULL COMMENT '用户id',
`start_time` datetime(0) NULL COMMENT '授权开始时间',
`end_time` datetime(0) NULL COMMENT '授权结束时间',
`auth_time` datetime(0) NULL COMMENT '授权时间',
`access_token` varchar(255) NULL COMMENT 'Token令牌',
`token_create_time` datetime(0) NULL COMMENT 'Token生成时间',
`token_end_time` datetime(0) NULL COMMENT 'Token失效时间',
`token_count` int(10) NULL COMMENT '当日token生成次数',
`last_update_time` datetime(0) NULL COMMENT '最后更新时间',
`last_update_user` varchar(255) NULL COMMENT '最后更新人',
PRIMARY KEY (`user_id`)
) COMMENT = '用户token记录表';
事实上该记录远不止这些,这里仅仅做了简化,其中start_time与end_time控制用户访问接口的整个有效期,token_end_time用来控制token的有效期,其他作为记录。
但是这里仅仅只要求token当日有效,并且每次访问只能是携带该表中最新token才放行访问推送接口(token接口不做限制)
权限设计
上面是系统权限表设计,主要还是采用OAuth2用户名密码模式思想。当然随之而来的就是用户业务权限,也就是用户会有哪些接口的权限,这里简化,即接口服务表api_auth与映射表api_user_auth,最后控制用户可以访问哪些接口
api_auth(接口服务表)
CREATE TABLE `test`.`api_auth` (
`service_id` varchar(36) NOT NULL COMMENT '服务ID',
`service_name` varchar(255) NULL COMMENT '服务名称',
`service_remark` varchar(255) NULL COMMENT '服务说明',
`service_url` varchar(255) NULL COMMENT '服务相对路径',
`status` int(4) NULL COMMENT '服务状态',
PRIMARY KEY (`service_id`)
) COMMENT = '接口服务表';
该表作为接口服务基础表,其中service_url是作为服务请求是否正确的依据
api_user_auth(用户接口权限表)
CREATE TABLE `test`.`Untitled` (
`user_id` varchar(36) NOT NULL COMMENT '用户id',
`service_id` varchar(255) NULL COMMENT '服务ID',
PRIMARY KEY (`user_id`)
) COMMENT = 'api_user_auth';
很显然通过基础接口服务与映射表可以很好的控制业务权限
模式设计
基本权限思路已经完成,很显然就是如何设计使代码的可读性,可扩展性与可维护性都很好了。
很显然我们这里可以抽象为校验与数据处理,而校验的行为都一样,只是不同业务的行为(如不同接口读取数据不一样),所以模板设计模式就可以解决。不过还是推荐使用工厂方法模式+策略模式去扩展,因为可能针对不同的大的分类业务会有不同的策略去处理
话不多说,如下图所示
码上有戏
如图,为项目代码结构
核心代码
由于http是无状态的请求,所以每次请求需要权限控制,通过shiro控制权限,除了gettoken放行外,其余请求都JwtFilter去拦截,判断token是否合法
ShiroConfig
@Bean("shiroFilter")
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
Map<String, Filter> filters = new HashMap<>();
filters.put("oauth2", new JwtFilter());
shiroFilter.setFilters(filters);
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/api/study/gettoken", "anon");
filterMap.put("/**", "oauth2");
shiroFilter.setFilterChainDefinitionMap(filterMap);
return shiroFilter;
}
JwtRealm进行授权和身份认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
JwtToken jwtToken = (JwtToken) token;
if (jwtToken == null) {
throw new AuthenticationException("token不能为空");
}
String accessToken = (String) token.getPrincipal();
ApiUserToken userToken = apiUserTokenService.getOne(new QueryWrapper<ApiUserToken>().eq("access_token", accessToken));
//判断token是否失效
boolean flag = userToken == null || userToken.getTokenEndTime().getTime() < System.currentTimeMillis();
if (flag) {
throw new IncorrectCredentialsException("token失效");
}
ApiUser user = apiUserService.getById(userToken.getUserId());
if (user.getStatus() == 0) {
throw new LockedAccountException("账号已被禁用,请联系管理员");
}
String salt = user.getSalt();
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, accessToken, ByteSource.Util.bytes(salt), getName());
return info;
}
RequestModel与ResponseModel作为最顶层接受与返回类,抽象共有参数
AbstractApiRequest为抽象一套模板,主要为校验token和权限,之后子类ApiXXRequest去实现action方法,并且转型为子类的xxRequestModel与xxResponseModel返回数据
postman测试
至此我们可以扩展推送功能,比如增加一个apiXXrequest与表,从而推送业务数据
1,获取token测试
get请求输入 http://localhost:8087/api/study/gettoken?username=admin&password=123456
2 获取api_leesin推送数据
http://localhost:8087/api/study/getinformation/leesinlist?token=最新token
然后需要携带分页参数:{“data”:"{“pageindex”:“1”}"}
3 获取api_yurnero推送数据 同二步骤
http://localhost:8087/api/study/getinformation/yurnerolist?token=最新token