博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
互联网开放平台API安全设计
阅读量:4880 次
发布时间:2019-06-11

本文共 13894 字,大约阅读时间需要 46 分钟。

互联网开放平台设计

1.需求:现在A公司与B公司进行合作,B公司需要调用A公司开放的外网接口获取数据,
如何保证外网开放接口的安全性。
2.常用解决办法:
2.1 使用加签名方式,防止篡改数据
2.2 使用Https加密传输
2.3 搭建OAuth2.0认证授权
2.4 使用令牌方式
2.5 搭建网关实现黑名单和白名单

 

案例:A公司调用B公司的外网接口获取数据,如何保证外网开放接口的安全性。

  

如何保证外网开放接口的安全性:

 1.搭建API网关控制接口访问权限

 2.开放平台设计  开放凭他设计oauth2.0协议   QQ授权 第三方联合登录

 3.采用Https加密传输协议 (使用Nginx配置Https)

 4.API接口数字签名(移动的接口)非对称加密RSA   

 5. 基于令牌方式实现API接口调用。基于accessToken实现API调用   防止抓包分析篡改数据  基于accessToken实现AP调用

 

 

基于令牌方式实现:

 表的设计:

  开放平台提供者需要为每个合作机构提供对应的APPID   APPSerect 

  基于令牌方式实现   AppId区分不同结构 永远不能变   AppSerect 在传输中实现加密功能(密钥) 可以发生改变   Appid+AppSerect生成对应access_token

 

表字段:

  App_Name 表机构名称

  App_ID  应用id 

  App_Serect  应用密钥(可更改)

  Is_flag  是否可用 

  acess_token  上一次access_token

 

开发步骤:

  Appid+AppSerect 对应生成accessToken 

  对应accessToken去表里查询。查询出的结果要求is_flag 是1, 0 代表不开放权限了

  

 

 

使用令牌方式搭建搭建API开放平台

