当前位置: 首页 > news >正文

深圳方维网站建设公司做网站需要做手机版吗

深圳方维网站建设公司,做网站需要做手机版吗,做母婴的网站,镇江市住房与城乡建设部网站1. 介绍 在我们日常的Java开发中#xff0c;免不了和其他系统的业务交互#xff0c;或者微服务之间的接口调用 如果我们想保证数据传输的安全#xff0c;对接口出参加密#xff0c;入参解密。 但是不想写重复代码#xff0c;我们可以提供一个通用starter#xff0c;提…1. 介绍 在我们日常的Java开发中免不了和其他系统的业务交互或者微服务之间的接口调用 如果我们想保证数据传输的安全对接口出参加密入参解密。 但是不想写重复代码我们可以提供一个通用starter提供通用加密解密功能 2. 前置知识 2.1 hutool-crypto加密解密工具 hutool-crypto提供了很多加密解密工具包括对称加密非对称加密摘要加密等等这不做详细介绍。 2.2 request流只能读取一次的问题 2.2.1 问题 在接口调用链中request的请求流只能调用一次处理之后如果之后还需要用到请求流获取数据就会发现数据为空。 比如使用了filter或者aop在接口处理之前获取了request中的数据对参数进行了校验那么之后就不能在获取request请求流了。 2.2.2 解决办法 继承HttpServletRequestWrapper将请求中的流copy一份复写getInputStream和getReader方法供外部使用。每次调用后的getInputStream方法都是从复制出来的二进制数组中进行获取这个二进制数组在对象存在期间一致存在。 使用Filter过滤器在一开始替换request为自己定义的可以多次读取流的request。 这样就实现了流的重复获取 InputStreamHttpServletRequestWrapper package xyz.hlh.cryptotest.utils;import org.apache.commons.io.IOUtils;import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStreamReader;/*** 请求流支持多次获取*/ public class InputStreamHttpServletRequestWrapper extends HttpServletRequestWrapper {/*** 用于缓存输入流*/private ByteArrayOutputStream cachedBytes;public InputStreamHttpServletRequestWrapper(HttpServletRequest request) {super(request);}Overridepublic ServletInputStream getInputStream() throws IOException {if (cachedBytes null) {// 首次获取流时将流放入 缓存输入流 中cacheInputStream();}// 从 缓存输入流 中获取流并返回return new CachedServletInputStream(cachedBytes.toByteArray());}Overridepublic BufferedReader getReader() throws IOException {return new BufferedReader(new InputStreamReader(getInputStream()));}/*** 首次获取流时将流放入 缓存输入流 中*/private void cacheInputStream() throws IOException {// 缓存输入流以便多次读取。为了方便, 我使用 org.apache.commons IOUtilscachedBytes new ByteArrayOutputStream();IOUtils.copy(super.getInputStream(), cachedBytes);}/*** 读取缓存的请求正文的输入流* p* 用于根据 缓存输入流 创建一个可返回的*/public static class CachedServletInputStream extends ServletInputStream {private final ByteArrayInputStream input;public CachedServletInputStream(byte[] buf) {// 从缓存的请求正文创建一个新的输入流input new ByteArrayInputStream(buf);}Overridepublic boolean isFinished() {return false;}Overridepublic boolean isReady() {return false;}Overridepublic void setReadListener(ReadListener listener) {}Overridepublic int read() throws IOException {return input.read();}}} HttpServletRequestInputStreamFilter package xyz.hlh.cryptotest.filter;import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import xyz.hlh.cryptotest.utils.InputStreamHttpServletRequestWrapper;import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import java.io.IOException;import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;/*** author HLH* description:* 请求流转换为多次读取的请求流 过滤器* email 17703595860163.com* date : Created in 2022/2/4 9:58*/ Component Order(HIGHEST_PRECEDENCE 1) // 优先级最高 public class HttpServletRequestInputStreamFilter implements Filter {Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {// 转换为可以多次获取流的requestHttpServletRequest httpServletRequest (HttpServletRequest) request;InputStreamHttpServletRequestWrapper inputStreamHttpServletRequestWrapper new InputStreamHttpServletRequestWrapper(httpServletRequest);// 放行chain.doFilter(inputStreamHttpServletRequestWrapper, response);} } 2.3 SpringBoot的参数校验validation 为了减少接口中业务代码之前的大量冗余的参数校验代码 SpringBoot-validation提供了优雅的参数校验入参都是实体类在实体类字段上加上对应注解就可以在进入方法之前进行参数校验如果参数错误会抛出错误BindException是不会进入方法的。 这种方法必须要求在接口参数上加注解Validated或者是Valid 但是很多清空下我们希望在代码中调用某个实体类的校验功能所以需要如下工具类。 ParamException package xyz.hlh.cryptotest.exception;import lombok.Getter;import java.util.List;/*** author HLH* description 自定义参数异常* email 17703595860163.com* date Created in 2021/8/10 下午10:56*/ Getter public class ParamException extends Exception {private final ListString fieldList;private final ListString msgList;public ParamException(ListString fieldList, ListString msgList) {this.fieldList fieldList;this.msgList msgList;} } ValidationUtils package xyz.hlh.cryptotest.utils;import xyz.hlh.cryptotest.exception.CustomizeException; import xyz.hlh.cryptotest.exception.ParamException;import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import java.util.LinkedList; import java.util.List; import java.util.Set;/*** author HLH* description 验证工具类* email 17703595860163.com* date Created in 2021/8/10 下午10:56*/ public class ValidationUtils {private static final Validator VALIDATOR Validation.buildDefaultValidatorFactory().getValidator();/*** 验证数据* param object 数据*/public static void validate(Object object) throws CustomizeException {SetConstraintViolationObject validate VALIDATOR.validate(object);// 验证结果异常throwParamException(validate);}/*** 验证数据(分组)* param object 数据* param groups 所在组*/public static void validate(Object object, Class? ... groups) throws CustomizeException {SetConstraintViolationObject validate VALIDATOR.validate(object, groups);// 验证结果异常throwParamException(validate);}/*** 验证数据中的某个字段(分组)* param object 数据* param propertyName 字段名称*/public static void validate(Object object, String propertyName) throws CustomizeException {SetConstraintViolationObject validate VALIDATOR.validateProperty(object, propertyName);// 验证结果异常throwParamException(validate);}/*** 验证数据中的某个字段(分组)* param object 数据* param propertyName 字段名称* param groups 所在组*/public static void validate(Object object, String propertyName, Class? ... groups) throws CustomizeException {SetConstraintViolationObject validate VALIDATOR.validateProperty(object, propertyName, groups);// 验证结果异常throwParamException(validate);}/*** 验证结果异常* param validate 验证结果*/private static void throwParamException(SetConstraintViolationObject validate) throws CustomizeException {if (validate.size() 0) {ListString fieldList new LinkedList();ListString msgList new LinkedList();for (ConstraintViolationObject next : validate) {fieldList.add(next.getPropertyPath().toString());msgList.add(next.getMessage());}throw new ParamException(fieldList, msgList);}}} 2.5 自定义starter 自定义starter步骤 创建工厂编写功能代码 声明自动配置类把需要对外提供的对象创建好通过配置类统一向外暴露 在resource目录下准备一个名为spring/spring.factories的文件以org.springframework.boot.autoconfigure.EnableAutoConfiguration为key自动配置类为value列表进行注册 2.6 RequestBodyAdvice和ResponseBodyAdvice RequestBodyAdvice是对请求的json串进行处理 一般使用环境是处理接口参数的自动解密 ResponseBodyAdvice是对请求相应的jsoin传进行处理一般用于相应结果的加密 3. 功能介绍 接口相应数据的时候返回的是加密之后的数据 接口入参的时候接收的是解密之后的数据但是在进入接口之前会自动解密取得对应的数据 4. 功能细节 加密解密使用对称加密的AES算法使用hutool-crypto模块进行实现 所有的实体类提取一个公共父类包含属性时间戳用于加密数据返回之后的实效性如果超过60分钟那么其他接口将不进行处理。 如果接口加了加密注解EncryptionAnnotation并且返回统一的json数据Result类则自动对数据进行加密。如果是继承了统一父类RequestBase的数据自动注入时间戳确保数据的时效性 如果接口加了解密注解DecryptionAnnotation并且参数使用RequestBody注解标注传入json使用统一格式RequestData类并且内容是继承了包含时间长的父类RequestBase则自动解密并且转为对应的数据类型 功能提供Springboot的starter实现开箱即用 5. 代码实现 5.1 项目结构 5.2 crypto-common 5.2.1 结构 5.3 crypto-spring-boot-starter 5.3.1 接口 5.3.2 重要代码 crypto.properties AES需要的参数配置 # 模式 cn.hutool.crypto.Mode crypto.modeCTS # 补码方式 cn.hutool.crypto.Mode crypto.paddingPKCS5Padding # 秘钥 crypto.keytestkey123456789 # 盐 crypto.ivtestiv1234567890 spring.factories 自动配置文件 org.springframework.boot.autoconfigure.EnableAutoConfiguration\ xyz.hlh.crypto.config.AppConfig CryptConfig AES需要的配置参数 package xyz.hlh.crypto.config;import cn.hutool.crypto.Mode; import cn.hutool.crypto.Padding; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.Getter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource;import java.io.Serializable;/*** author HLH* description: AES需要的配置参数* email 17703595860163.com* date : Created in 2022/2/4 13:16*/ Configuration ConfigurationProperties(prefix crypto) PropertySource(classpath:crypto.properties) Data EqualsAndHashCode Getter public class CryptConfig implements Serializable {private Mode mode;private Padding padding;private String key;private String iv;} AppConfig 自动配置类 package xyz.hlh.crypto.config;import cn.hutool.crypto.symmetric.AES; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;import javax.annotation.Resource; import java.nio.charset.StandardCharsets;/*** author HLH* description: 自动配置类* email 17703595860163.com* date : Created in 2022/2/4 13:12*/ Configuration public class AppConfig {Resourceprivate CryptConfig cryptConfig;Beanpublic AES aes() {return new AES(cryptConfig.getMode(), cryptConfig.getPadding(), cryptConfig.getKey().getBytes(StandardCharsets.UTF_8), cryptConfig.getIv().getBytes(StandardCharsets.UTF_8));}} DecryptRequestBodyAdvice 请求自动解密 package xyz.hlh.crypto.advice;import com.fasterxml.jackson.databind.ObjectMapper; import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.MethodParameter; import org.springframework.http.HttpInputMessage; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice; import xyz.hlh.crypto.annotation.DecryptionAnnotation; import xyz.hlh.crypto.common.exception.ParamException; import xyz.hlh.crypto.constant.CryptoConstant; import xyz.hlh.crypto.entity.RequestBase; import xyz.hlh.crypto.entity.RequestData; import xyz.hlh.crypto.util.AESUtil;import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.lang.reflect.Type;/*** author HLH* description: requestBody 自动解密* email 17703595860163.com* date : Created in 2022/2/4 15:12*/ ControllerAdvice public class DecryptRequestBodyAdvice implements RequestBodyAdvice {Autowiredprivate ObjectMapper objectMapper;/*** 方法上有DecryptionAnnotation注解的进入此拦截器* param methodParameter 方法参数对象* param targetType 参数的类型* param converterType 消息转换器* return true进入false跳过*/Overridepublic boolean supports(MethodParameter methodParameter, Type targetType, Class? extends HttpMessageConverter? converterType) {return methodParameter.hasMethodAnnotation(DecryptionAnnotation.class);}Overridepublic HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class? extends HttpMessageConverter? converterType) throws IOException {return inputMessage;}/*** 转换之后执行此方法解密赋值* param body spring解析完的参数* param inputMessage 输入参数* param parameter 参数对象* param targetType 参数类型* param converterType 消息转换类型* return 真实的参数*/SneakyThrowsOverridepublic Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class? extends HttpMessageConverter? converterType) {// 获取requestRequestAttributes requestAttributes RequestContextHolder.getRequestAttributes();ServletRequestAttributes servletRequestAttributes (ServletRequestAttributes) requestAttributes;if (servletRequestAttributes null) {throw new ParamException(request错误);}HttpServletRequest request servletRequestAttributes.getRequest();// 获取数据ServletInputStream inputStream request.getInputStream();RequestData requestData objectMapper.readValue(inputStream, RequestData.class);if (requestData null || StringUtils.isBlank(requestData.getText())) {throw new ParamException(参数错误);}// 获取加密的数据String text requestData.getText();// 放入解密之前的数据request.setAttribute(CryptoConstant.INPUT_ORIGINAL_DATA, text);// 解密String decryptText null;try {decryptText AESUtil.decrypt(text);} catch (Exception e) {throw new ParamException(解密失败);}if (StringUtils.isBlank(decryptText)) {throw new ParamException(解密失败);}// 放入解密之后的数据request.setAttribute(CryptoConstant.INPUT_DECRYPT_DATA, decryptText);// 获取结果Object result objectMapper.readValue(decryptText, body.getClass());// 强制所有实体类必须继承RequestBase类设置时间戳if (result instanceof RequestBase) {// 获取时间戳Long currentTimeMillis ((RequestBase) result).getCurrentTimeMillis();// 有效期 60秒long effective 60*1000;// 时间差long expire System.currentTimeMillis() - currentTimeMillis;// 是否在有效期内if (Math.abs(expire) effective) {throw new ParamException(时间戳不合法);}// 返回解密之后的数据return result;} else {throw new ParamException(String.format(请求参数类型%s 未继承%s, result.getClass().getName(), RequestBase.class.getName()));}}/*** 如果body为空转为空对象* param body spring解析完的参数* param inputMessage 输入参数* param parameter 参数对象* param targetType 参数类型* param converterType 消息转换类型* return 真实的参数*/SneakyThrowsOverridepublic Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class? extends HttpMessageConverter? converterType) {String typeName targetType.getTypeName();Class? bodyClass Class.forName(typeName);return bodyClass.newInstance();} } EncryptResponseBodyAdvice 相应自动加密 package xyz.hlh.crypto.advice;import cn.hutool.json.JSONUtil; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageConverter; 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; import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; import xyz.hlh.crypto.annotation.EncryptionAnnotation; import xyz.hlh.crypto.common.entity.Result; import xyz.hlh.crypto.common.exception.CryptoException; import xyz.hlh.crypto.entity.RequestBase; import xyz.hlh.crypto.util.AESUtil;import java.lang.reflect.Type;/*** author HLH* description:* email 17703595860163.com* date : Created in 2022/2/4 15:12*/ ControllerAdvice public class EncryptResponseBodyAdvice implements ResponseBodyAdviceResult? {Autowiredprivate ObjectMapper objectMapper;Overridepublic boolean supports(MethodParameter returnType, Class? extends HttpMessageConverter? converterType) {ParameterizedTypeImpl genericParameterType (ParameterizedTypeImpl)returnType.getGenericParameterType();// 如果直接是Result则返回if (genericParameterType.getRawType() Result.class returnType.hasMethodAnnotation(EncryptionAnnotation.class)) {return true;}if (genericParameterType.getRawType() ! ResponseEntity.class) {return false;}// 如果是ResponseEntityResultfor (Type type : genericParameterType.getActualTypeArguments()) {if (((ParameterizedTypeImpl) type).getRawType() Result.class returnType.hasMethodAnnotation(EncryptionAnnotation.class)) {return true;}}return false;}SneakyThrowsOverridepublic Result? beforeBodyWrite(Result? body, MethodParameter returnType, MediaType selectedContentType, Class? extends HttpMessageConverter? selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {// 加密Object data body.getData();// 如果data为空直接返回if (data null) {return body;}// 如果是实体并且继承了Request则放入时间戳if (data instanceof RequestBase) {((RequestBase)data).setCurrentTimeMillis(System.currentTimeMillis());}String dataText JSONUtil.toJsonStr(data);// 如果data为空直接返回if (StringUtils.isBlank(dataText)) {return body;}// 如果位数小于16报错if (dataText.length() 16) {throw new CryptoException(加密失败数据小于16位);}String encryptText AESUtil.encryptHex(dataText);return Result.builder().status(body.getStatus()).data(encryptText).message(body.getMessage()).build();} } 5.4 crypto-test 5.4.1 结构 5.4.2 重要代码 application.yml 配置文件 spring:mvc:format:date-time: yyyy-MM-dd HH:mm:ssdate: yyyy-MM-dd# 日期格式化jackson:date-format: yyyy-MM-dd HH:mm:ss Teacher 实体类 package xyz.hlh.crypto.entity;import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.hibernate.validator.constraints.Range;import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.io.Serializable; import java.util.Date;/*** author HLH* description: Teacher实体类使用SpringBoot的validation校验* email 17703595860163.com* date : Created in 2022/2/4 10:21*/ Data NoArgsConstructor AllArgsConstructor EqualsAndHashCode(callSuper true) public class Teacher extends RequestBase implements Serializable {NotBlank(message 姓名不能为空)private String name;NotNull(message 年龄不能为空)Range(min 0, max 150, message 年龄不合法)private Integer age;NotNull(message 生日不能为空)private Date birthday;} TestController 测试Controller package xyz.hlh.crypto.controller;import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import xyz.hlh.crypto.annotation.DecryptionAnnotation; import xyz.hlh.crypto.annotation.EncryptionAnnotation; import xyz.hlh.crypto.common.entity.Result; import xyz.hlh.crypto.common.entity.ResultBuilder; import xyz.hlh.crypto.entity.Teacher;/*** author HLH* description: 测试Controller* email 17703595860163.com* date : Created in 2022/2/4 9:16*/ RestController public class TestController implements ResultBuilder {/*** 直接返回对象不加密* param teacher Teacher对象* return 不加密的对象*/PostMapping(/get)public ResponseEntityResult? get(Validated RequestBody Teacher teacher) {return success(teacher);}/*** 返回加密后的数据* param teacher Teacher对象* return 返回加密后的数据 ResponseBodyResult格式*/PostMapping(/encrypt)EncryptionAnnotationpublic ResponseEntityResult? encrypt(Validated RequestBody Teacher teacher) {return success(teacher);}/*** 返回加密后的数据* param teacher Teacher对象* return 返回加密后的数据 Result格式*/PostMapping(/encrypt1)EncryptionAnnotationpublic Result? encrypt1(Validated RequestBody Teacher teacher) {return success(teacher).getBody();}/*** 返回解密后的数据* param teacher Teacher对象* return 返回解密后的数据*/PostMapping(/decrypt)DecryptionAnnotationpublic ResponseEntityResult? decrypt(Validated RequestBody Teacher teacher) {return success(teacher);}}
http://www.dnsts.com.cn/news/102645.html

