创建一个注解 RSAdecrypt
import java.lang.annotation.*;
@Target(ElementType.PARAMETER)//声明此注解只能在方法参数上生效
@Retention(RetentionPolicy.RUNTIME)//声明此注解生命周期 使用runtime可以在运行时动态获得注解信息
@Documented
public @interface RSAdecrypt {
String[] value() default "";//设置注解需要传递字符串数组的参数 (方便之后给pojo对象的成员变量解密)
}
实现方法参数解析类(也就是前端传到后端并解析的实现类,前端的键名对应后端的参数名)
public class RSAdecryptResolver implements HandlerMethodArgumentResolver {
private String key = "xxxxx";//这里是你生成的密钥
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RSAdecrypt.class);
//此方法判断在什么条件下才执行此解析器
//也就是前端控制器 Controller类里的具体方法中的参数前加了此注解才会返回true执行以下方法
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
//MethodParameter 对象指的是进入此解析的参数对象 也就是加了注解的参数对象
String methodparametername = parameter.getParameterName();//获取controller层我们定义的参数名
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);//将nativerequest对象转换成HttpServletRequest对象
String agument = request.getParameter(methodparametername);//调用此方法获得请求体的实际行参的参数值, 也就是被前端加密的字符串 所以说一定要 方法参数名 和 前端的键 保持一致
return decrypt(agument,key);//返回解密后的字符串
}
//这是RSA解密 有兴趣的同学可以看相关文档 本章不讨论此问题
private String decrypt(String str, String privateKey) throws Exception {
//64位解码加密后的字符串
byte[] inputByte = Base64.getDecoder().decode(str);
//base64编码的私钥
byte[] decoded = Base64.getDecoder().decode(privateKey);
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
//RSA解密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, priKey);
String outStr = new String(cipher.doFinal(inputByte));
return outStr;
}
}
在springboot配置类中添加个参数解析器就ok啦
@Configuration
public class CommonRegister implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new RSAdecryptResolver());
}
}
就可以使用注解自动解密啦 代码清爽多了
@RequestMapping("/loginmg")
@ResponseBody
public String login(@RSAdecrypt String param){
return param;
}
还有个pojo对象针对成员变量解密稍后更新
针对pojo对象的解密
public class RSAdecryptResolver implements HandlerMethodArgumentResolver {
private String key;
public RSAdecryptResolver(String key) {
this.key = key;
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RSAdecrypt.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
//获取注解
RSAdecrypt annotation = parameter.getParameterAnnotation(RSAdecrypt.class);
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
//获取被注解标记的参数字节码文件
Class c = parameter.getParameterType();
//字符串就走传统方法,非走下面的反射方法
if (c !=String.class){
//获取注解的value值(需要解密的对象成员变量名,必须是string,不然就没意义啦)
List m = Arrays.asList(annotation.value());
//通过反射执行构造器生成此pojo对象(必须有个无参构造器)
Object o = c.newInstance();
//通过反射遍历每个成员变量 使用Declaredfield才能获取private成员变量
for (Field field : c.getDeclaredFields()) {
//获取成员变量的type类型
Class g = field.getType();
//获取成员变量名
String s = field.getName();
//通过成员变量名获取它的set方法 upper方法作用就是将成名变量名的首字母大写 getMethod(参数一:方法名,参数二:此方法如果需要传递参数给他个传参类型,,当然是成员变量的类型咯,,此成员变量的set方法传参类型肯定是他本身的类型,,如果是多参就多加几个,动态参数无所谓的)
Method set = c.getMethod("set"+upper(s),g);
Object p = request.getParameter(s);//通过成员变量名来获取传过来的键名(也就是常说的pojo对象属性名一定要和传过来的键名对应)
//如果你的注解的value 也就是需要解密的字段集合 包含通过反射获得的成员变量名
if (m.contains(s)){
set.invoke(o,decrypt((String) p,key));//则set方法执行,invoke(参数一:哪个对象需要执行set方法当然是前面生成的newinsatnce,参数二:传入参数的具体对象,如果要解密必须是string类型的)
continue;//封装好了跳过此循环,next成员变量
}
set.invoke(o,p); //这边就是除了你注解value传入需要解密的属性名以外的都走这个普通pojo赋值 再说明一下p是根据field成员变量名获得前端键所对应的值
}
//每个成员变量都处理完了返回pojo
return o;
}
return decrypt(request.getParameter(parameter.getParameterName()),key);//他是String类型的就走传统方法解密咯
}
//简单的首字母大写
private String upper(String attr){
return attr.substring(0,1).toUpperCase()+attr.substring(1);
}
pojo对象注解解密就是这个样子的
@RequestMapping(value = "/addsalesman",method = RequestMethod.POST)
@ResponseBody //传入需要解密的属性名就ok了
public Map addsalesman(@RSAdecrypt({"password","phone","email"}) Salesman salesman){
}