Created
December 7, 2020 02:45
-
-
Save la3rence/ed3dbebf066a4af526832a3ea04caa59 to your computer and use it in GitHub Desktop.
利用注解和切面给接口做签名验证(SpringBoot)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 { | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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