1.Mavne导入加密解密所需的依赖
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-io</artifactId> <version>1.3.2</version>
</dependency>
2.在aspect切面包下建立 入参切面与出参切面
DecodeRequestBodyAdvice.class 入参拦截
流程:获取本次请求->判断是否包含加密/解密注解->对请求字符串解密->重构请求过来的加密字符串为实体类对象
package com.iliebe.web.aspect; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import com.icbc.api.internal.util.codec.Base64; import com.iliebe.web.annotation.SecurityParameter; import com.iliebe.web.constants.SystemConstant; import com.iliebe.web.util.EncryptForPKCS7Util; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.core.MethodParameter; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpInputMessage; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice; import org.apache.commons.io.IOUtils; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Type; import java.security.NoSuchAlgorithmException; /** * @Date: 2020/6/27 下午3:59 * @Description: TODO(请求参数加密) */ @Slf4j @ControllerAdvice(basePackages = "com.iliebe.web.controller") public class DecodeRequestBodyAdvice implements RequestBodyAdvice { @Override public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) { return true; } @Override public Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) { return body; } @Override public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException { try { boolean encode = false; String apiPath = ""; // 方法是否 包含注解 if (methodParameter.getMethod().isAnnotationPresent(SecurityParameter.class)) { //获取注解配置的包含和去除字段 SecurityParameter serializedField = methodParameter.getMethodAnnotation(SecurityParameter.class); //入参是否需要解密 encode = serializedField.inDecode(); apiPath = serializedField.apiPath(); } if (encode) { return new MyHttpInputMessage(inputMessage,apiPath); } else { return inputMessage; } } catch (Exception e) { e.printStackTrace(); log.error("方法method :【{}】参数解密出现异常:{}", methodParameter.getMethod().getName(), e.getMessage()); return inputMessage; } } @Override public Object afterBodyRead(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) { return body; } class MyHttpInputMessage implements HttpInputMessage { private HttpHeaders headers; private InputStream body; public MyHttpInputMessage(HttpInputMessage inputMessage,String apiPath) throws Exception { this.headers = inputMessage.getHeaders(); // 获取加密 json中 值 String src = easpString(IOUtils.toString(inputMessage.getBody(), "UTF-8")); // 解密加密字符串 String decryptStr = null; if (StringUtils.isBlank(apiPath)){ decryptStr = EncryptForPKCS7Util.Decrypt(src, SystemConstant.AES_KEY); } if (StringUtils.isBlank(decryptStr)){ throw new RuntimeException("参数【requestData】解密异常!"); } this.body = IOUtils.toInputStream(decryptStr, "UTF-8"); } @Override public InputStream getBody() { return body; } @Override public HttpHeaders getHeaders() { return headers; } /** * 获取requestData数据 */ public String easpString(String requestData) { if (StrUtil.isNotEmpty(requestData)) { JSONObject jsonObject = new JSONObject(requestData); String requestDataStr = jsonObject.getStr("requestData"); if (StrUtil.isBlank(requestDataStr)) { throw new RuntimeException("参数【requestData】缺失异常!"); } return requestDataStr; } return ""; } } public static String genAesSecret(){ try { KeyGenerator kg = KeyGenerator.getInstance("AES"); //下面调用方法的参数决定了生成密钥的长度,可以修改为128, 192或256 kg.init(128); SecretKey sk = kg.generateKey(); byte[] b = sk.getEncoded(); String secret = Base64.encodeBase64String(b); return secret; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); throw new RuntimeException("没有此算法"); } } public static void main(String[] args) { System.out.println(EncryptForPKCS7Util.Decrypt("jH+iZFzwlWyStzAfHaLW4eQ2MoStfSAg0hsXk6miSvfm7C+oRED1zSI1uT0wkYbX4Q0YeVqA7JM/kxC0kSrOJA3v3mByGBo8DmlS349DvpY=", "Q6Oxh8pknkr3/B+0ukKVqg==")); } }
EncodeResponseBodyAdvice.class 出参拦截
流程:获取本次响应->判断是否包含加密/解密注解->对响应数据加密->返回响应数据为加密后的字符串
package com.iliebe.web.aspect; import com.fasterxml.jackson.databind.ObjectMapper; import com.iliebe.web.annotation.SecurityParameter; import com.iliebe.web.constants.SystemConstant; import com.iliebe.web.util.EncryptForPKCS7Util; import com.iliebe.web.util.JsonConfig; import lombok.extern.slf4j.Slf4j; 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; /** * @Date: 2020/6/27 下午3:59 * @Description: TODO(返回数据解密) */ @Slf4j @ControllerAdvice(basePackages = "com.iliebe.web.controller") public class EncodeResponseBodyAdvice implements ResponseBodyAdvice { @Override public boolean supports(MethodParameter methodParameter, Class aClass) { return true; } @Override public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { boolean encode = false; if (methodParameter.getMethod().isAnnotationPresent(SecurityParameter.class)) { //获取注解配置的包含和去除字段 SecurityParameter serializedField = methodParameter.getMethodAnnotation(SecurityParameter.class); //出参是否需要加密 encode = serializedField.outEncode(); } if (encode) { ObjectMapper objectMapper = JsonConfig.getObjectMapper(); try { String result = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(body); return EncryptForPKCS7Util.Encrypt(result, SystemConstant.AES_KEY); } catch (Exception e) { e.printStackTrace(); log.error("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密出现异常:" + e.getMessage()); } } return body; } }
3.加密注解,加在controller层中需要加密/解密的接口
SecurityParameter
package com.iliebe.web.annotation; import org.springframework.web.bind.annotation.Mapping; import java.lang.annotation.*; /** * @Date: 2020/6/27 下午3:59 * @Description: TODO(参数加密注解) */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Mapping @Documented public @interface SecurityParameter { /** * 入参是否解密,默认解密 */ boolean inDecode() default true; /** * 出参是否加密,默认加密 */ boolean outEncode() default true; /** * 方法路径名 */ String apiPath() default ""; }
4.配置类 配置Json数据编码为UTF-8
package com.iliebe.web.util; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import org.springframework.context.annotation.Configuration; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; /** * 配置返回的json字符 key为字符串时null则不返回 * * @Date: 2019/5/13 13:45 */ @Configuration public class JsonConfig { /** * 网络层需要标识gbk,utf-8 * @return */ public static ObjectMapper getObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); JavaTimeModule timeModule = new JavaTimeModule(); timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); timeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); objectMapper.registerModule(timeModule); return objectMapper; } }
5.加密/解密方法
package com.iliebe.web.util; import org.apache.commons.io.IOUtils; import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.Security; import java.util.Base64; /** * @Date: 2020/6/27 下午3:59 * @Description: TODO() */ public class EncryptForPKCS7Util { private static final String SECRET = "AES"; private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS7Padding"; static { Security.addProvider(new BouncyCastleProvider()); }
/** * AES加密ECB模式PKCS7Padding填充方式 * * @param str 字符串 * @param key 密钥 * @return 加密字符串 * @throws Exception 异常信息 */ public static String Encrypt(String str, String key) { try { Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, SECRET)); byte[] doFinal = cipher.doFinal(str.getBytes(StandardCharsets.UTF_8)); return new String(Base64.getEncoder().encode(doFinal)); } catch (Exception e) { e.printStackTrace(); } return ""; } /** * AES解密ECB模式PKCS7Padding填充方式 * * @param str 字符串 * @param key 密钥 * @return 解密字符串 * @throws Exception 异常信息 */ public static String Decrypt(String str, String key) { try { Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, SECRET)); byte[] doFinal = cipher.doFinal(Base64.getDecoder().decode(str)); return new String(doFinal); } catch (Exception e) { e.printStackTrace(); } return ""; } }
注意事项:
在Web项目中如果使用了全局异常拦截器,需要在全局异常捕获的地方也处理,如果是只针对某端的请求处理,可以通过请求header中加入标识,再根据标识判断是否加密/解密。
标签:return,String,Spring,org,AOP,import,com,public,参出 From: https://www.cnblogs.com/sansui/p/16824424.html