开放平台如何保证接口安全?

常用解决办法

  • 使用加签名方式,防止篡改数据
  • 使用Https加密传输
  • 搭建OAuth2.0认证授权
  • 使用令牌方式
  • 搭建网关实现黑名单和白名单

令牌方式保证接口安全

  1. 表设计
CREATE TABLE `table_app` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `app_name` varchar(255) NOT NULL COMMENT '三方系统的名称',
  `app_id` varchar(0) NOT NULL COMMENT '应用id',
  `app_secrect` varchar(255) NOT NULL COMMENT '应用秘钥(可更改)',
  `is_deleted` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否可用',
  `last_access_token` varchar(0) NOT NULL DEFAULT '' COMMENT '上一次的access_token',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  1. 代码实现
package com.example.nginx.controller;

import com.example.nginx.service.AppService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AppController {

    @Autowired
    private AppService appService;

    @GetMapping(path = "/getAccessToken")
    public String getAccessToken(String appId, String appSecrect) {
        return appService.doGetAccessToken(appId, appSecrect);
    }

    @GetMapping(path = "/openApi/getResult")
    public String getResult() {
        return "success";
    }
}

@Service
@Transactional(rollbackFor = Exception.class)
public class AppServiceImpl extends ServiceImpl<AppMapper, App> implements AppService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public String doGetAccessToken(String appId, String appSecrect) {
        //查询应用是否存在
        App app = this.getApp(appId);
        //删除旧的AccessToken
        stringRedisTemplate.delete(app.getLastAccessToken());
        //生成新的AccessToken
        String token = TokenUtils.getToken();
        app.setLastAccessToken(token);
        super.updateById(app);
        stringRedisTemplate.opsForValue().set(token, app.getAppId(), 2, TimeUnit.HOURS);
        return token;
    }

    private App getApp(String appId) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("app_id", appId);
        App app = super.getOne(queryWrapper);
        Assert.notNull(app, "该应用不存在或无访问权限");
        return app;
    }

}

package com.example.nginx.aop;

import com.example.nginx.util.AopUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@Configuration
public class AccessTokenInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String accessToken = request.getParameter("accessToken");
        if(StringUtils.isEmpty(accessToken)){
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            AopUtils.returnMsg("accessToken不能为空", attributes);
            return false;
        }
        String value = stringRedisTemplate.opsForValue().get(accessToken);
        if(StringUtils.isEmpty(value)){
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            AopUtils.returnMsg("accessToken不存在或失效", attributes);
            return false;
        }
        return true;
    }
}

@Configuration
public class WebConfig extends WebMvcConfigurationSupport {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
    }

    @Autowired
    private AccessTokenInterceptor accessTokenInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(accessTokenInterceptor).addPathPatterns("/openApi/**");
    }

}

public class AopUtils {

    public static void returnMsg(String msg, ServletRequestAttributes attributes) {
        HttpServletResponse httpServletResponse = attributes.getResponse();
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("application/json; charset=utf-8");
        try (PrintWriter servletOutputStream = httpServletResponse.getWriter();) {
            Map<String, String> result = new HashMap<String, String>() {
                {
                    put("msg", msg);
                }
            };
            servletOutputStream.print(JSON.toJSONString(result));
            servletOutputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
  1. 测试用例
    开放平台如何保证接口安全?_第1张图片
    开放平台如何保证接口安全?_第2张图片
    在这里插入图片描述
    在这里插入图片描述
  2. 遇到的问题
  • 拦截器路径配置规则.
    一个*:只匹配字符,不匹配路径(/)
    两个**:匹配字符,和路径(/)

你可能感兴趣的:(互联网安全架构)