目 录CONTENT

文章目录

Swaager

Josue
2022-03-16 / 0 评论 / 0 点赞 / 161 阅读 / 2,413 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-11-10,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

接口文档

  • API一定需要开发文档配合,移动端只需要根据开发文档进行开发即可;

  • 传统的开发文档问题:格式随意,更新不及时;

一、 ShowDoc

一个非常适合IT团队的 API文档、技术文档工具

https://www.showdoc.cc/

二、 swagger2

Swagger能够根据代码中的注解自动生成api文档,并且提供测试接口

2.1、依赖

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

2.2、示例

@Configuration
@EnableSwagger2
public class SwaggerConfig implements WebMvcConfigurer {

    @Bean
    public Docket productApi() {

        //添加head参数start
        ParameterBuilder tokenPar = new ParameterBuilder();
        List<Parameter> pars = new ArrayList<Parameter>();
        tokenPar.name("token").description("令牌").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
        pars.add(tokenPar.build());

        return new Docket(DocumentationType.SWAGGER_2).select()
                // 扫描的包路径
                .apis(RequestHandlerSelectors.basePackage("cn.wolfcode.luowowo.controller"))
                // 定义要生成文档的Api的url路径规则
                .paths(PathSelectors.any())
                .build()
                .globalOperationParameters(pars)
                // 设置swagger-ui.html页面上的一些元素信息。
                .apiInfo(metaData());
    }

    private ApiInfo metaData() {
        return new ApiInfoBuilder()
                // 标题
                .title("SpringBoot集成Swagger2")
                // 描述
                .description("骡窝窝项目接口文档")
                // 文档版本
                .version("1.0.0")
                .license("Apache License Version 2.0")
                .licenseUrl("https://www.apache.org/licenses/LICENSE-2.0")
                .build();
    }

    //ui页面
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");

        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
}

2.3、访问

http://localhost:8080/swagger-ui.html

三、 Knife4j接口文档API

这个更加强大,注解和swagger2都一样

1.3 Spring Boot 框架集成Knife4j | knife4j (xiaominfo.com)

四、 常见注解标签(Swagger2)

4.1、@Api/@ApiOperation

  • @Api:用在类上,说明该类的作用
  • @Api(value = “用户资源”,description = “用户资源控制器”)
  • @ApiOperation:用在方法上,说明方法的作用
  • @ApiOperation(value = “注册功能”,notes = “其实就是新增用户”)

4.2、@ApiImplicitParams/@ApiImplicitParam

@ApiImplicitParams:用在方法上包含一组参数说明
@ApiImplicitParam:用在@ApiImplicitParams注解中,指定一个请求参数的各个方面
paramType:参数放在哪个地方
header-->请求参数的获取
query-->请求参数的获取
path-->请求参数的获取(用于restful接口):
body-->请求实体中


@ApiImplicitParams({
    @ApiImplicitParam(value = "昵称",name = "nickName",dataType = "String",required = true),
    @ApiImplicitParam(value = "邮箱",name = "email",dataType = "String",required = true),
    @ApiImplicitParam(value = "密码",name = "password",dataType = "String",required = true)
})

4.3、@ApiModel/@ApiModelProperty

  • @ApiModel:描述一个Model的信息(这种一般用在post创建的时候,使用@RequestBody这样的场景,请求参数无法使用@ApiImplicitParam注解进行描述的时候)
  • @ApiModelProperty:描述一个model的属性
  • @ApiModel(value=“用户”,description=“平台注册用户模型”)
  • @ApiModelProperty(value=“昵称”,name=“nickName”,dataType = “String”,required = true)

4.4、@ApiResponses/@ApiResponse

  • @ApiResponses:用于表示一组响应
  • @ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息(200相应不写在这里面)
    • code:数字,例如400
    • message:信息,例如"请求参数没填好"
    • response:抛出的异常类
@ApiResponses({
	@ApiResponse(code=200,message="用户注册成功")
})

4.5、@ApiIgnore

有些接口不想显示,就贴上去,可以贴在类上,也可以贴在方法上。

五、 接口安全

api接口分类:

  • 公共接口:你查快递,你查天气预报,你查飞机,火车班次等,这些都是有公共的接口
  • 私密接口:需要登录访问或者公司内部接口

接口安全要求:

  1. 防伪装攻击(案例:在公共网络环境中,第三方 有意或恶意 的调用我们的接口):接口防刷
  2. 防篡改攻击(案例:在公共网络环境中,请求头/查询字符串/内容 在传输过程被修改):接口防篡改(签名机制)
  3. 防重放攻击(案例:在公共网络环境中,请求被截获,稍后被重放或多次重放):接口时效性
  4. 防数据信息泄漏(案例:截获用户登录请求,截获到账号、密码等):接口加密(https/对称加解密)

5.1、 接口防刷

/*
	1:设计一个redis临时key, 有效时间是1分钟,1分钟内只允许10访问
     key>   url : ip
     value>访问的次数

	2:设置拦截器,拦截需要防刷的接口url

	3:拦截逻辑
           3.1>拦截url,拼接key查询redis中是否存在
           3.2>如果不存在setnx  url:ip  10
           3.3>如果存在derc  url:ip 
           3.4>如果次数减到0,拦截返回:请勿频繁访问
           3.5>其他情况直接放行。

*/

