幂等注解使用
// 从redis中获取Token @RequestMapping("/redisToken") public String RedisToken() { return redisTokenUtils.getToken(); } // 验证Token @RequestMapping(value = "/addOrderExtApiIdempotent", produces = "application/json; charset=utf-8") @ExtApiIdempotent public String addOrderExtApiIdempotent(@RequestBody OrderEntity orderEntity, HttpServletRequest request) { int result = orderMapper.addOrder(orderEntity); return result > 0 ? "添加成功" : "添加失败" + ""; }封装生成token注解
@Target(value = ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface ExtApiToken { }改造ExtApiAopIdempotent,添加如下方法:
// 前置通知转发Token参数 @Before("rlAop()") public void before(JoinPoint point) { MethodSignature signature = (MethodSignature) point.getSignature(); ExtApiToken extApiToken = signature.getMethod().getDeclaredAnnotation(ExtApiToken.class); if (extApiToken != null) { extApiToken(); } }API接口保证幂等性
@RestController public class OrderController { @Autowired private OrderMapper orderMapper; @Autowired private RedisTokenUtils redisTokenUtils; // 从redis中获取Token @RequestMapping("/redisToken") public String RedisToken() { return redisTokenUtils.getToken(); } // 验证Token @RequestMapping(value = "/addOrderExtApiIdempotent", produces = "application/json; charset=utf-8") @ExtApiIdempotent(value = ConstantUtils.EXTAPIHEAD) public String addOrderExtApiIdempotent(@RequestBody OrderEntity orderEntity, HttpServletRequest request) { int result = orderMapper.addOrder(orderEntity); return result > 0 ? "添加成功" : "添加失败" + ""; } }页面防止重复提交
@Controller public class OrderPageController { @Autowired private OrderMapper orderMapper; @RequestMapping("/indexPage") @ExtApiToken public String indexPage(HttpServletRequest req) { return "indexPage"; } @RequestMapping("/addOrderPage") @ExtApiIdempotent(value = ConstantUtils.EXTAPIFROM) public String addOrder(OrderEntity orderEntity) { int addOrder = orderMapper.addOrder(orderEntity); return addOrder > 0 ? "success" : "fail"; } } 三、API安全接口安全设计 3.1 互联网开放平台设计需求:现在A公司与B公司进行合作,B公司需要调用A公司开放的外网接口获取数据,
如何保证外网开放接口的安全性。常用解决办法:
1、搭建API网关控制接口访问权限,实现黑名单和白名单
2、开放平台设计,搭建OAuth2.0协议认证授权,(QQ授权,第三方联合登录)
3、使用Https加密传输协议
4、使用加签名方式,防止篡改数据(API接口数字签名)
5、使用令牌方式实现API接口调用,基于accessToken实现API调用
3.2 使用令牌方式搭建搭建API开放平台原理:为每个合作机构创建对应的appid、app_secret,生成对应的access_token(有效期2小时),在调用外网开放接口的时候,必须传递有效的access_token。
数据库表设计
CREATE TABLE `m_app` ( `id` int(11) NOT NULL AUTO_INCREMENT, `app_name` varchar(255) DEFAULT NULL, `app_id` varchar(255) DEFAULT NULL, `app_secret` varchar(255) DEFAULT NULL, `is_flag` varchar(255) DEFAULT NULL, `access_token` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;字段解释
App_Name 表示机构名称 App_ID 应用id App_Secret 应用密钥 (可更改) Is_flag 是否可用 (是否对某个机构开放) access_token 上一次access_token获取AccessToken
// 创建获取getAccessToken @RestController @RequestMapping(value = "/auth") public class AuthController extends BaseApiService { @Autowired private BaseRedisService baseRedisService; private long timeToken = 60 * 60 * 2; @Autowired private AppMapper appMapper; // 使用appId+appSecret 生成AccessToke @RequestMapping("/getAccessToken") public ResponseBase getAccessToken(AppEntity appEntity) { AppEntity appResult = appMapper.findApp(appEntity); if (appResult == null) { return setResultError("没有对应机构的认证信息"); } int isFlag = appResult.getIsFlag(); if (isFlag == 1) { return setResultError("您现在没有权限生成对应的AccessToken"); } // ### 获取新的accessToken 之前删除之前老的accessToken // 从redis中删除之前的accessToken String accessToken = appResult.getAccessToken(); baseRedisService.delKey(accessToken); // 生成的新的accessToken String newAccessToken = newAccessToken(appResult.getAppId()); JSONObject jsonObject = new JSONObject(); jsonObject.put("accessToken", newAccessToken); return setResultSuccessData(jsonObject); } private String newAccessToken(String appId) { // 使用appid+appsecret 生成对应的AccessToken 保存两个小时 String accessToken = TokenUtils.getAccessToken(); // 保证在同一个事物redis 事物中 // 生成最新的token key为accessToken value 为 appid baseRedisService.setString(accessToken, appId, timeToken); // 表中保存当前accessToken appMapper.updateAccessToken(accessToken, appId); return accessToken; } }