springboot aop配合反射统一签名验证怎么实现
发表于:2025-01-24 作者:千家信息网编辑
千家信息网最后更新 2025年01月24日,本篇内容主要讲解"springboot aop配合反射统一签名验证怎么实现",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"springboot aop配合反
千家信息网最后更新 2025年01月24日springboot aop配合反射统一签名验证怎么实现接口统一签名校验
本篇内容主要讲解"springboot aop配合反射统一签名验证怎么实现",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"springboot aop配合反射统一签名验证怎么实现"吧!
aop配合反射统一签名验证
直接上代码,作为记录。
CheckSignAspect.java
@Aspect //定义一个切面@Configuration@Log4j2public class CheckSignAspect { // 定义切点Pointcut @Pointcut("execution(* com.lsj.xxl.controller.*.*CheckSign(..))") public void excudeService() { } @Around("excudeService()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { String class_name = pjp.getTarget().getClass().getName(); String method_name = pjp.getSignature().getName(); String[] paramNames = getFieldsName(class_name, method_name); Object[] method_args = pjp.getArgs(); SortedMapmap = logParam(paramNames, method_args); if (map != null) { String sign = map.get("sign").toUpperCase(); map.remove("sign"); String realSign = SignUtil.createSign("utf8", map); if (!realSign.equals(sign)) { return "签名校验错误"; } } Object result = pjp.proceed(); return result; } /** * 使用javassist来获取方法参数名称 * * @param class_name 类名 * @param method_name 方法名 * @return * @throws Exception */ private String[] getFieldsName(String class_name, String method_name) throws Exception { Class> clazz = Class.forName(class_name); String clazz_name = clazz.getName(); ClassPool pool = ClassPool.getDefault(); ClassClassPath classPath = new ClassClassPath(clazz); pool.insertClassPath(classPath); CtClass ctClass = pool.get(clazz_name); CtMethod ctMethod = ctClass.getDeclaredMethod(method_name); MethodInfo methodInfo = ctMethod.getMethodInfo(); CodeAttribute codeAttribute = methodInfo.getCodeAttribute(); LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); if (attr == null) { return null; } String[] paramsArgsName = new String[ctMethod.getParameterTypes().length]; int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1; for (int i = 0; i < paramsArgsName.length; i++) { paramsArgsName[i] = attr.variableName(i + pos); } return paramsArgsName; } /** * 打印方法参数值 基本类型直接打印,非基本类型需要重写toString方法 * * @param paramsArgsName 方法参数名数组 * @param paramsArgsValue 方法参数值数组 */ private SortedMap logParam(String[] paramsArgsName, Object[] paramsArgsValue) { Map map = new HashMap(); if (ArrayUtils.isEmpty(paramsArgsName) || ArrayUtils.isEmpty(paramsArgsValue)) { log.info("该方法没有参数"); return null; }// StringBuffer buffer = new StringBuffer(); for (int i = 0; i < paramsArgsName.length; i++) { //参数名 String name = paramsArgsName[i]; //参数值 Object value = paramsArgsValue[i];// if ("sign".equals(name)){// continue;// } if (isPrimite(value.getClass())) { map.put(name, String.valueOf(value)); } else { map.put(name, value.toString()); } } return new TreeMap<>(map); } /** * 判断是否为基本类型:包括String * * @param clazz clazz * @return true:是; false:不是 */ private boolean isPrimite(Class> clazz) { if (clazz.isPrimitive() || clazz == String.class) { return true; } else { return false; } }}
SignUtil.java
public class SignUtil { public static String createSign(String characterEncoding, SortedMapparameters) { StringBuffer sb = new StringBuffer(); for (Map.Entry entry : parameters.entrySet()) { if (!Strings.isNullOrEmpty(entry.getValue()) && !"sign".equals(entry.getKey()) && !"key".equals(entry.getKey())) { sb.append(entry.getKey() + "=" + entry.getValue() + "&"); } } String s = sb.toString(); if (s.length() > 0) { s = s.substring(0, sb.toString().length() - 1); } System.out.println("待加密字符串:" + s); String sign = MD5Util.MD5Encode(s, characterEncoding).toUpperCase(); return sign; }}
测试
@PostMapping("test1") public String bbCheckSign( @RequestParam("value") String value, @RequestParam("bb")String bb, @RequestParam("sign")String sign){ return "ok"; }
补充:
上述方式也可换为注解实现,具体修改代码如下:
添加注解
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME )public @interface CheckSign {}
改变切点
@Pointcut("@annotation(com.lsj.xxl.annotation.CheckSign)")
接口统一签名校验
实现接口请求签名校验,时间戳判断,响应数据返回签名等内容。
这个签名校验,和返回签名可以用多种方法实现。
第一种aop 方式实现
自定义注解体
/** * @author xxx */@Retention(value = RetentionPolicy.RUNTIME)public @interface SignatureValidation {}
aop实现
/** * @author xxx */@Aspect@Componentpublic class SignatureValidation { /** * 时间戳请求最小限制(600s) * 设置的越小,安全系数越高,但是要注意一定的容错性 */ private static final long MAX_REQUEST = 10 * 60 * 1000L; /** * 秘钥 */ private static final String SECRET= "test"; /** * 验签切点(完整的找到设置的文件地址) */ @Pointcut("execution(@com.xx.xxx.xxxxx.aop.SignatureValidation * *(..))") private void verifyUserKey() { } /** * 获取请求数据,并校验签名数据 */ @Before("verifyUserKey()") public void doBasicProfiling(JoinPoint point) { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = Objects.requireNonNull(attributes).getRequest(); String sign = "" ; String timestamp = "" ; String version = ""; SortedMapsortedMap = new TreeMap (); for ( Object obj :point.getArgs()) { JSONObject jsonObject =JSONUtil.parseObj(obj); if( !StrUtil.isEmptyIfStr(jsonObject.get("sign"))){ sign=jsonObject.get("sign").toString(); } if(!StrUtil.isEmptyIfStr(jsonObject.get("timestamp"))){ timestamp=jsonObject.get("timestamp").toString(); sortedMap.put("timestamp", timestamp); } if(!StrUtil.isEmptyIfStr(jsonObject.get("version"))){ version=jsonObject.get("version").toString(); sortedMap.put("version", version); } if(!StrUtil.isEmptyIfStr(jsonObject.get("data"))){ String dataStr= jsonObject.get("data").toString(); if(!JSONUtil.isJsonObj(dataStr)) { if(JSONUtil.isJsonArray(dataStr)){ sortedMap.put("data", JSONUtil.parseArray(dataStr).toString()); } sortedMap.put("data", dataStr); } else { JSONObject dataJson= JSONUtil.parseObj(dataStr); @SuppressWarnings("unchecked") Set keySet = dataJson.keySet(); String key = ""; Object value = null; // 遍历json数据,添加到SortedMap对象 for (Iterator iterator = keySet.iterator(); iterator.hasNext();) { key = iterator.next(); value = dataJson.get(key); String valueStr=""; if(!StrUtil.isEmptyIfStr(value)){ valueStr=value.toString(); } sortedMap.put(key, valueStr); } } } } if (StrUtil.isEmptyIfStr(sign)) { throw new CustomException(BaseResultInfoEnum.ERROR_MISSING_SIGN_1009.getMsg(),BaseResultInfoEnum.ERROR_MISSING_SIGN_1009.getCode()); } //新的签名 String newSign = createSign(sortedMap,SECRET); if (!newSign.equals(sign.toUpperCase())) { throw new CustomException(BaseResultInfoEnum.ERROR_SIGN_1010.getMsg(),BaseResultInfoEnum.ERROR_SIGN_1010.getCode()); } } /** * * @param point * @param responseObject 返回参数 */ @AfterReturning(pointcut="verifyUserKey()",returning="responseObject") public void afterReturning(JoinPoint point,Object responseObject) { HttpServletResponse response=((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); if(responseObject instanceof ResponseModel){ ResponseModel responseModel= (ResponseModel) responseObject; responseModel.setTimestamp(System.currentTimeMillis()); responseModel.setVersion(0); String sign= Md5Utils.createSign(Md5Utils.createParameters(responseModel),SECRET); responseModel.setSign(sign); } }}
md5签名
/** * @author xxx */public class Md5Utils { /** * 生成签名 * @param parameters * @param key 商户ID * @return */ public static String createSign(SortedMapparameters, String key){ StringBuffer sb = new StringBuffer(); Set es = parameters.entrySet(); Iterator it = es.iterator(); while(it.hasNext()) { Map.Entry entry = (Map.Entry)it.next(); String k = (String)entry.getKey(); Object v = entry.getValue(); if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + key); String sign = SecureUtil.md5(sb.toString()).toUpperCase(); return sign; } /** * 签名参数 * @param responseModel 响应数据签名返回给调用者 * @return */ public static SortedMap createParameters(ResponseModel responseModel){ SortedMap sortedMap = new TreeMap (); if(responseModel!=null) { sortedMap.put("timestamp", Convert.toStr(responseModel.getTimestamp()) ); sortedMap.put("version", Convert.toStr(responseModel.getVersion())); JSONObject json = JSONUtil.parseObj(responseModel, false); if(responseModel.getData()!=null) { sortedMap.put("data", json.get("data").toString()); } } return sortedMap; }}
使用,在控制中的方法上方注解即可
第二种拦截器
这里只做了时间判断,签名校验可以根据需要修改即可实现。
过滤器
import javax.servlet.*;import javax.servlet.annotation.WebFilter;import javax.servlet.http.HttpServletRequest;import java.io.IOException;/** * @author xx */@WebFilter(urlPatterns = "/*",filterName = "channelFilter")public class ChannelFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { ServletRequest requestWrapper = null; if(servletRequest instanceof HttpServletRequest) { requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest); } if(requestWrapper == null) { filterChain.doFilter(servletRequest, servletResponse); } else { filterChain.doFilter(requestWrapper, servletResponse); } } @Override public void destroy() { }}
拦截器配置
import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.config.annotation.CorsRegistry;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/** * @author xxxx */@Configurationpublic class InterceptorConfig implements WebMvcConfigurer{ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(getHandlerInterceptor()); } @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedHeaders("Content-Type", "x-requested-with", "X-Custom-Header") .allowedMethods("PUT", "POST", "GET", "DELETE", "OPTIONS") .allowedOrigins("*") .allowCredentials(true); } @Bean public FilterRegistrationBean repeatedlyReadFilter() { FilterRegistrationBean registration = new FilterRegistrationBean(); ChannelFilter repeatedlyReadFilter = new ChannelFilter(); registration.setFilter(repeatedlyReadFilter); registration.addUrlPatterns("/*"); return registration; } @Bean public HandlerInterceptor getHandlerInterceptor() { return new TimestampInterceptor(); }}
RequestWrapper 请求流重写处理
import javax.servlet.ReadListener;import javax.servlet.ServletInputStream;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;import java.io.*;/** * @author xxx */public class RequestWrapper extends HttpServletRequestWrapper { private final String body; public RequestWrapper(HttpServletRequest request) { super(request); StringBuilder stringBuilder = new StringBuilder(); BufferedReader bufferedReader = null; InputStream inputStream = null; try { inputStream = request.getInputStream(); if (inputStream != null) { bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); char[] charBuffer = new char[128]; int bytesRead = -1; while ((bytesRead = bufferedReader.read(charBuffer)) > 0) { stringBuilder.append(charBuffer, 0, bytesRead); } } else { stringBuilder.append(""); } } catch (IOException ex) { } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (bufferedReader != null) { try { bufferedReader.close(); } catch (IOException e) { e.printStackTrace(); } } } body = stringBuilder.toString(); } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes()); ServletInputStream servletInputStream = new ServletInputStream() { @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } @Override public int read() throws IOException { return byteArrayInputStream.read(); } }; return servletInputStream; } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(this.getInputStream())); } public String getBody() { return this.body; }}
拦截器 这里可以实现请求来的签名处理,这里只处理时间了
import cn.hutool.core.util.StrUtil;import cn.hutool.json.JSONObject;import cn.hutool.json.JSONUtil;import xxx.xxx.xxx.common.result.BaseResultInfoEnum;import xxx.xxx.common.core.exception.CustomException;import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * @author xxx */public class TimestampInterceptor implements HandlerInterceptor { /** * 时间戳请求最小限制(600s) * 设置的越小,安全系数越高,但是要注意一定的容错性 */ private static final long MAX_REQUEST = 10 * 60 * 1000L; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; if (handlerMethod.getBean() instanceof BasicErrorController) { return true; }// if ("GET".equals(request.getMethod())) {// return true;// } ValidateResponse validateResponse = new ValidateResponse(true, null); RequestWrapper myRequestWrapper = new RequestWrapper((HttpServletRequest) request); validateResponse= checkTimestamp(myRequestWrapper.getBody()); if (!validateResponse.isValidate()) { throw validateResponse.getException(); } } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { } private ValidateResponse checkTimestamp(String requestBody) { try { JSONObject jsonObject = JSONUtil.parseObj(requestBody); String timestamp = "" ; if(!StrUtil.isEmptyIfStr(jsonObject.get("timestamp"))) { timestamp=jsonObject.get("timestamp").toString(); } if (StrUtil.isEmptyIfStr(timestamp)) { return new ValidateResponse(false, new CustomException(BaseResultInfoEnum.ERROR_MISSING_TIMESTAMP_1007.getMsg(), BaseResultInfoEnum.ERROR_MISSING_TIMESTAMP_1007.getCode())); } long now = System.currentTimeMillis(); long time = Long.parseLong(timestamp); if (now - time > MAX_REQUEST) { return new ValidateResponse(false, new CustomException(BaseResultInfoEnum.ERROR_TIMESTAMP_TIMEOUT_1008.getMsg(), BaseResultInfoEnum.ERROR_TIMESTAMP_TIMEOUT_1008.getCode())); } } catch (Exception e) { e.printStackTrace(); } return new ValidateResponse(true, null); } /** * 校验返回对象 */ private static class ValidateResponse { private boolean validate; private CustomException exception; public ValidateResponse(boolean validate, CustomException exception) { this.validate = validate; this.exception = exception; } public boolean isValidate() { return validate; } public Exception getException() { return exception; } }}
返回给前端(或其他平台的)处理类
import comzzzz.xx.common.pojo.PageResponseModel;import com.zzz.xxxx.common.pojo.ResponseModel;import org.springframework.core.MethodParameter;import org.springframework.http.MediaType;import org.springframework.http.server.ServerHttpRequest;import org.springframework.http.server.ServerHttpResponse;import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;/** * @author sunrh */@ControllerAdvicepublic class ResponseBodyTimestamp implements ResponseBodyAdvice { @Override public boolean supports(MethodParameter methodParameter, Class aClass) { return true; } @Override public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {//就是这里处理返回签名数据 if(o instanceof ResponseModel){ ResponseModel responseModel= (ResponseModel) o; responseModel.setTimestamp(System.currentTimeMillis()); return responseModel; } if(o instanceof PageResponseModel){ PageResponseModel responseModel= (PageResponseModel) o; responseModel.setTimestamp(System.currentTimeMillis()); return responseModel; } return o; }}
最终实现的效果图
到此,相信大家对"springboot aop配合反射统一签名验证怎么实现"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
方法
参数
数据
统一
时间
处理
反射
验证
注解
内容
切点
类型
拦截器
最小
安全
安全系数
代码
容错性
对象
接口
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
福建正规网络技术标准
数据库技术 关系数据库
手机系统服务器在哪里找
基岩版1.18新生存服务器
闵行区上门软件开发定制大概费用
数据库dbf删了
天津网络安全培训学院
清空mysql数据库表
删除数据库次数据文件
科技文献检索的数据库名是什么
软件开发中项目角色
云柜网络技术有限公司
游戏侏罗纪进化服务器无法访问
默认数据库文件位置更改
网络安全四格漫画教程
麻花视频软件开发
互联网深科技文章
河北二手服务器回收公司
特色软件开发服务电话
湘潭杯网络安全
合肥数据库招聘
java软件开发不了怎么办
梦幻西游各服务器等级人数统计
博兴轻工业管理软件开发公司
网络安全需要学习什么
邢台网络技术预算
ivr 服务器
网络安全密匙在哪查看
数据库设计案例
谷歌为什么不用数据库