/* 拦截器 防刷拦截器 */
public class BrushProofInterceptor implements HandlerInterceptor {
    @Autowired
    private ISecurityRedisService securityRedisService;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        if(!(handler instanceof HandlerMethod)){
            return  true;
        }
        //防刷验证
        String url = request.getRequestURI().substring(1);
        String ip = RequestUtil.getIPAddress();
        String key = RedisKeys.BRUSH_PROOF.join(url, ip);
        if(!securityRedisService.isAllowBrush(key)){
            response.setContentType("text/json;charset=UTF-8");
            response.getWriter().write(JSON.toJSONString(JsonResult.error(500, "请勿频繁访问","谢谢咯")));
            return false;
        }
        return true;
    }
}
/* redis服务 */ 
@Service
public class SecurityRedisServiceImpl implements ISecurityRedisService {

    @Autowired
    private StringRedisTemplate template;

    @Override
    public boolean isAllowBrush(String key) {
        //如果有不做 任何操作,如果没有添加
        template.opsForValue().setIfAbsent(key, "10", RedisKeys.BRUSH_PROOF.getTime(), TimeUnit.SECONDS);
        Long decrement = template.opsForValue().decrement(key);
        return decrement >= 0;
    }
}

/* 拦截器配置 */
    @Bean
    public BrushProofInterceptor brushProofInterceptor(){
        return  new BrushProofInterceptor();
    }

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

        //防刷
        registry.addInterceptor(brushProofInterceptor())
                .addPathPatterns("/**");

    }

5.2、接口防篡改

/*
    签名机制
    思路:

    1:前端传参数时,对参数的名进行字典排序,然后按照参数名的属性对参数值进行有序拼接
           比如:
           参数列表: c=参数1, f=参数2,  a=参数3,   b=参数4
           参数排名: a  b  c  f  
           参数值拼接:a=参数3&数4& c=参数1& f=参数2

    2:使用MD5对拼接参数值串进行加密得到参数签名sign_client


    3:将所有参数与sign一同发送到后端


    4:后端获取到所有参数, 按照同样的逻辑,MD5加密得到sign_server
           参数列表: c=参数1, f=参数2,  a=参数3,   b=参数4   sign_client
           参数排名: a  b  c  f  
           参数值拼接:a=参数3&数4& c=参数1& f=参数2

    5:对比sign_server 跟sign_client 2个签名是否一致, 一致表示参数没变篡改,否则提示参数被改,不合法。

*/
--------------------------------------------------------------------------------------------------------

// 页面改动
// common.js


//数据防篡改
function getSignStr(param) {
    var sdic=Object.keys(param).sort();
    var signStr = "";
    for(var i in sdic){

        if(i == 0){
            signStr +=sdic[i]+"="+param[sdic[i]];
        }else{
            signStr +="&"+sdic[i]+"="+param[sdic[i]];
        }
    }

    console.log(hex_md5(signStr));
    return hex_md5(signStr).toUpperCase();
}

请求方法:ajaxRequest
param.sign = getSignStr(param);


操作有页面添加md5.js
 <script src="js/md5/md5.js"></script>
-------------------------------------------------------------------------------------------------
    
    
    
// 后台:
// 签名拦截器
/**
 * 签名拦截(防篡改)
 */
public class SignInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(!(handler instanceof HandlerMethod)){
            return  true;
        }

        //签名验证
        Map<String, String[]> map = request.getParameterMap();
        Set<String> keys = map.keySet();
        Map<String, Object> param = new HashMap<>();
        for (String s : map.keySet()) {
            if("sign".equalsIgnoreCase(s)){
                continue;
            }
            param.put(s, arrayToString(map.get(s)));
        }
        String signatures = Md5Utils.signatures(param);
        String sign = request.getParameter("sign");
        if(sign == null || !sign.equalsIgnoreCase(signatures)){
            response.setContentType("text/json;charset=UTF-8");
            response.getWriter().write(JSON.toJSONString(new JsonResult(501,"签名校验失败","不好意思咯")));
            return false;
        }
        return true;
    }


    private String arrayToString(String [] array){

        StringBuilder sb = new StringBuilder(10);
        for (String s : array) {
            sb.append(s);
        }
        return sb.toString();
    }
}

 // 配置拦截器
    @Bean
    public SignInterceptor signInterceptor(){
        return  new SignInterceptor();
    }

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

        //防刷
        registry.addInterceptor(brushProofInterceptor())
                .addPathPatterns("/**");

        //签名
        InterceptorRegistration it = registry.addInterceptor(signInterceptor())
                .addPathPatterns("/**");

    }

5.3、 接口时效性

签名机制+有效时间
思路:

1:前端传参数时,对参数的名进行字典排序,然后按照参数名的属性对参数值进行有序拼接
       比如:
       参数列表: c=参数1, f=参数2,  a=参数3,   b=参数4  timestamp=188888
       参数排名: a  b  c  f   timestamp
       参数值拼接:a=参数3&数4& c=参数1& f=参数2&timestamp=188888
    
2:使用MD5对拼接参数值串进行加密得到参数签名sign_client
    
3:将所有参数与sign一同发送到后端
    
4:后端获取到所有参数, 按照同样的逻辑,MD5加密得到sign_server
       参数列表: c=参数1, f=参数2,  a=参数3,   b=参数4   sign_client
       参数排名: a  b  c  f  
       参数值拼接:a=参数3&数4& c=参数1& f=参数2&timestamp=188888
    
5:获取timestamp与当前时间对比是否在有效时间内, 比如1分钟, 在执行下一步, 不在,提示接口访问失效
    
6:对比sign_server 跟sign_client 2个签名是否一致, 一致表示参数没变篡改,否则提示参数被改,不合法。

5.4、接口加密

阿里云服务申请https加密证书

https://www.cnblogs.com/shibaolong/p/9837247.html

0

评论区