springboot aop配合反射统一签名验证怎么实现
发表于:2025-11-07 作者:千家信息网编辑
千家信息网最后更新 2025年11月07日,本篇内容主要讲解"springboot aop配合反射统一签名验证怎么实现",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"springboot aop配合反
千家信息网最后更新 2025年11月07日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(); SortedMap map = 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, SortedMap parameters) { 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 = ""; SortedMap sortedMap = 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(SortedMap parameters, 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安全错误
数据库的锁怎样保障安全
高级php软件开发
宁波磐天网络技术有限公司
计算机应用网络技术自考
内蒙古网络安全问题
阿里云服务器删除
车品投广州网络技术有限公司
结构设计与软件开发
软件开发需要在家学吗
南京鱼跃医疗软件开发部加班多吗
网络安全竞赛初选题
网络安全截获属于被动攻击的是
泰拉瑞亚手游服务器招聘
试论网络安全面临的风险
帝国神话pvp服务器不见了
sqlite3数据库安装
中国地图版网络技术应用
面试官软件开发
网络安全值不值得考研
软件开发深圳兼职
华为服务器下载官网
惠普服务器配置管理口
软件开发有哪些证书
诛仙新建数据库
信威网络技术有限公司
pgsql数据库还原命令
数据库创建视图解题步骤
网络安全防校园欺凌观后感
软件开发要求80天交付
致远协同办公数据库
数据库的索引关键词