相关文章:

  • 网站优化连云港哪家强?seo教学
  • wordpress游戏站东营网站建设怎么建设
  • 用php做的单车租赁网站上海关键词推广公司
  • 会声会影免费模板网站做产品网站淘宝百度
  • 厦门外贸网站建网站文章做百度排名
  • 无锡网站建设维护网站开发大作业报告
  • 未来做那个网站致富天津重型网站建设方案公司
  • 电子商务网站建设与维护项目五响应式WordPress企业主题
  • 制作竞拍网站西安seo外包费用
  • 郴州买房网站wordpress网站图片
  • 企业网站系统功能设计说明延安网站建设
  • 网站内页要不要加上关键词和描述微投票网站
  • 燕莎网站建设一个网站里有两个网页怎么做
  • 德州手机网站建设电话西安注册公司网上申请入口
  • 网站建设基本要点api软件
  • 报修网站模板上海网站seo策划
  • 什么叫网站的空间感计算机网站建设维护的基本知识
  • 如何制作营销网站模板手机网站用什么开发好
  • 外贸网站做哪些语言网络运维工程师需要考什么证书
  • 建设什么企业网站wordpress 华哥
  • 公司做网站费用入什么科目链接买卖平台
  • 做一组静态页面网站多少钱上海微网站
  • 网站备案百度站长提交wordpress显示全文
  • 网站的建设与管理暂行办法石家庄百度推广官网
  • 如何建立自己的微网站全国城市感染率排名
  • ppt网站模板想注册个网站做短租房投资多少钱
  • 南阳做玉器网站对网站开发流程的认识
  • 云建站步骤专业网站建设团队
  • 建设银行英文网站建设教育信息网站工作总结
  • 如何制作一个php网站源码建设微信商城网站的公司