登陆

极彩娱乐官网-SpringMVC恳求参数和呼应成果大局加密和解密

admin 2019-10-29 114人围观 ,发现0个评论

条件

前段时刻在做一个对外的网关项目,涉及到加密和解密模块,这儿详细分析解决计划和适用的场景。为了模仿实在的交互场景,先定制一下整个交互流程。第三方传输(包含恳求和呼应)数据报文包含三个部分:

  • 1、timestamp,long类型极彩娱乐官网-SpringMVC恳求参数和呼应成果大局加密和解密,时刻戳。
  • 2、data,String类型,实践的事务恳求数据转化成的Json字符串再进行加密得到的密文。
  • 3、sign,签名,生成规矩算法伪代码是SHA-256(data=xxx×tamp=11111),防篡改。

为了简略起见,加密和解密选用AES,对称秘钥为"throwable"。上面的场景和加解密比如只是是为了模仿实在场景,安全系数低,切勿直接用于出产环境。

现在还有一个当地要考虑,便是无法得知第三方怎么提交恳求数据,假定都是选用POST的Http恳求办法,提交报文的时分指定ContentType为application/json或许application/x-www-form-urlencoded,两种ContentType提交办法的恳求体是不相同的:

//application/x-www-form-urlencoded
timestamp=xxxx&data=yyyyyy&sign=zzzzzzz
//application/json
{"timestamp":xxxxxx,"data":"yyyyyyyy","sign":"zzzzzzz"}

终究一个要考虑的当地是,第三方强制要求部分接口需求用明文进行恳求,在供给一些接口办法的时分,答应运用明文交互。总结一下便是要做到以下三点:

  • 1、需求加解密的接口恳求参数要进行解密,呼应成果要进行加密。
  • 2、不需求加解密的接口能够用明文恳求。
  • 3、兼容ContentType为application/json或许application/x-www-form-urlencoded两种办法。

上面三种状况要一起兼容算是非常苛刻的场景,在出产环境中或许也是很少状况下才遇到,不过仍是能找到相对高雅的解决计划。先界说两个特定场景的接口:

1、下单接口(加密)

  • URL:/order/save
  • HTTP METHOD:POST
  • ContentType:application/x-www-form-urlencoded
  • 原始参数:orderId=yyyyyyyyy&userId=xxxxxxxxx&amount=zzzzzzzzz
  • 加密参数:timestamp=xxxx&data=yyyyyy&sign=zzzzzzz

2、订单查询接口(明文)

  • URL:/order/query
  • ContentType:application/json
  • HTTP METHOD:POST
  • 原始参数:{"userId":"xxxxxxxx"}

两个接口的ContentType不相同是为了成心杂乱化场景,鄙人面的可取计划中,做法是把application/x-www-form-urlencoded中的方式如xxx=yyy&aaa=bbb的表单参数和application/json中方式如{"key":"value"}的恳求参数一致作为application/json方式的参数处理,这样的话,咱们就能够直接在控制器办法中运用@RequestBody。

计划

咱们首要根据上面说到的加解密计划,供给一个加解密东西类:

public enum EncryptUtils {
/**
* SINGLETON
*/
SINGLETON;
private static final String SECRET = "throwable";
private static final String CHARSET = "UTF-8";
public String sha(String raw) throws Exception {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(raw.getBytes(CHARSET));
return Hex.encodeHexString(messageDigest.digest());
}
private Cipher createAesCipher() throws Exception {
return Cipher.getInstance("AES");
}

public String encryptByAes(String raw) throws Exception {
Cipher aesCipher = createAesCipher();
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128, new SecureRandom(SECRET.getBytes(CHARSET)));
SecretKey secretKey = keyGenerator.generateKey();
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
aesCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] bytes = aesCipher.doFinal(raw.getBytes(CHARSET));
return Hex.encodeHexString(bytes);
}
public String decryptByAes(String raw) throws Exception {
byte[] bytes = Hex.decodeHex(raw);
Cipher aesCipher = createAesCipher();
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128, new SecureRandom(SECRET.getBytes(CHARSET)));
SecretKey secretKey = keyGenerator.generateKey();
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
aesCipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
return new String(aesCipher.doFinal(bytes), CHARSET);
}
}

