接口文档
-
API一定需要开发文档配合,移动端只需要根据开发文档进行开发即可;
-
传统的开发文档问题:格式随意,更新不及时;
一、 ShowDoc
一个非常适合IT团队的 API文档、技术文档工具
二、 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接口分类:
- 公共接口:你查快递,你查天气预报,你查飞机,火车班次等,这些都是有公共的接口
- 私密接口:需要登录访问或者公司内部接口
接口安全要求:
- 防伪装攻击(案例:在公共网络环境中,第三方 有意或恶意 的调用我们的接口):接口防刷
- 防篡改攻击(案例:在公共网络环境中,请求头/查询字符串/内容 在传输过程被修改):接口防篡改(签名机制)
- 防重放攻击(案例:在公共网络环境中,请求被截获,稍后被重放或多次重放):接口时效性
- 防数据信息泄漏(案例:截获用户登录请求,截获到账号、密码等):接口加密(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×tamp=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×tamp=188888
5:获取timestamp与当前时间对比是否在有效时间内, 比如1分钟, 在执行下一步, 不在,提示接口访问失效
6:对比sign_server 跟sign_client 2个签名是否一致, 一致表示参数没变篡改,否则提示参数被改,不合法。
5.4、接口加密
评论区