前言
简单说一下需求
要做一个项目,简单保证系统内部的安全性,然后还要防止高并发的攻击,于是就要对进入系统的请求做一些限制,其实也就是如何做快速失败,这里我使用的是过滤器来实现的,对于不必要的请求,直接过滤掉就可以了。
思路
- 签名算法校验参数有效性,无效直接返回
403
token
校验,拿着token
去redis
中进行比对,如果token
校验失败,同样直接返回403
签名算法是什么
为了防止API调用过程中被黑客恶意篡改,调用任何一个API都需要携带签名,服务端会根据请求参数,对签名进行验证,签名不合法的请求将会被拒绝。
如果我们正常的http请求参数是这样的
{
"param1": "aaa",
"param2": "bbb"
}
加了签名之后的参数就是这样的
{
"param1": "aaa",
"param2": "bbb",
"sign": "f2f8ba7d0ac080bf"
}
加了签名之后多了一个名为 sign
的参数,这个sign就是在客户端或者web端对参数进行加密的结果
大概加密就是把参数全部合在一起进行md5加密的结果,到了服务端,再对参数进行一次加密,比对一下和参数重的sign是否相同就好了
说几句
- 逻辑不复杂,稍微用心看点就可以看明白
- md5算法可以更换,换成任意自己喜欢的,但是,要保证客户端与服务端的一致
- token校验暂且不写,token校验如果写的话还可以单另写一篇,这篇主要讲过滤器实现
- 如果有需要,我上传github之后,再来贴链接,或者在下面评论也可以,我每条评论都看
- 我认为,大家需要学习的不是代码怎么写,而是要怎么去思考,一旦思考停止了,那就都没意义了
贴代码
ValidParamsFilter
校验参数有效性的过滤器
package com.stu.filter;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
/**
* <p>
* 校验参数有效性的过滤器
* </p>
*
* @author Banana
* @since 2021/1/30
*/
//@WebFilter(filterName = "validParamsFilter", urlPatterns = "/*")
@Component
public class ValidParamsFilter implements Filter {
//这里面 填写不需要 被拦截的地址
private static final Set<String> ALLOWED_PATHS = Collections.unmodifiableSet(
new HashSet<String>(Arrays.asList("/login", "/isLogin", "/findCategory"))
);
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
Map<String, String> map;
// 一个request的包装类,初始化时缓存了body,重写了getInputStream返回缓存的body,实现重复读取body
BodyReaderRequestWrapper requestWrapper = new BodyReaderRequestWrapper(request);
String body = ReqGetBody.getBody(requestWrapper);
boolean flag = false;
try {
if (!body.isEmpty()) {
map = (Map<String, String>) JSON.parse(body);
// 签名校验算法
flag = ValidParamUtils.valid(map);
}
} catch (Exception e) {
// 可以记录一下用户行为
}
if (flag) {
filterChain.doFilter(requestWrapper, response);
} else {
response.sendError(403, "param valid fail!!!");
}
}
}
ValidParamUtils
用来校验参数有效性的小算法
package com.stu.filter;
import java.util.Arrays;
import java.util.Map;
/**
* <p>
*
* </p>
*
* @author Banana
* @since 2021/2/1
*/
public class ValidParamUtils {
public static Boolean valid(Map<String, String> params) {
String headerSign = params.get("sign");
if (headerSign == null) return false;
params.remove("sign");
// 第一步:检查参数是否已经排序
String[] keys = params.keySet().toArray(new String[0]);
Arrays.sort(keys);
// 第二步:把所有参数名和参数值串在一起
StringBuilder query = new StringBuilder();
for (String key : keys) {
String value = params.get(key);
query.append(key).append(value);
}
String paramsString = query.toString();
String sign = MD5Utils.getMD5Str(paramsString);
return sign.equals(headerSign);
}
}
FilterConfig
这里用来注入filter,因为需要控制filter开启关闭,所以我把开关抽象在了配置文件中
package com.stu.config;
import com.stu.filter.ValidParamsFilter;
import com.stu.filter.ValidTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* <p>
*
* </p>
*
* @author 刁琳童
* @since 2021/1/30
*/
@Configuration
public class FilterConfig {
@Value("${filter.valid-param}")
private Boolean validParam;
@Value("${filter.valid-token}")
private Boolean validToken;
@Autowired
private ValidParamsFilter validParamsFilter;
@Autowired
private ValidTokenFilter validTokenFilter;
@Bean
public FilterRegistrationBean validParamsFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean(validParamsFilter);
registration.addUrlPatterns("/*");
registration.setName("validParamsFilter");
registration.setEnabled(validParam);
registration.setOrder(1);
return registration;
}
@Bean
public FilterRegistrationBean validTokenFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean(validTokenFilter);
registration.addUrlPatterns("/*");
registration.setName("validTokenFilter");
registration.setEnabled(validToken);
registration.setOrder(2);
return registration;
}
}