Skip to content

Instantly share code, notes, and snippets.

@la3rence
Created December 7, 2020 02:45
Show Gist options
  • Save la3rence/ed3dbebf066a4af526832a3ea04caa59 to your computer and use it in GitHub Desktop.
Save la3rence/ed3dbebf066a4af526832a3ea04caa59 to your computer and use it in GitHub Desktop.
利用注解和切面给接口做签名验证(SpringBoot)
package com.example.open.server.aop;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 请在需要签名的接口上打上此注解
*
* @author https://lawrenceli.me
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedSign {
}
package com.example.open.server.aop;
// 一些工具类用的 hutool,你可以自己把它们换掉
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.crypto.SecureUtil;
import com.example.common.constants.ErrorCode;
import com.example.common.model.ResultBody;
import lombok.extern.slf4j.Slf4j;
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.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.TreeMap;
/**
* 切面:参数签名验证,配置合适的切点表达式来切入指定的方法
*
* @author https://lawrenceli.me
*/
@Aspect
@Slf4j
@Component
public class SignAop {
// 签名变量的参数名
private static final String SIGN_PARAM_NAME = "sign";
@Value("${example.client-secret}")
private String secretKey;
/**
* 定义切入点
*/
@Pointcut("execution(* com.example.open.server.controller.HelloController.*(..))")
public void aspect() {
}
/**
* 签名方法环绕通知
* 签名方式:
* 所有除 sign 以外参数按字母顺序拼接参数名和参数值,最后拼上当前日期(yyyy-MM-dd)和 secret,再做 MD5 并转大写:
* 如参数:a=1, b=22: 伪代码 sign = MD5(a1b222020-01-01secret),其中 secret 需要替换成提供的值。
*
* @param point 切点
* @param needSign 是否需要签名的注解
* @return 方法反射
*/
@Around("aspect()&&@annotation(needSign)")
public Object doAround(ProceedingJoinPoint point, NeedSign needSign) throws Throwable {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
assert requestAttributes != null;
HttpServletRequest request = requestAttributes.getRequest();
StringBuilder params = new StringBuilder();
Map<String, String[]> parameterMap = request.getParameterMap();
TreeMap<String, String[]> sortedParamMap = MapUtil.sort(parameterMap);
for (String paramName : sortedParamMap.keySet()) {
if (!SIGN_PARAM_NAME.equals(paramName.trim())) {
params.append(paramName).append(sortedParamMap.get(paramName)[0]);
}
}
String today = DateUtil.today();
params.append(today).append(secretKey);
// 获取客户端传来的签名
String signFromFront = request.getParameter(SIGN_PARAM_NAME);
String rightSign = SecureUtil.md5(params.toString()).toUpperCase();
return rightSign.equals(signFromFront) ? point.proceed() : ResultBody.failed(ErrorCode.SIGNATURE_DENIED);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment