提供三方API接口、调用第三方接口API接口、模拟API接口(三)使用AOP切面编程实现signature签名验证

接着第一篇文章
提供三方API接口、调用第三方接口API接口、模拟API接口(一)通过signature签名验证,避免参数恶意修改
我们来继续优化:

/**
     * 模拟后端校验签名
     * @param request
     * @param data
     * @return
     * @throws UnsupportedEncodingException
     */
    @GetMapping("/sign/verifySign")
    public boolean verifySign(HttpServletRequest request, @RequestParam Map<String, String> data) throws UnsupportedEncodingException {
        String sign = request.getHeader("sign");
        String signType = request.getHeader("sign_type");
        return ApiUtil.verifySign(data, sign, signType);
    }

背景:看这块代码,第一篇中的签名验证是在请求的业务逻辑中手动去调用进行signature验证的。这样代码显得太臃肿,因此采用注解与aop拦截的方式来实现参数校验,复用代码的同时,让程序变得简洁与高效。

接下来先通过AOP实现参数签名校验;

封装请求常量类

package com.atguigu.signcenter.constant;

/**
 *
 * 请求参数常量类
 * @author: jd
 * @create: 2024-08-04
 */
public class ReqParameterConstant {

    /**
     * 请求中的参数类型是head 头部参数的
     */
    public static final  String HEAD ="head";

    /**
     * 请求中的参数类型是form,是表单类型的
     */
    public static final  String FORM ="form";

}

自定义签名校验注解

package com.atguigu.signcenter.annotation;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface VerifySign {

    /**
     * 参数类型 head 、 form
     */
    String type();
}

aop拦截器

package com.atguigu.signcenter.component;

import com.atguigu.signcenter.annotation.VerifySign;
import com.atguigu.signcenter.constant.ReqParameterConstant;
import com.atguigu.signcenter.util.ApiUtil;
import com.sun.deploy.association.utility.AppUtility;
import io.swagger.annotations.Extension;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 *
 * 切面类
 * 先定义一个切面,这个切面主要的针对于controller中的所有方法,
 * 再顶定义一个环绕通知,这个环绕通知主要是为了判断是否有自定义注解,@VerifySign ,如果有自定义注解的话,则会做一些业务处理、逻辑校验
 * @author: jd
 * @create: 2024-08-04
 */

@Aspect
@Slf4j
@Component
public class VerifySignAop {


    /**
     * 定义一个切面方法, 使用 AOP 的环绕通知去拦截这里标明的所有访问,拦截的是 controller 层中的所有的public方法,任何参数都可
     */
    @Pointcut("execution(public * com.atguigu.signcenter.controller.*.*(..)))")
    public  void myPointCut(){

    }


    /**
     * 环绕通知,主要用于判断对于切面方法中是否有某个注解,如果有的话,则做逻辑校验或者某些业务逻辑的处理
     * @return
     */
    @Around("myPointCut()")
    @Synchronized
    public Object aroundAOP(ProceedingJoinPoint joinPoint) throws Throwable {
        //获得getSignature方法对象
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 判断方法上是否有注解 @VerifySign
        VerifySign  verifySign = methodSignature.getMethod().getDeclaredAnnotation(VerifySign.class);
        //定义两个变量用于,接收参数签名字符串和参数签名的加密方式字符串
        String sign = null;
        String signType = null;
        if(null!=verifySign){
            // 获取上下文的请求
            HttpServletRequest request = getRequest();
            String type = verifySign.type();
            log.info("type:{} " + type);
            if(ReqParameterConstant.HEAD.equals(type)){
                sign =  request.getHeader("sign");
                signType = request.getHeader("sign_type");
            }

            if(ReqParameterConstant.FORM.equals(type)){
                sign = request.getParameter("sign");
                signType = request.getParameter("sign_type");
            }
            if(StringUtils.isBlank(sign)){
                response("该请求无签名!");
                return null;
            }
            if(StringUtils.isBlank(signType)||!"RSA".equals(signType)){
                response("无签名加密方式|签名加密方式不是RSA,所以不支持!");
                return null;
            }
            log.info("sign:{} " , sign);
            // 获取请求的所有参数 ,并将他形成一个HashMap,为了参数验证构建
            Map<String, String> data = new HashMap();
            Enumeration<String> parameterNames = request.getParameterNames();
            while (parameterNames.hasMoreElements()){
                String element = parameterNames.nextElement();
                data.put(element,request.getParameter(element));
            }
            //参数验签
            boolean verifyFlag = ApiUtil.verifySign(data, sign, signType);
            if (!verifyFlag) {
                response("无效的签名!");
                return null;
            } else {
                log.info("有效的签名。");
            }


        }
        //程序继续向下执行
        Object proceed = joinPoint.proceed();
        return proceed;


    }




    /**
     *
     * 获取容器上下文请求
     * @return
     */
    public HttpServletRequest getRequest() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        return request;
    }


    /**
     * 响应错误响应信息
     * @param msg
     */
    public void response(String msg) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = requestAttributes.getResponse();
        response.setHeader("Content-type", "text/html;charset=UTF-8");
        try(PrintWriter writer = response.getWriter()) {
            writer.println(msg);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }




}

控制器的业务方法添加签名校验注解


    @VerifySign(type = "head")
    @GetMapping("sign/testAnnotation")
    @ResponseBody
    public String test(@RequestParam Map<String,String> data){
        // 模拟业务逻辑
        // TODO Something
        log.info("做一些业务,业务模拟");
        return "成功通过AOP切面中的校验,执行了业务逻辑";
    }

测试
需要携带上参数,不然有签名没用,因为签名就是为了验证参数是否发生了变化
参数不带签名时候:
http://localhost:8025/sign/testAnnotation?name=zhangsan&age=100
在这里插入图片描述
可以看到,验证提示 该请求无签名! ,而且也没有打印controller中模拟业务的代码(“做一些业务,业务模拟”)。

携带上签名参数
在这里插入图片描述

测试结果:
在这里插入图片描述

IDEA控制台打印结果:
在这里插入图片描述
至此完成了,AOP实现签名验证的代码实现。

码字不易,不足之处还请大家多多指教~

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值