留意为了简化加解密操作引进了apache的codec依靠:


commons-codec
commons-codec
1.11

上面的加解密进程中要留意两点:

  • 1、加密后的成果是byte数组,要把二进制转化为十六进制字符串。
  • 2、解密的时分要把原始密文由十六进制转化为二进制的byte数组。

上面两点有必要留意,否则会发生乱码,这个和编码相关,详细能够看之前写的一篇博客。

不引荐的计划

其实最暴力的计划是直接定制每个控制器的办法参数类型,由于咱们能够和第三方商量哪些恳求途径需求加密,哪些是不需求加密,乃至哪些是application/x-www-form-urlencoded,哪些是application/json的恳求,这样咱们能够经过很多的硬编码到达终究的方针。举个比如:

@RestController
public class Controller1 {
@Autowired
private ObjectMapper objectMapper;
@PostMapping(value = "/order/save",
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity saveOrder(@RequestParam(name = "sign") String sign,
@RequestParam(name = "timestamp") Long timestamp,
@RequestParam(name = "data") String data) throws Exception {
EncryptModel model = new EncryptModel();
model.setData(data);
model.setTimestamp(timestamp);
model.setSign(sign);
String inRawSign = String.format("data=%s×tamp=%d", model.getData(), model.getTimestamp());
String inSign = EncryptUtils.SINGLETON.sha(inRawSign);
if (!inSign.equals(model.getSign())){
throw new IllegalArgumentException("验证参数签名失利!");
}
//这儿疏忽实践的事务逻辑,简略设置回来的data为一个map
Map result = new HashMap<>(8);
result.put("code", "200");
result.put("message", "success");
EncryptModel out = new EncryptModel();
out.setTimestamp(System.currentTimeMillis());
out.setData(EncryptUtils.SINGLETON.encryptByAes(objectMapper.writeValueAsString(result)));
String rawSign = String.format("data=%s×tamp=%d", out.getData(), out.getTimestamp());
out.setSign(EncryptUtils.SINGLETON.sha(rawSign));
return ResponseEntity.ok(out);
}
@PostMapping(value = "/order/query",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity queryOrder(@RequestBody User user){
Order order = new Order();
//这儿疏忽实践的事务逻辑
return ResponseEntity.ok(order);
}
}

这种做法能在短时刻完结对应的加解密功用,不需求加解密的接口不必引进相关的代码即可。缺陷非常显着,存在硬编码、代码冗余等问题,一旦接口增多,项意图保护难度大大提高。因而,这种做法是不可取的。

混合计划之Filter和SpringMVC的Http音讯转换器

这儿先说一点,这儿是在SpringMVC中运用Filter。由于要兼容两种contentType,咱们需求做到几点:

  • 1、修正恳求头的contentType为application/json。
  • 2、修正恳求体中的参数,一致转化为InputStream。
  • 3、定制URL规矩,差异需求加解密和不需求加解密的URL。

运用Filter有一个长处:不需求了解SpringMVC的流程,也不需求扩展SpringMVC的相关组件。缺陷也比较显着:

  • 1、假如需求区别加解密,只能经过URL规矩进行过滤。
  • 2、需求加密的接口的SpringMVC控制器的回来参数有必要是加密后的实体类,无法做到加密逻辑和事务逻辑彻底拆分,也便是解密逻辑对接纳的参数是无感知,可是加密逻辑对回来成果是有感知的。

PS:上面说到的几个需求修正恳求参数、恳求头号是由于特别场景的定制,所以假如无此场景能够直接看下面的"单纯的Json恳求参数和Json呼应成果"末节。流程大致如下:

编写Filter的完结和HttpServletRequestWrapper的完结:

//CustomEncryptFilter
@RequiredArgsConstructor
public class CustomEncryptFilter extends OncePerRequestFilter {
private final ObjectMapper objectMapper;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
//Content-Type
String contentType = request.getContentType();
String requestBody = null;
boolean shouldEncrypt = false;
if (StringUtils.substringMatch(contentType, 0, MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
shouldEncrypt = true;
requestBody = convertFormToString(request);
} else if (StringUtils.substringMatch(contentType, 0, MediaType.APPLICATION_JSON_VALUE)) {
shouldEncrypt = true;
requestBody = convertInputStreamToString(request.getInputStream());
}
if (!shouldEncrypt) {
filterChain.doFilter(request, response);
} else {
CustomEncryptHttpWrapper wrapper = new CustomEncryptHttpWrapper(request, requestBody);
wrapper.putHeader("Content-Type", MediaType.APPLICATION_PROBLEM_JSON_UTF8_VALUE);
filterChain.doFilter(wrapper, response);
}
}
private String convertFormToString(HttpServletRequest request) {
Map result = new HashMap<>(8);
Enumeration parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String name = parameterNames.nextElement();
result.put(name, request.getParameter(name));
}
try {
return objectMapper.writeValueAsString(result);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(e);
}
}
private String convertInputStreamToString(InputStream inputStream) throws IOException {
return StreamUtils.copyToString(inputStream, Charset.forName("UTF-8"));
}
}
//CustomEncryptHttpWrapper
public class CustomEncryptHttpWrapper extends HttpServletRequestWrapper {
private final Map headers = new HashMap<>(8);
private final byte[] data;
public CustomEncryptHttpWrapper(HttpServletRequest request, String content) {
super(request);
data = content.getBytes(Charset.forName("UTF-8"));
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String key = headerNames.nextElement();
headers.put(key, request.getHeader(key));
}
}
public void putHeader(String key, String value) {
headers.put(key, value);
}
@Override
public String getHeader(String name) {
return headers.get(name);
}
@Override
public Enumeration getHeaders(String name) {
return Collections.enumeration(Collections.singletonList(headers.get(name)));
}
@Override
public Enumeration getHeaderNames() {
return Collections.enumeration(headers.keySet());
}
@Override
public ServletInputStream getInputStream() throws IOException {
ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return !isReady();
}
@Override
public boolean isReady() {
return inputStream.available() > 0;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() throws IOException {
return inputStream.read();
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return super.getReader();
}
}
//CustomEncryptConfiguration
@Configuration
public class CustomEncryptConfiguration {
@Bean
public FilterRegistrationBean customEncryptFilter(ObjectMapper objectMapper){
FilterRegistrationBean bean = new FilterRegistrationBean<>(new CustomEncryptFilter(objectMapper));
bean.addUrlPatterns("/e/*");
return bean;
}
}

控制器代码:

//可加密的,空接口
public interface Encryptable {
}
@Data
public class Order implements Encryptable{
private Long userId;
}
@Data
public class EncryptResponse implements Encryptable {

private Integer code;
private T data;
}
@RequiredArgsConstructor
@RestController
public class Controller {
private final ObjectMapper objectMapper;
@PostMapping(value = "/e/order/save",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public EncryptResponse saveOrder(@RequestBody Order order) throws Exception {
//这儿疏忽实践的事务逻辑,简略设置回来的data为一个map
EncryptResponse response = new EncryptResponse<>();
response.setCode(200);
response.setData(order);
return response;
}
@PostMapping(value = "/c/order/query",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity queryOrder(@RequestBody User user) {
Order order = new Order();
//这儿疏忽实践的事务逻辑
return ResponseEntity.ok(order);
}
}

这儿或许有人有疑问,为什么不在Filter做加解密的操作?由于考虑到场景太特别,要兼容两种方式的表单提交参数,假如在Filter做加解密操作,会影响到Controller的编码,这就违反了大局加解密不影响到里层事务代码的方针。上面的Filter只会阻拦URL满意/e/*的恳求,因而查询接口/c/order/query不会受到影响。这儿运用了标识接口用于决议恳求参数或许呼应成果是否需求加解密,也便是只需求在HttpMessageConverter中判别恳求参数的类型或许呼应成果的类型是否加解密标识接口的子类:

@RequiredArgsConstructor
public class CustomEncryptHttpMessageConverter extends MappingJackson2HttpMessageConverter {
private final ObjectMapper objectMapper;
@Override
protected Object readInternal(Class
throws IOException, HttpMessageNotReadableException {
if (Encryptable.class.isAssignableFrom(clazz)) {
EncryptModel in = objectMapper.readValue(StreamUtils.copyToByteArray(inputMessage.getBody()), EncryptModel.class);
String inRawSign = String.format("data极彩娱乐官网-SpringMVC恳求参数和呼应成果大局加密和解密=%s×tamp=%d", in.getData(), in.getTimestamp());
String inSign;
try {
inSign = EncryptUtils.SINGLETON.sha(inRawSign);
} catch (Exception e) {
throw new IllegalArgumentException("验证参数签名失利!");
}
if (!inSign.equals(in.getSign())) {
throw new IllegalArgumentE极彩娱乐官网-SpringMVC恳求参数和呼应成果大局加密和解密xception("验证参数签名失利!");
}
try {
return objectMapper.readValue(EncryptUtils.SINGLETON.decryptByAes(in.getData()), clazz);
} catch (Exception e) {
throw new IllegalArgumentException("解密失利!");
}
} else {
return super.readInternal(clazz, inputMessage);
}
}
@Override
protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
Class
if (Encryptable.class.isAssignableFrom(clazz)) {
EncryptModel out = new EncryptModel();
out.setTimestamp(System.currentTimeMillis());
try {
out.setData(EncryptUtils.SINGLETON.encryptByAes(objectMapper.writeValueAsString(object)));
String rawSign = String.format("data=%s×tamp=%d", out.getData(), out.getTimestamp());
out.setSign(EncryptUtils.SINGLETON.sha(rawSign));
} catch (Exception e) {
throw new IllegalArgumentEx极彩娱乐官网-SpringMVC恳求参数和呼应成果大局加密和解密ception("参数签名失利!");
}
super.writeInternal(out, type, outputMessage);
} else {
super.writeInternal(object, type, outputMessage);
}
}
}

自完结的HttpMessageConverter首要需求判别恳求参数的类型和回来值的类型,然后判别是否需求进行加解密。

单纯的Json恳求参数和Json呼应成果的加解密处理最佳实践

一般状况下,对接方的恳求参数和呼应成果是彻底标准一致运用Json(contentType指定为application/json,运用@RequestBody接纳参数),那么一切的工作就会变得简略,由于不需求考虑恳求参数由xxx=yyy&aaa=bbb转换为InputStream再交给SpringMVC处理,因而咱们只需求供给一个MappingJackson2HttpMessageConverter子类完结(承继它而且掩盖对应办法,增加加解密特性)。咱们仍是运用标识接口用于决议恳求参数或许呼应成果是否需求加解密:

@RequiredArgsConstructor
public class CustomEncryptHttpMessageConverter extends MappingJackson2HttpMessageConverter {
private final ObjectMapper objectMapper;
@Override
protected Object readInternal(Class
throws IOException, HttpMessageNotReadableException {
if (Encryptable.class.isAssignableFrom(clazz)) {
EncryptModel in = objectMapper.readValue(StreamUtils.copyToByteArray(inputMessage.getBody()), EncryptModel.class);
String inRawSign = String.format("data=%s×tamp=%d", in.getData(), in.getTimestamp());
String inSign;
try {
inSign = EncryptUtils.SINGLETON.sha(inRawSign);
} catch (Exception e) {
throw new IllegalArgumentException("验证参数签名失利!");
}
if (!inSign.equals(in.getSign())) {
throw new IllegalArgumentException("验证参数签名失利!");
}
try {
return objectMapper.readValue(EncryptUtils.SINGLETON.decryptByAes(in.getData()), clazz);
} catch (Exception e) {
throw new IllegalArgumentException("解密失利!");
}
} else {
return super.readInternal(clazz, inputMessage);
}
}
@Override
protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
Class
if (Encryptable.class.isAssignableFrom(clazz)) {
EncryptModel out = new EncryptModel();
out.setTimestamp(System.currentTimeMillis());
try {
out.setData(EncryptUtils.SINGLETON.encryptByAes(objectMapper.writeValueAsString(object)));
String rawSign = String.format("data=%s×tamp=%d", out.getData(), out.getTimestamp());
out.setSign(EncryptUtils.SINGLETON.sha(rawSign));
} catch (Exception e) {
throw new IllegalArgumentException("参数签名失利!");
}
super.writeInternal(out, type, outputMessage);
} else {
super.writeInternal(object, type, outputMessag极彩娱乐官网-SpringMVC恳求参数和呼应成果大局加密和解密e);
}
}
}

没错,代码是复制上一节供给的HttpMessageConverter完结,然后控制器办法的参数运用@RequestBody注解而且类型完结加解密标识接口Encryptable即可,回来值的类型也需求完结加解密标识接口Encryptable。这种做法能够让控制器的代码对加解密彻底无感知。当然,也能够不改动本来的MappingJackson2HttpMessageConverter完结,运用RequestBodyAdvice和ResponseBodyAdvice完结相同的功用:

@RequiredArgsConstructor
public class CustomRequestBodyAdvice extends RequestBodyAdviceAdapter {
private final ObjectMapper objectMapper;
@Override
public boolean supports(MethodParameter methodParameter, Type targetType,
Class
Class
return Encryptable.class.isAssignableFrom(clazz);
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
Class
Class
if (Encryptable.class.isAssignableFrom(clazz)) {
String content = StreamUtils.copyToString(inputMessage.getBody(), Charset.forName("UTF-8"));
EncryptModel in = objectMapper.readValue(content, EncryptModel.class);
String inRawSign = String.format("data=%s×tamp=%d", in.getData(), in.getTimestamp());
String inSign;
try {
inSign = EncryptUtils.SINGLETON.sha(inRawSign);
} catch (Exception e) {
throw new IllegalArgumentException("验证参数签名失利!");
}
if (!inSign.equals(in.getSign())) {
throw new IllegalArgumentException("验证参数签名失利!");
}
ByteArrayInputStream inputStream = new ByteArrayInputStream(in.getData().getBytes(Charset.forName("UTF-8")));
return new MappingJacksonInputMessage(inputStream, inputMessage.getHeaders());
} else {
return super.beforeBodyRead(inputMessage, parameter, targetType, converterType);
}
}
}
@RequiredArgsConstructor
public class CustomResponseBodyAdvice extends JsonViewResponseBodyAdvice {
private final ObjectMapper objectMapper;
@Override
public boolean supports(MethodParameter returnType, Class
Class
return Encryptable.class.isAssignableFrom(parameterType);
}
@Override
protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,
MethodParameter returnType, ServerHttpRequest request,
ServerHttpResponse response) {
Class
if (Encryptable.class.isAssignableFrom(parameterType)) {
EncryptModel out = new EncryptModel();
out.setTimestamp(System.currentTimeMillis());
try {
out.setData(EncryptUtils.SINGLETON.encryptByAes(objectMapper.writeValueAsString(bodyContainer.getValue())));
String rawSign = String.format("data=%s×tamp=%d", out.getData(), out.getTimestamp());
out.setSign(EncryptUtils.SINGLETON.sha(rawSign));
out.setSign(EncryptUtils.SINGLETON.sha(rawSign));
} catch (Exception e) {
throw new IllegalArgumentException("参数签名失利!");
}
} else {
super.beforeBodyWriteInternal(bodyContainer, contentType, returnType, request, response);
}
}
}

单纯的application/x-www-form-urlencoded表单恳求参数和Json呼应成果的加解密处理最佳实践

一般状况下,对接方的恳求参数彻底选用application/x-www-form-urlencoded表单恳求参数回来成果悉数依照Json接纳,咱们也能够经过一个HttpMessageConverter完结就完结加解密模块。

public class FormHttpMessageConverter implements HttpMessageConverter {
private final List mediaTypes;
private final ObjectMapper objectMapper;
public FormHttpMessageConverter(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
this.mediaTypes = new ArrayList<>(1);
this.mediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
}
@Override
public boolean canRead(Class
return Encryptable.class.isAssignableFrom(clazz) && mediaTypes.contains(mediaType);
}
@Override
public boolean canWrite(Class
return Encryptable.class.isAssignableFrom(clazz) && mediaTypes.contains(mediaType);
}
@Override
public List getSupportedMediaTypes() {
return mediaTypes;
}
@Override
public Object read(Class
IOException, HttpMessageNotReadableException {
if (Encryptable.class.isAssignableFrom(clazz)) {
String content = StreamUtils.copyToString(inputMessage.getBody(), Charset.forName("UTF-8"));
EncryptModel in = objectMapper.readValue(content, EncryptModel.class);
String inRawSign = String.format("data=%s×tamp=%d", in.getData(), in.getTimestamp());
String inSign;
try {
inSign = EncryptUtils.SINGLETON.sha(inRawSign);
} catch (Exception e) {
throw new IllegalArgumentException("验证参数签名失利!");
}
if (!inSign.equals(in.getSign())) {
throw new IllegalArgumentException("验证参数签名失利!");
}
try {
return objectMapper.readValue(EncryptUtils.SINGLETON.decryptByAes(in.getData()), clazz);
} catch (Exception e) {
throw new IllegalArgumentException("解密失利!");
}
} else {
MediaType contentType = inputMessage.getHeaders().getContentType();
Charset charset = (contentType != null && contentType.getCharset() != null ?
contentType.getCharset() : Charset.forName("UTF-8"));
String body = StreamUtils.copyToString(inputMessage.getBody(), charset);
String[] pairs = StringUtils.tokenizeToStringArray(body, "&");
MultiValueMap result = new LinkedMultiValueMap<>(pairs.length);
for (String pair : pairs) {
int idx = pair.indexOf('=');
if (idx == -1) {
result.add(URLDecoder.decode(pair, charset.name()), null);
} else {
String name = URLDecoder.decode(pair.substring(0, idx), charset.name());
String value = URLDecoder.decode(pair.substring(idx + 1), charset.name());
result.add(name, value);
}
}
return result;
}
}
@Override
public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
Class
if (Encryptable.class.isAssignableFrom(clazz)) {
EncryptModel out = new EncryptMod充满抛瓦el();
out.setTimestamp(System.currentTimeMillis());
try {
out.setData(EncryptUtils.SINGLETON.encryptByAes(objectMapper.writeValueAsString(o)));
String rawSign = String.format("data=%s×tamp=%d", out.getData(), out.getTimestamp());
out.setSign(EncryptUtils.SINGLETON.sha(rawSign));
StreamUtils.copy(objectMapper.writeValueAsString(out)
.getBytes(Charset.forName("UTF-8")), outputMessage.getBody());
} catch (Exception e) {
throw new IllegalArgumentException("参数签名失利!");
}
} else {
String out = objectMapper.writeValueAsString(o);
StreamUtils.copy(out.getBytes(Charset.forName("UTF-8")), outputMessage.getBody());
}
}
}

上面的HttpMessageConverter的完结能够参阅org.springframework.http.converter.FormHttpMessageConverte极彩娱乐官网-SpringMVC恳求参数和呼应成果大局加密和解密r。

小结

这篇文章强行杂乱化了实践的状况(可是在实践中真的碰到过),一般状况下,现在盛行运用Json进行数据传输,在SpringMVC项目中,咱们只需求针对性地改造MappingJackson2HttpMessageConverter即可(承继而且增加特性),假如对SpringMVC的源码相对了解的话,直接增加自界说的RequestBodyAdvice(RequestBodyAdviceAdapter)和ResponseBodyAdvice(JsonViewResponseBodyAdvice)完结也能够到达意图。至于为什么运用HttpMessageConverter做加解密功用,这儿根据SpringMVC源码的对恳求参数处理的进程整理了一张处理流程图:

上面流程最中心的代码能够看AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters和HandlerMethodArgumentResolverComposite#resolveArgument,究竟源码不会哄人。控制器办法回来值的处理是根本对称的,阅览起来也比较轻松。

请关注微信公众号
微信二维码
不容错过
Powered By Z-BlogPHP