原理:为每个合作机构创建对应的appid、app_secret,生成对应的access_token(有效期2小时),在调用外网开放接口的时候,必须传递有效的access_token。

 

 生成accessToken之后

 用accessToken对接口进行调用 此时会被 配置  API/* 拦截到,获取到携带哦的accessToken,然后去redis中查询对应的appId,如果redis中有对应的appId,利用appId去表中查询对应的app信息

 信息包括isFlag 看权限是否开启 如果开启 继续往下走

 放行到被拦截的 业务逻辑中

 

 

生成accessToken:

 controller:

  

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import com.alibaba.fastjson.JSONObject;import com.itmayiedu.base.BaseApiService;import com.itmayiedu.base.ResponseBase;import com.itmayiedu.entity.AppEntity;import com.itmayiedu.mapper.AppMapper;import com.itmayiedu.utils.BaseRedisService;import com.itmayiedu.utils.TokenUtils;// 创建获取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);  //key为accessToken          // 表中保存当前accessToken        appMapper.updateAccessToken(accessToken, appId);        return accessToken;    }}

去调用API接口:

拦截器去进行处理验证:

import java.io.IOException;import java.io.PrintWriter;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.commons.lang.StringUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import com.alibaba.fastjson.JSONObject;import com.itmayiedu.base.BaseApiService;import com.itmayiedu.utils.BaseRedisService;//验证AccessToken 是否正确@Componentpublic class AccessTokenInterceptor extends BaseApiService implements HandlerInterceptor {    @Autowired    private BaseRedisService baseRedisService;        public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o)            throws Exception {        System.out.println("---------------------开始进入请求地址拦截----------------------------");        String accessToken = httpServletRequest.getParameter("accessToken");        // 判断accessToken是否空        if (StringUtils.isEmpty(accessToken)) {            // 参数Token accessToken            resultError(" this is parameter accessToken null ", httpServletResponse);            return false;        }        String appId = (String) baseRedisService.getString(accessToken);        if (StringUtils.isEmpty(appId)) {            // accessToken 已经失效!            resultError(" this is  accessToken Invalid ", httpServletResponse);            return false;        }        // 正常执行业务逻辑...        return true;    }    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o,            ModelAndView modelAndView) throws Exception {        System.out.println("--------------处理请求完成后视图渲染之前的处理操作---------------");    }    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,            Object o, Exception e) throws Exception {        System.out.println("---------------视图渲染之后的操作-------------------------0");    }    // 返回错误提示    public void resultError(String errorMsg, HttpServletResponse httpServletResponse) throws IOException {        PrintWriter printWriter = httpServletResponse.getWriter();        printWriter.write(new JSONObject().toJSONString(setResultError(errorMsg)));    }}

拦截器的配置类:

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configurationpublic class WebAppConfig {    @Autowired    private AccessTokenInterceptor accessTokenInterceptor;    @Bean    public WebMvcConfigurer WebMvcConfigurer() {        return new WebMvcConfigurer() {            public void addInterceptors(InterceptorRegistry registry) {                registry.addInterceptor(accessTokenInterceptor).addPathPatterns("/openApi/*");            };        };    }}

 

接口controller:

import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import com.itmayiedu.base.BaseApiService;import com.itmayiedu.base.ResponseBase;@RestController@RequestMapping("/openApi")public class MemberController extends BaseApiService {    @RequestMapping("/getUser")    public ResponseBase getUser() {        return setResultSuccess("获取会员信息接口");    }}

 

Base类:

@Componentpublic class BaseApiService {    public ResponseBase setResultError(Integer code, String msg) {        return setResult(code, msg, null);    }    // 返回错误,可以传msg    public ResponseBase setResultError(String msg) {        return setResult(Constants.HTTP_RES_CODE_500, msg, null);    }    // 返回成功,可以传data值    public ResponseBase setResultSuccessData(Object data) {        return setResult(Constants.HTTP_RES_CODE_200, Constants.HTTP_RES_CODE_200_VALUE, data);    }    public ResponseBase setResultSuccessData(Integer code, Object data) {        return setResult(code, Constants.HTTP_RES_CODE_200_VALUE, data);    }    // 返回成功,沒有data值    public ResponseBase setResultSuccess() {        return setResult(Constants.HTTP_RES_CODE_200, Constants.HTTP_RES_CODE_200_VALUE, null);    }    // 返回成功,沒有data值    public ResponseBase setResultSuccess(String msg) {        return setResult(Constants.HTTP_RES_CODE_200, msg, null);    }    // 通用封装    public ResponseBase setResult(Integer code, String msg, Object data) {        return new ResponseBase(code, msg, data);    }}

 

package com.itmayiedu.base;import lombok.Getter;import lombok.Setter;import lombok.extern.slf4j.Slf4j;@Getter@Setter@Slf4jpublic class ResponseBase {    private Integer rtnCode;    private String msg;    private Object data;    public ResponseBase() {    }    public ResponseBase(Integer rtnCode, String msg, Object data) {        super();        this.rtnCode = rtnCode;        this.msg = msg;        this.data = data;    }    public static void main(String[] args) {        ResponseBase responseBase = new ResponseBase();        responseBase.setData("123456");        responseBase.setMsg("success");        responseBase.setRtnCode(200);        System.out.println(responseBase.toString());        log.info("itmayiedu...");    }    @Override    public String toString() {        return "ResponseBase [rtnCode=" + rtnCode + ", msg=" + msg + ", data=" + data + "]";    }}

entity:表的实体类

public class AppEntity {    private long id;    private String appId;    private String appName;    private String appSecret;    private String accessToken;    private int isFlag;    public long getId() {        return id;    }    public void setId(long id) {        this.id = id;    }    public String getAppId() {        return appId;    }    public void setAppId(String appId) {        this.appId = appId;    }    public String getAppName() {        return appName;    }    public void setAppName(String appName) {        this.appName = appName;    }    public String getAppSecret() {        return appSecret;    }    public void setAppSecret(String appSecret) {        this.appSecret = appSecret;    }    public int getIsFlag() {        return isFlag;    }    public void setIsFlag(int isFlag) {        this.isFlag = isFlag;    }    public String getAccessToken() {        return accessToken;    }    public void setAccessToken(String accessToken) {        this.accessToken = accessToken;    }}

Mapper:

 

ublic interface AppMapper {    @Select("SELECT ID AS ID ,APP_NAME AS appName, app_id as appId, app_secret as appSecret ,is_flag as isFlag , access_token as accessToken from m_app "            + "where app_id=#{appId} and app_secret=#{appSecret}  ")    AppEntity findApp(AppEntity appEntity);    @Select("SELECT ID AS ID ,APP_NAME AS appName, app_id as appId, app_secret as appSecret ,is_flag as isFlag  access_token as accessToken from m_app "            + "where app_id=#{appId} and app_secret=#{appSecret}  ")    AppEntity findAppId(@Param("appId") String appId);    @Update(" update m_app set access_token =#{accessToken} where app_id=#{appId} ")    int updateAccessToken(@Param("accessToken") String accessToken, @Param("appId") String appId);}

 

 Utils类:

 

import java.util.concurrent.TimeUnit;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Component;@Componentpublic class BaseRedisService {    @Autowired    private StringRedisTemplate stringRedisTemplate;    public void setString(String key, Object data, Long timeout) {        if (data instanceof String) {            String value = (String) data;            stringRedisTemplate.opsForValue().set(key, value);        }        if (timeout != null) {            stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);        }    }    public Object getString(String key) {        return stringRedisTemplate.opsForValue().get(key);    }    public void delKey(String key) {        stringRedisTemplate.delete(key);    }}
public interface Constants {    // 响应请求成功    String HTTP_RES_CODE_200_VALUE = "success";    // 系统错误    String HTTP_RES_CODE_500_VALUE = "fial";    // 响应请求成功code    Integer HTTP_RES_CODE_200 = 200;    // 系统错误    Integer HTTP_RES_CODE_500 = 500;    // 未关联QQ账号    Integer HTTP_RES_CODE_201 = 201;    // 发送邮件    String MSG_EMAIL = "email";    // 会员token    String TOKEN_MEMBER = "TOKEN_MEMBER";    // 支付token    String TOKEN_PAY = "TOKEN_pay";    // 支付成功    String PAY_SUCCESS = "success";    // 支付白    String PAY_FAIL = "fail";    // 用户有效期 90天    Long TOKEN_MEMBER_TIME = (long) (60 * 60 * 24 * 90);    int COOKIE_TOKEN_MEMBER_TIME = (60 * 60 * 24 * 90);    Long PAY_TOKEN_MEMBER_TIME = (long) (60 * 15);    // cookie 会员 totoken 名称    String COOKIE_MEMBER_TOKEN = "cookie_member_token";}

 

public class TokenUtils {    @RequestMapping("/getToken")    public static String getAccessToken() {        return UUID.randomUUID().toString().replace("-", "");    }}

yml:

spring:  mvc:    view:      # 页面默认前缀目录      prefix: /WEB-INF/jsp/      # 响应页面默认后缀      suffix: .jspspring:  datasource:    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8    username: root    password: root    driver-class-name: com.mysql.jdbc.Driver    test-while-idle: true    test-on-borrow: true    validation-query: SELECT 1 FROM DUAL    time-between-eviction-runs-millis: 300000    min-evictable-idle-time-millis: 1800000  redis:    database: 1    host: 106.15.185.133    port: 6379    password: meitedu.+@    jedis:      pool:        max-active: 8        max-wait: -1        max-idle: 8        min-idle: 0    timeout: 10000

 

表单设计: 

  

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。 如果获取最新的accessToken(使用appid+appsecret 生成对应的AccessToken 保存两个小时   保证唯一且临时),需要从redis删除原先的accessTokne。

           通过两个字段去查询:

           

 

            第二步去调用接口时候 会去拦截器进行限制 ,从redis获取appId,然后去app表中获取信息,看看是否有权限。

   

 

原理:为每个合作机构创建对应的appid、app_secret,生成对应的access_token(有效期2小时),在调用外网开放接口的时候,必须传递有效的access_token。

 

 

 

 

 

 

 

  

转载于:https://www.cnblogs.com/toov5/p/10313818.html

你可能感兴趣的文章
百度外卖 前端面试题
查看>>
record for json formate site
查看>>
查询树形的根节点
查看>>
HDU 1272 小希的迷宫
查看>>
hdu 5412 CRB and Queries(整体二分)
查看>>
CentOS如何安装linux桌面?
查看>>
Speech and Booth Demo in Maker Faire Shenzhen 2018
查看>>
bzoj 1670: [Usaco2006 Oct]Building the Moat护城河的挖掘
查看>>
bzoj 2281: [Sdoi2011]黑白棋
查看>>
bzoj 4475: [Jsoi2015]子集选取
查看>>
团队开发7
查看>>
java之静态代理与动态代理
查看>>
软件测试2019:第四次作业
查看>>
201571030335 + 小学四则运算练习软件项目报告
查看>>
不用代码就能实现get与post
查看>>
gdb基本调试命令
查看>>
互联网开放平台API安全设计
查看>>
OPMN
查看>>
LOG收集系统(一):原日志至收集
查看>>
【文摘】经营十二条
查看>>