思路:appId,appKey(appSecret)(网络中不传输),提供方将这两个参数下发给使用方。
httpServletRequest.getParameterNames():获取所有参数的名字
@Data
public class SignDTO {
private String appId;
private String name;
private String sign;
}
@RestController
@RequestMapping("/api-safe")
public class ApiSafeController {
@GetMapping("/hello")
public String hello(){
return "hello api safe";
}
/**
* 测试 get 方法 参数防篡改
* @return
*/
@RequestMapping("/get-test")
public String getTest(String appId, String name , String sign , long timestamp , HttpServletRequest httpServletRequest){
// 为了排序
HashMap<String,String> map= new HashMap<>();
// 参数写死
// map.put("appId",appId);
// map.put("name",name);
// map.put("timestamp",timestamp);
// 获取get中的参数
Enumeration<String> parameterNames = httpServletRequest.getParameterNames();
while (parameterNames.hasMoreElements()){
//获取 name
String parametename = parameterNames.nextElement();
// 获取值
String parameterValue = httpServletRequest.getParameter(parametename);
map.put(parametename,parameterValue);
}
// 让接口在有效期内访问
// long time = System.currentTimeMillis() - timestamp;
// if (time > 1000 * 30){
// return "接口过期了";
// }
String s = CheckUtils.generatorSign(map);
if (s.equals(sign)){
return "校验通过";
}else {
return "校验 不通过";
}
}
@PostMapping("/post-test")
public String postTest(@RequestBody SignDTO signDTO){
System.out.println("进入controller方法");
JSONObject obj = JSONUtil.parseObj(signDTO);
System.out.println("controller参数:"+obj);
return "controller";
}
}
public class CheckUtils {
/**
* app secret和 appId,一一对应
*/
public static String appSecret = "aaa";
// 校验 签名
public static boolean checkSign(Map<String,String> map){
String sign = (String) map.get("sign");
map.remove("sign");
// 生成sign
String s = CheckUtils.generatorSign(map);
if (s.equals(sign)){
return true;
}else {
return false;
}
}
// 根据map生成签名
public static String generatorSign(Map<String,String> map){
map.remove("sign");
// 排序:
Map<String, String> stringObjectMap = sortMapByKey(map);
// 转格式: name=张三&age=10,: name,张三,age,10
Set<Map.Entry<String, String>> entries = stringObjectMap.entrySet();
StringBuilder sb = new StringBuilder();
for (Map.Entry<String,String> e : entries){
sb.append(e.getKey()+","+e.getValue()).append("#");
}
// 组装secret 在参数的后面 添加 secret
sb.append("secret").append(appSecret);
// 生成签名
return MD5Util.md5(sb.toString());
// sha256生成 签名
// return Sha256Utils.getSHA256(sb.toString());
}
public static Map<String,String> sortMapByKey(Map<String,String> map){
// 判断一下map是否为空,自己写
Map<String,String> sortMap = new TreeMap<>(new MyMapComparator());
sortMap.putAll(map);
return sortMap;
}
static class MyMapComparator implements Comparator<String>{
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
}
public static void main(String[] args) {
HashMap<String,String> map = new HashMap<>();
map.put("appId","1");
map.put("name","2");
map.put("urlParam","3");
Map<String, String> stringObjectMap = sortMapByKey(map);
System.out.println(stringObjectMap);
String s = generatorSign(map);
// 74f0c8c14fd2869121c910601e9ea859
System.out.println(s);
}
}
public class Sha256Utils {
/**
* 利用java原生的类实现SHA256加密
*
* @param str 加密后的报文
* @return
*/
public static String getSHA256(String str) {
MessageDigest messageDigest;
String encodestr = "";
try {
messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(str.getBytes("UTF-8"));
encodestr = byte2Hex(messageDigest.digest());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return encodestr;
}
/**
* 将byte转为16进制
*
* @param bytes
* @return
*/
private static String byte2Hex(byte[] bytes) {
StringBuffer stringBuffer = new StringBuffer();
String temp = null;
for (int i = 0; i < bytes.length; i++) {
temp = Integer.toHexString(bytes[i] & 0xFF);
if (temp.length() == 1) {
//1得到一位的进行补0操作
stringBuffer.append("0");
}
stringBuffer.append(temp);
}
return stringBuffer.toString();
}
}
@Component
public class SignAuthFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 签名的验证
// HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletRequest request = new BodyReaderHttpServletRequestWrapper((HttpServletRequest)servletRequest);
HttpServletResponse response = (HttpServletResponse) servletResponse;
// 获取参数.统一get和post,不管url,还是 body
SortedMap<String, String> allParams = HttpParamUtils.getAllParams(request);
// 校验签名
boolean b = CheckUtils.checkSign(allParams);
System.out.println("校验签名结果:"+b);
if (b){
filterChain.doFilter(request,response);
}else {
response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
JSONObject param = new JSONObject();
param.put("code",-1);
param.put("message", "签名错了");
writer.append(param.toJSONString());
}
System.out.println("filter生效了");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化");
}
@Override
public void destroy() {
System.out.println("销毁");
}
}
/**
* 保存过滤器里面的流
*/
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
String sessionStream = getBodyString(request);
body = sessionStream.getBytes(Charset.forName("UTF-8"));
}
/**
* 获取请求Body
*
* @param request
* @return
*/
public String getBodyString(final ServletRequest request) {
StringBuilder sb = new StringBuilder();
try (
InputStream inputStream = cloneInputStream(request.getInputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")))
) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
/**
* Description: 复制输入流</br>
*/
public InputStream cloneInputStream(ServletInputStream inputStream) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
try {
while ((len = inputStream.read(buffer)) > -1) {
byteArrayOutputStream.write(buffer, 0, len);
}
byteArrayOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() {
return bais.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}
@Slf4j
public class HttpParamUtils {
/**
* 获取所有参数,包括 url和body
* @param request
* @return
* @throws IOException
*/
public static SortedMap<String,String> getAllParams(HttpServletRequest request) throws IOException {
// 获取 url上的参数
Map<String, String> urlParams = getUrlParams(request);
System.out.println("url 参数:"+urlParams);
// 获取 body上的参数
Map<String, String> bodyParams = getBodyParams(request);
// 总的参数的map
SortedMap<String , String> allMap = new TreeMap<>();
for (Map.Entry entry : urlParams.entrySet()){
allMap.put((String) entry.getKey() , (String)entry.getValue());
}
for (Map.Entry entry : bodyParams.entrySet()){
allMap.put((String) entry.getKey() , (String)entry.getValue());
}
log.info("所有的参数:"+allMap);
return allMap;
}
/**
* 获取body中的参数
*/
private static Map<String,String> getBodyParams(HttpServletRequest request) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream())) ;
StringBuilder sb = new StringBuilder();
// 读取 流
String s = "";
while ((s=reader.readLine())!=null){
sb.append(s);
}
// 转map
Map map = JSONObject.parseObject(sb.toString(), Map.class);
System.out.println("body参数:"+map);
return map;
}