0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

SpringBoot接口加密解密,新姿勢(shì)!

jf_ro2CN3Fa ? 來(lái)源:芋道源碼 ? 作者:芋道源碼 ? 2022-11-29 11:13 ? 次閱讀


1. 介紹

在我們?nèi)粘5?a href="http://ttokpm.com/v/tag/852/" target="_blank">Java開發(fā)中,免不了和其他系統(tǒng)的業(yè)務(wù)交互,或者微服務(wù)之間的接口調(diào)用

如果我們想保證數(shù)據(jù)傳輸?shù)陌踩?,?duì)接口出參加密,入?yún)⒔饷堋?/p>

但是不想寫重復(fù)代碼,我們可以提供一個(gè)通用starter,提供通用加密解密功能

基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

  • 項(xiàng)目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 視頻教程:https://doc.iocoder.cn/video/

2. 前置知識(shí)

2.1 hutool-crypto加密解密工具

hutool-crypto提供了很多加密解密工具,包括對(duì)稱加密,非對(duì)稱加密,摘要加密等等,這不做詳細(xì)介紹。

2.2 request流只能讀取一次的問題

2.2.1 問題:

在接口調(diào)用鏈中,request的請(qǐng)求流只能調(diào)用一次,處理之后,如果之后還需要用到請(qǐng)求流獲取數(shù)據(jù),就會(huì)發(fā)現(xiàn)數(shù)據(jù)為空。

比如使用了filter或者aop在接口處理之前,獲取了request中的數(shù)據(jù),對(duì)參數(shù)進(jìn)行了校驗(yàn),那么之后就不能在獲取request請(qǐng)求流了

2.2.2 解決辦法

繼承HttpServletRequestWrapper,將請(qǐng)求中的流copy一份,復(fù)寫getInputStream和getReader方法供外部使用。每次調(diào)用后的getInputStream方法都是從復(fù)制出來(lái)的二進(jìn)制數(shù)組中進(jìn)行獲取,這個(gè)二進(jìn)制數(shù)組在對(duì)象存在期間一致存在。

使用Filter過濾器,在一開始,替換request為自己定義的可以多次讀取流的request。

這樣就實(shí)現(xiàn)了流的重復(fù)獲取

InputStreamHttpServletRequestWrapper

packagexyz.hlh.cryptotest.utils;

importorg.apache.commons.io.IOUtils;

importjavax.servlet.ReadListener;
importjavax.servlet.ServletInputStream;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletRequestWrapper;
importjava.io.BufferedReader;
importjava.io.ByteArrayInputStream;
importjava.io.ByteArrayOutputStream;
importjava.io.IOException;
importjava.io.InputStreamReader;

/**
*請(qǐng)求流支持多次獲取
*/
publicclassInputStreamHttpServletRequestWrapperextendsHttpServletRequestWrapper{

/**
*用于緩存輸入流
*/
privateByteArrayOutputStreamcachedBytes;

publicInputStreamHttpServletRequestWrapper(HttpServletRequestrequest){
super(request);
}

@Override
publicServletInputStreamgetInputStream()throwsIOException{
if(cachedBytes==null){
//首次獲取流時(shí),將流放入緩存輸入流中
cacheInputStream();
}

//從緩存輸入流中獲取流并返回
returnnewCachedServletInputStream(cachedBytes.toByteArray());
}

@Override
publicBufferedReadergetReader()throwsIOException{
returnnewBufferedReader(newInputStreamReader(getInputStream()));
}

/**
*首次獲取流時(shí),將流放入緩存輸入流中
*/
privatevoidcacheInputStream()throwsIOException{
//緩存輸入流以便多次讀取。為了方便, 我使用 org.apache.commons IOUtils
cachedBytes=newByteArrayOutputStream();
IOUtils.copy(super.getInputStream(),cachedBytes);
}

/**
*讀取緩存的請(qǐng)求正文的輸入流
*

*用于根據(jù)緩存輸入流創(chuàng)建一個(gè)可返回的 */ publicstaticclassCachedServletInputStreamextendsServletInputStream{ privatefinalByteArrayInputStreaminput; publicCachedServletInputStream(byte[]buf){ //從緩存的請(qǐng)求正文創(chuàng)建一個(gè)新的輸入流 input=newByteArrayInputStream(buf); } @Override publicbooleanisFinished(){ returnfalse; } @Override publicbooleanisReady(){ returnfalse; } @Override publicvoidsetReadListener(ReadListenerlistener){ } @Override publicintread()throwsIOException{ returninput.read(); } } }

HttpServletRequestInputStreamFilter

packagexyz.hlh.cryptotest.filter;

importorg.springframework.core.annotation.Order;
importorg.springframework.stereotype.Component;
importxyz.hlh.cryptotest.utils.InputStreamHttpServletRequestWrapper;

importjavax.servlet.Filter;
importjavax.servlet.FilterChain;
importjavax.servlet.ServletException;
importjavax.servlet.ServletRequest;
importjavax.servlet.ServletResponse;
importjavax.servlet.http.HttpServletRequest;
importjava.io.IOException;

importstaticorg.springframework.core.Ordered.HIGHEST_PRECEDENCE;

/**
*@authorHLH
*@description:
*請(qǐng)求流轉(zhuǎn)換為多次讀取的請(qǐng)求流過濾器
*@email17703595860@163.com
*@date:Createdin2022/2/49:58
*/
@Component
@Order(HIGHEST_PRECEDENCE+1)//優(yōu)先級(jí)最高
publicclassHttpServletRequestInputStreamFilterimplementsFilter{

@Override
publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{

//轉(zhuǎn)換為可以多次獲取流的request
HttpServletRequesthttpServletRequest=(HttpServletRequest)request;
InputStreamHttpServletRequestWrapperinputStreamHttpServletRequestWrapper=newInputStreamHttpServletRequestWrapper(httpServletRequest);

//放行
chain.doFilter(inputStreamHttpServletRequestWrapper,response);
}
}

2.3 SpringBoot的參數(shù)校驗(yàn)validation

為了減少接口中,業(yè)務(wù)代碼之前的大量冗余的參數(shù)校驗(yàn)代碼

SpringBoot-validation提供了優(yōu)雅的參數(shù)校驗(yàn),入?yún)⒍际菍?shí)體類,在實(shí)體類字段上加上對(duì)應(yīng)注解,就可以在進(jìn)入方法之前,進(jìn)行參數(shù)校驗(yàn),如果參數(shù)錯(cuò)誤,會(huì)拋出錯(cuò)誤BindException,是不會(huì)進(jìn)入方法的。

這種方法,必須要求在接口參數(shù)上加注解@Validated或者是@Valid

但是很多清空下,我們希望在代碼中調(diào)用某個(gè)實(shí)體類的校驗(yàn)功能,所以需要如下工具類

ParamException

packagexyz.hlh.cryptotest.exception;

importlombok.Getter;

importjava.util.List;

/**
*@authorHLH
*@description自定義參數(shù)異常
*@email17703595860@163.com
*@dateCreatedin2021/8/10下午10:56
*/
@Getter
publicclassParamExceptionextendsException{

privatefinalListfieldList;
privatefinalListmsgList;

publicParamException(ListfieldList,ListmsgList){
this.fieldList=fieldList;
this.msgList=msgList;
}
}

ValidationUtils

packagexyz.hlh.cryptotest.utils;

importxyz.hlh.cryptotest.exception.CustomizeException;
importxyz.hlh.cryptotest.exception.ParamException;

importjavax.validation.ConstraintViolation;
importjavax.validation.Validation;
importjavax.validation.Validator;
importjava.util.LinkedList;
importjava.util.List;
importjava.util.Set;

/**
*@authorHLH
*@description驗(yàn)證工具類
*@email17703595860@163.com
*@dateCreatedin2021/8/10下午10:56
*/
publicclassValidationUtils{

privatestaticfinalValidatorVALIDATOR=Validation.buildDefaultValidatorFactory().getValidator();

/**
*驗(yàn)證數(shù)據(jù)
*@paramobject數(shù)據(jù)
*/
publicstaticvoidvalidate(Objectobject)throwsCustomizeException{

Set>validate=VALIDATOR.validate(object);

//驗(yàn)證結(jié)果異常
throwParamException(validate);
}

/**
*驗(yàn)證數(shù)據(jù)(分組)
*@paramobject數(shù)據(jù)
*@paramgroups所在組
*/
publicstaticvoidvalidate(Objectobject,Class...groups)throwsCustomizeException{

Set>validate=VALIDATOR.validate(object,groups);

//驗(yàn)證結(jié)果異常
throwParamException(validate);
}

/**
*驗(yàn)證數(shù)據(jù)中的某個(gè)字段(分組)
*@paramobject數(shù)據(jù)
*@parampropertyName字段名稱
*/
publicstaticvoidvalidate(Objectobject,StringpropertyName)throwsCustomizeException{
Set>validate=VALIDATOR.validateProperty(object,propertyName);

//驗(yàn)證結(jié)果異常
throwParamException(validate);

}

/**
*驗(yàn)證數(shù)據(jù)中的某個(gè)字段(分組)
*@paramobject數(shù)據(jù)
*@parampropertyName字段名稱
*@paramgroups所在組
*/
publicstaticvoidvalidate(Objectobject,StringpropertyName,Class...groups)throwsCustomizeException{

Set>validate=VALIDATOR.validateProperty(object,propertyName,groups);

//驗(yàn)證結(jié)果異常
throwParamException(validate);

}

/**
*驗(yàn)證結(jié)果異常
*@paramvalidate驗(yàn)證結(jié)果
*/
privatestaticvoidthrowParamException(Set>validate)throwsCustomizeException{
if(validate.size()>0){
ListfieldList=newLinkedList<>();
ListmsgList=newLinkedList<>();
for(ConstraintViolationnext:validate){
fieldList.add(next.getPropertyPath().toString());
msgList.add(next.getMessage());
}

thrownewParamException(fieldList,msgList);
}
}

}

		

2.4 自定義starter

自定義starter步驟

  • 創(chuàng)建工廠,編寫功能代碼

  • 聲明自動(dòng)配置類,把需要對(duì)外提供的對(duì)象創(chuàng)建好,通過配置類統(tǒng)一向外暴露

  • 在resource目錄下準(zhǔn)備一個(gè)名為spring/spring.factories的文件,以org.springframework.boot.autoconfigure.EnableAutoConfiguration為key,自動(dòng)配置類為value列表,進(jìn)行注冊(cè)

2.5 RequestBodyAdvice和ResponseBodyAdvice

  • RequestBodyAdvice是對(duì)請(qǐng)求的json串進(jìn)行處理, 一般使用環(huán)境是處理接口參數(shù)的自動(dòng)解密

  • ResponseBodyAdvice是對(duì)請(qǐng)求相應(yīng)的jsoin傳進(jìn)行處理,一般用于相應(yīng)結(jié)果的加密

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

  • 項(xiàng)目地址:https://github.com/YunaiV/yudao-cloud
  • 視頻教程:https://doc.iocoder.cn/video/

3. 功能介紹

接口相應(yīng)數(shù)據(jù)的時(shí)候,返回的是加密之后的數(shù)據(jù) 接口入?yún)⒌臅r(shí)候,接收的是解密之后的數(shù)據(jù),但是在進(jìn)入接口之前,會(huì)自動(dòng)解密,取得對(duì)應(yīng)的數(shù)據(jù)

4. 功能細(xì)節(jié)

加密解密使用對(duì)稱加密的AES算法,使用hutool-crypto模塊進(jìn)行實(shí)現(xiàn)

所有的實(shí)體類提取一個(gè)公共父類,包含屬性時(shí)間戳,用于加密數(shù)據(jù)返回之后的實(shí)效性,如果超過60分鐘,那么其他接口將不進(jìn)行處理。

如果接口加了加密注解EncryptionAnnotation,并且返回統(tǒng)一的json數(shù)據(jù)Result類,則自動(dòng)對(duì)數(shù)據(jù)進(jìn)行加密。如果是繼承了統(tǒng)一父類RequestBase的數(shù)據(jù),自動(dòng)注入時(shí)間戳,確保數(shù)據(jù)的時(shí)效性

如果接口加了解密注解DecryptionAnnotation,并且參數(shù)使用RequestBody注解標(biāo)注,傳入json使用統(tǒng)一格式RequestData類,并且內(nèi)容是繼承了包含時(shí)間長(zhǎng)的父類RequestBase,則自動(dòng)解密,并且轉(zhuǎn)為對(duì)應(yīng)的數(shù)據(jù)類型

功能提供Springboot的starter,實(shí)現(xiàn)開箱即用

5. 代碼實(shí)現(xiàn)

https://gitee.com/springboot-hlh/spring-boot-csdn/tree/master/09-spring-boot-interface-crypto

5.1 項(xiàng)目結(jié)構(gòu)

28f5d220-6f8b-11ed-8abf-dac502259ad0.png

5.2 crypto-common

5.2.1 結(jié)構(gòu)

29035a26-6f8b-11ed-8abf-dac502259ad0.png

5.3 crypto-spring-boot-starter

5.3.1 接口

291bc674-6f8b-11ed-8abf-dac502259ad0.png

5.3.2 重要代碼

crypto.properties AES需要的參數(shù)配置

#模式cn.hutool.crypto.Mode
crypto.mode=CTS
#補(bǔ)碼方式cn.hutool.crypto.Mode
crypto.padding=PKCS5Padding
#秘鑰
crypto.key=testkey123456789
#鹽
crypto.iv=testiv1234567890

spring.factories 自動(dòng)配置文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=
xyz.hlh.crypto.config.AppConfig

CryptConfig AES需要的配置參數(shù)

packagexyz.hlh.crypto.config;

importcn.hutool.crypto.Mode;
importcn.hutool.crypto.Padding;
importlombok.Data;
importlombok.EqualsAndHashCode;
importlombok.Getter;
importorg.springframework.boot.context.properties.ConfigurationProperties;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.context.annotation.PropertySource;

importjava.io.Serializable;

/**
*@authorHLH
*@description:AES需要的配置參數(shù)
*@email17703595860@163.com
*@date:Createdin2022/2/413:16
*/
@Configuration
@ConfigurationProperties(prefix="crypto")
@PropertySource("classpath:crypto.properties")
@Data
@EqualsAndHashCode
@Getter
publicclassCryptConfigimplementsSerializable{

privateModemode;
privatePaddingpadding;
privateStringkey;
privateStringiv;

}

AppConfig 自動(dòng)配置類

packagexyz.hlh.crypto.config;

importcn.hutool.crypto.symmetric.AES;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;

importjavax.annotation.Resource;
importjava.nio.charset.StandardCharsets;

/**
*@authorHLH
*@description:自動(dòng)配置類
*@email17703595860@163.com
*@date:Createdin2022/2/413:12
*/
@Configuration
publicclassAppConfig{

@Resource
privateCryptConfigcryptConfig;

@Bean
publicAESaes(){
returnnewAES(cryptConfig.getMode(),cryptConfig.getPadding(),cryptConfig.getKey().getBytes(StandardCharsets.UTF_8),cryptConfig.getIv().getBytes(StandardCharsets.UTF_8));
}

}

DecryptRequestBodyAdvice 請(qǐng)求自動(dòng)解密

packagexyz.hlh.crypto.advice;

importcom.fasterxml.jackson.databind.ObjectMapper;
importlombok.SneakyThrows;
importorg.apache.commons.lang3.StringUtils;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.core.MethodParameter;
importorg.springframework.http.HttpInputMessage;
importorg.springframework.http.converter.HttpMessageConverter;
importorg.springframework.web.bind.annotation.ControllerAdvice;
importorg.springframework.web.context.request.RequestAttributes;
importorg.springframework.web.context.request.RequestContextHolder;
importorg.springframework.web.context.request.ServletRequestAttributes;
importorg.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
importxyz.hlh.crypto.annotation.DecryptionAnnotation;
importxyz.hlh.crypto.common.exception.ParamException;
importxyz.hlh.crypto.constant.CryptoConstant;
importxyz.hlh.crypto.entity.RequestBase;
importxyz.hlh.crypto.entity.RequestData;
importxyz.hlh.crypto.util.AESUtil;

importjavax.servlet.ServletInputStream;
importjavax.servlet.http.HttpServletRequest;
importjava.io.IOException;
importjava.lang.reflect.Type;

/**
*@authorHLH
*@description:requestBody自動(dòng)解密
*@email17703595860@163.com
*@date:Createdin2022/2/415:12
*/
@ControllerAdvice
publicclassDecryptRequestBodyAdviceimplementsRequestBodyAdvice{

@Autowired
privateObjectMapperobjectMapper;

/**
*方法上有DecryptionAnnotation注解的,進(jìn)入此攔截器
*@parammethodParameter方法參數(shù)對(duì)象
*@paramtargetType參數(shù)的類型
*@paramconverterType消息轉(zhuǎn)換器
*@returntrue,進(jìn)入,false,跳過
*/
@Override
publicbooleansupports(MethodParametermethodParameter,TypetargetType,Class>converterType){
returnmethodParameter.hasMethodAnnotation(DecryptionAnnotation.class);
}

@Override
publicHttpInputMessagebeforeBodyRead(HttpInputMessageinputMessage,MethodParameterparameter,TypetargetType,Class>converterType)throwsIOException{
returninputMessage;
}

/**
*轉(zhuǎn)換之后,執(zhí)行此方法,解密,賦值
*@parambodyspring解析完的參數(shù)
*@paraminputMessage輸入?yún)?shù)
*@paramparameter參數(shù)對(duì)象
*@paramtargetType參數(shù)類型
*@paramconverterType消息轉(zhuǎn)換類型
*@return真實(shí)的參數(shù)
*/
@SneakyThrows
@Override
publicObjectafterBodyRead(Objectbody,HttpInputMessageinputMessage,MethodParameterparameter,TypetargetType,Class>converterType){

//獲取request
RequestAttributesrequestAttributes=RequestContextHolder.getRequestAttributes();
ServletRequestAttributesservletRequestAttributes=(ServletRequestAttributes)requestAttributes;
if(servletRequestAttributes==null){
thrownewParamException("request錯(cuò)誤");
}

HttpServletRequestrequest=servletRequestAttributes.getRequest();

//獲取數(shù)據(jù)
ServletInputStreaminputStream=request.getInputStream();
RequestDatarequestData=objectMapper.readValue(inputStream,RequestData.class);

if(requestData==null||StringUtils.isBlank(requestData.getText())){
thrownewParamException("參數(shù)錯(cuò)誤");
}

//獲取加密的數(shù)據(jù)
Stringtext=requestData.getText();

//放入解密之前的數(shù)據(jù)
request.setAttribute(CryptoConstant.INPUT_ORIGINAL_DATA,text);

//解密
StringdecryptText=null;
try{
decryptText=AESUtil.decrypt(text);
}catch(Exceptione){
thrownewParamException("解密失敗");
}

if(StringUtils.isBlank(decryptText)){
thrownewParamException("解密失敗");
}

//放入解密之后的數(shù)據(jù)
request.setAttribute(CryptoConstant.INPUT_DECRYPT_DATA,decryptText);

//獲取結(jié)果
Objectresult=objectMapper.readValue(decryptText,body.getClass());

//強(qiáng)制所有實(shí)體類必須繼承RequestBase類,設(shè)置時(shí)間戳
if(resultinstanceofRequestBase){
//獲取時(shí)間戳
LongcurrentTimeMillis=((RequestBase)result).getCurrentTimeMillis();
//有效期60秒
longeffective=60*1000;

//時(shí)間差
longexpire=System.currentTimeMillis()-currentTimeMillis;

//是否在有效期內(nèi)
if(Math.abs(expire)>effective){
thrownewParamException("時(shí)間戳不合法");
}

//返回解密之后的數(shù)據(jù)
returnresult;
}else{
thrownewParamException(String.format("請(qǐng)求參數(shù)類型:%s 未繼承:%s",result.getClass().getName(),RequestBase.class.getName()));
}
}

/**
*如果body為空,轉(zhuǎn)為空對(duì)象
*@parambodyspring解析完的參數(shù)
*@paraminputMessage輸入?yún)?shù)
*@paramparameter參數(shù)對(duì)象
*@paramtargetType參數(shù)類型
*@paramconverterType消息轉(zhuǎn)換類型
*@return真實(shí)的參數(shù)
*/
@SneakyThrows
@Override
publicObjecthandleEmptyBody(Objectbody,HttpInputMessageinputMessage,MethodParameterparameter,TypetargetType,Class>converterType){
StringtypeName=targetType.getTypeName();
ClassbodyClass=Class.forName(typeName);
returnbodyClass.newInstance();
}
}

EncryptResponseBodyAdvice 相應(yīng)自動(dòng)加密

packagexyz.hlh.crypto.advice;

importcn.hutool.json.JSONUtil;
importcom.fasterxml.jackson.databind.ObjectMapper;
importlombok.SneakyThrows;
importorg.apache.commons.lang3.StringUtils;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.core.MethodParameter;
importorg.springframework.http.MediaType;
importorg.springframework.http.ResponseEntity;
importorg.springframework.http.converter.HttpMessageConverter;
importorg.springframework.http.server.ServerHttpRequest;
importorg.springframework.http.server.ServerHttpResponse;
importorg.springframework.web.bind.annotation.ControllerAdvice;
importorg.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
importsun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
importxyz.hlh.crypto.annotation.EncryptionAnnotation;
importxyz.hlh.crypto.common.entity.Result;
importxyz.hlh.crypto.common.exception.CryptoException;
importxyz.hlh.crypto.entity.RequestBase;
importxyz.hlh.crypto.util.AESUtil;

importjava.lang.reflect.Type;

/**
*@authorHLH
*@description:
*@email17703595860@163.com
*@date:Createdin2022/2/415:12
*/
@ControllerAdvice
publicclassEncryptResponseBodyAdviceimplementsResponseBodyAdvice<Result>{

@Autowired
privateObjectMapperobjectMapper;

@Override
publicbooleansupports(MethodParameterreturnType,Class>converterType){
ParameterizedTypeImplgenericParameterType=(ParameterizedTypeImpl)returnType.getGenericParameterType();

//如果直接是Result,則返回
if(genericParameterType.getRawType()==Result.class&&returnType.hasMethodAnnotation(EncryptionAnnotation.class)){
returntrue;
}

if(genericParameterType.getRawType()!=ResponseEntity.class){
returnfalse;
}

//如果是ResponseEntity
for(Typetype:genericParameterType.getActualTypeArguments()){
if(((ParameterizedTypeImpl)type).getRawType()==Result.class&&returnType.hasMethodAnnotation(EncryptionAnnotation.class)){
returntrue;
}
}

returnfalse;
}

@SneakyThrows
@Override
publicResultbeforeBodyWrite(Resultbody,MethodParameterreturnType,MediaTypeselectedContentType,Class>selectedConverterType,ServerHttpRequestrequest,ServerHttpResponseresponse){

//加密
Objectdata=body.getData();

//如果data為空,直接返回
if(data==null){
returnbody;
}

//如果是實(shí)體,并且繼承了Request,則放入時(shí)間戳
if(datainstanceofRequestBase){
((RequestBase)data).setCurrentTimeMillis(System.currentTimeMillis());
}

StringdataText=JSONUtil.toJsonStr(data);

//如果data為空,直接返回
if(StringUtils.isBlank(dataText)){
returnbody;
}

//如果位數(shù)小于16,報(bào)錯(cuò)
if(dataText.length()16){
thrownewCryptoException("加密失敗,數(shù)據(jù)小于16位");
}

StringencryptText=AESUtil.encryptHex(dataText);

returnResult.builder()
.status(body.getStatus())
.data(encryptText)
.message(body.getMessage())
.build();
}
}

5.4 crypto-test

5.4.1 結(jié)構(gòu)

292e5bc2-6f8b-11ed-8abf-dac502259ad0.png

5.4.2 重要代碼

application.yml 配置文件

spring:
mvc:
format:
date-time:yyyy-MM-ddHHss
date:yyyy-MM-dd
#日期格式化
jackson:
date-format:yyyy-MM-ddHHss

Teacher 實(shí)體類

packagexyz.hlh.crypto.entity;

importlombok.AllArgsConstructor;
importlombok.Data;
importlombok.EqualsAndHashCode;
importlombok.NoArgsConstructor;
importorg.hibernate.validator.constraints.Range;

importjavax.validation.constraints.NotBlank;
importjavax.validation.constraints.NotNull;
importjava.io.Serializable;
importjava.util.Date;

/**
*@authorHLH
*@description:Teacher實(shí)體類,使用SpringBoot的validation校驗(yàn)
*@email17703595860@163.com
*@date:Createdin2022/2/410:21
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper=true)
publicclassTeacherextendsRequestBaseimplementsSerializable{

@NotBlank(message="姓名不能為空")
privateStringname;
@NotNull(message="年齡不能為空")
@Range(min=0,max=150,message="年齡不合法")
privateIntegerage;
@NotNull(message="生日不能為空")
privateDatebirthday;

}

TestController 測(cè)試Controller

packagexyz.hlh.crypto.controller;

importorg.springframework.http.ResponseEntity;
importorg.springframework.validation.annotation.Validated;
importorg.springframework.web.bind.annotation.PostMapping;
importorg.springframework.web.bind.annotation.RequestBody;
importorg.springframework.web.bind.annotation.RestController;
importxyz.hlh.crypto.annotation.DecryptionAnnotation;
importxyz.hlh.crypto.annotation.EncryptionAnnotation;
importxyz.hlh.crypto.common.entity.Result;
importxyz.hlh.crypto.common.entity.ResultBuilder;
importxyz.hlh.crypto.entity.Teacher;

/**
*@authorHLH
*@description:測(cè)試Controller
*@email17703595860@163.com
*@date:Createdin2022/2/49:16
*/
@RestController
publicclassTestControllerimplementsResultBuilder{

/**
*直接返回對(duì)象,不加密
*@paramteacherTeacher對(duì)象
*@return不加密的對(duì)象
*/
@PostMapping("/get")
publicResponseEntity>get(@Validated@RequestBodyTeacherteacher){
returnsuccess(teacher);
}

/**
*返回加密后的數(shù)據(jù)
*@paramteacherTeacher對(duì)象
*@return返回加密后的數(shù)據(jù)ResponseBody格式
*/
@PostMapping("/encrypt")
@EncryptionAnnotation
publicResponseEntity>encrypt(@Validated@RequestBodyTeacherteacher){
returnsuccess(teacher);
}

/**
*返回加密后的數(shù)據(jù)
*@paramteacherTeacher對(duì)象
*@return返回加密后的數(shù)據(jù)Result格式
*/
@PostMapping("/encrypt1")
@EncryptionAnnotation
publicResultencrypt1(@Validated@RequestBodyTeacherteacher){
returnsuccess(teacher).getBody();
}

/**
*返回解密后的數(shù)據(jù)
*@paramteacherTeacher對(duì)象
*@return返回解密后的數(shù)據(jù)
*/
@PostMapping("/decrypt")
@DecryptionAnnotation
publicResponseEntity>decrypt(@Validated@RequestBodyTeacherteacher){
returnsuccess(teacher);
}

}


審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • JAVA
    +關(guān)注

    關(guān)注

    19

    文章

    2952

    瀏覽量

    104489
  • 管理系統(tǒng)
    +關(guān)注

    關(guān)注

    1

    文章

    2376

    瀏覽量

    35763
  • spring
    +關(guān)注

    關(guān)注

    0

    文章

    338

    瀏覽量

    14296
  • SpringBoot
    +關(guān)注

    關(guān)注

    0

    文章

    173

    瀏覽量

    161

原文標(biāo)題:SpringBoot 接口加密解密,新姿勢(shì)!

文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    安卓APP開發(fā)中,如何使用加密芯片?

    加密芯片是一種專門設(shè)計(jì)用于保護(hù)信息安全的硬件設(shè)備,它通過內(nèi)置的加密算法對(duì)數(shù)據(jù)進(jìn)行加密解密,以防止敏感數(shù)據(jù)被竊取或篡改。如下圖HD-RK3568-IOT工控板,搭載ATSHA204A
    的頭像 發(fā)表于 10-31 17:43 ?259次閱讀
    安卓APP開發(fā)中,如何使用<b class='flag-5'>加密</b>芯片?

    用 AI 解鎖技術(shù)調(diào)研的新姿勢(shì)

    1. 前言 在日常開發(fā)中,為了保證技術(shù)方案的質(zhì)量,一般會(huì)在撰寫前進(jìn)行調(diào)研。如果先前沒有相關(guān)領(lǐng)域的知識(shí)儲(chǔ)備,筆者的調(diào)研方式一般是先通過搜索引擎進(jìn)行關(guān)鍵字查詢,然后再基于搜索的結(jié)果進(jìn)行發(fā)散。這樣調(diào)研的結(jié)果受關(guān)鍵字抽象程度和搜索引擎排名影響較大,可能會(huì)存在偏差導(dǎo)致調(diào)研不充分。剛好大模型風(fēng)靡有一段時(shí)間了,就想如果AI能自動(dòng)檢索資料并進(jìn)行內(nèi)容總結(jié),豈不美哉。避免重復(fù)造輪子,先在網(wǎng)上檢索了一下,發(fā)現(xiàn)剛好有一個(gè)工具“ ST
    的頭像 發(fā)表于 08-05 13:44 ?172次閱讀
    用 AI 解鎖技術(shù)調(diào)研的<b class='flag-5'>新姿勢(shì)</b>

    鴻蒙開發(fā)接口安全:【@system.cipher (加密算法)】

    加密類型,可選項(xiàng)有: 1.?encrypt?加密 2.?decrypt?解密
    的頭像 發(fā)表于 06-06 09:11 ?931次閱讀
    鴻蒙開發(fā)<b class='flag-5'>接口</b>安全:【@system.cipher (<b class='flag-5'>加密</b>算法)】

    基于 FPGA 的光纖混沌加密系統(tǒng)

    和可靠性有限,很多時(shí)候不能滿足用戶的需求。因此,需要更加快速,更加安全可靠的加密實(shí)現(xiàn)方式來(lái)滿足人們 在一些場(chǎng)合下的數(shù)據(jù)保密要求。 由于我們傳輸?shù)乃俾蔬_(dá)到 5Gbps,這種 GTP 高速接口下若使
    發(fā)表于 04-26 17:18

    鴻蒙OS開發(fā)問題:(ArkTS)【 RSA加解密,解決中文亂碼等現(xiàn)象】

    RSA加解密開始構(gòu)建工具類就是舉步維艱,官方文檔雖然很全,但是還是有很多小瑕疵,在自己經(jīng)過幾天的時(shí)間,徹底解決了中文亂碼的問題、分段加密的問題。
    的頭像 發(fā)表于 03-27 21:23 ?1654次閱讀
    鴻蒙OS開發(fā)問題:(ArkTS)【 RSA加<b class='flag-5'>解密</b>,解決中文亂碼等現(xiàn)象】

    加密狗是什么意思 加密狗怎么解除加密

    加密狗(Dongle)又稱為加密鎖、硬件鎖或USB密鑰是一種用于軟件保護(hù)和授權(quán)管理的硬件設(shè)備。它通常是一個(gè)外部設(shè)備,插入到計(jì)算機(jī)的USB接口上,以確保只有經(jīng)過授權(quán)的用戶可以訪問該軟件。加密
    的頭像 發(fā)表于 01-25 17:19 ?8054次閱讀

    對(duì)稱加密算法工作模式詳解

    對(duì)稱密碼體制,又稱為單密鑰密碼機(jī)制,其基本特征為加密密鑰和解密密鑰相同。
    的頭像 發(fā)表于 01-04 11:25 ?1848次閱讀
    對(duì)稱<b class='flag-5'>加密</b>算法工作模式詳解

    加密芯片工作原理max32555

    加密芯片MAX32555是一種廣泛應(yīng)用的硬件加密設(shè)備,它采用先進(jìn)的加密算法和技術(shù),對(duì)數(shù)據(jù)進(jìn)行加密解密操作,以確保數(shù)據(jù)傳輸和存儲(chǔ)的安全性。本
    的頭像 發(fā)表于 12-13 15:03 ?1978次閱讀

    SpringBoot實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源

    最近在做業(yè)務(wù)需求時(shí),需要從不同的數(shù)據(jù)庫(kù)中獲取數(shù)據(jù)然后寫入到當(dāng)前數(shù)據(jù)庫(kù)中,因此涉及到切換數(shù)據(jù)源問題。本來(lái)想著使用Mybatis-plus中提供的動(dòng)態(tài)數(shù)據(jù)源SpringBoot的starter:dynamic-datasource-spring-boot-starter來(lái)實(shí)現(xiàn)。
    的頭像 發(fā)表于 12-08 10:53 ?964次閱讀
    <b class='flag-5'>SpringBoot</b>實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源

    php加密方式有哪些

    PHP加密方式有許多種,以下是一些常用的加密方式: 對(duì)稱加密 對(duì)稱加密算法使用相同的密鑰進(jìn)行加密解密
    的頭像 發(fā)表于 12-04 15:32 ?610次閱讀

    21489可以在啟動(dòng)加載程序過程中將SPIFLASH中的程序按照一定的解密算法解密后,加載到內(nèi)部RAM運(yùn)行嗎?

    解密算法解密后,加載到內(nèi)部RAM運(yùn)行?這樣,只要將編譯好的程序經(jīng)過一定的加密算法處理后,再燒寫到SPIFLASH中,就算別人能夠讀出程序也沒什么作用。有沒有這種功能或者實(shí)現(xiàn)方式?
    發(fā)表于 11-30 07:47

    FDE磁盤加密技術(shù)是什么

    磁盤加密技術(shù) 如上節(jié)所述,Disk Encryption磁盤加密,目標(biāo)是保護(hù)數(shù)據(jù)at Rest狀態(tài)下的機(jī)密性,加密對(duì)象是整個(gè)磁盤/分區(qū)、或者文件系統(tǒng),采用實(shí)時(shí)加解密技術(shù)。 磁盤
    的頭像 發(fā)表于 11-29 10:54 ?1206次閱讀
    FDE磁盤<b class='flag-5'>加密</b>技術(shù)是什么

    一個(gè)注解搞定SpringBoot接口防刷

    技術(shù)要點(diǎn):springboot的基本知識(shí),redis基本操作,
    的頭像 發(fā)表于 11-28 10:46 ?380次閱讀

    高效穩(wěn)定管理電源的正確“姿勢(shì)”!

    高效穩(wěn)定管理電源的正確“姿勢(shì)”!
    的頭像 發(fā)表于 11-23 09:04 ?381次閱讀
    高效穩(wěn)定管理電源的正確“<b class='flag-5'>姿勢(shì)</b>”!

    javaweb和springboot能一起用嗎

    JavaWeb 和 SpringBoot 是兩種針對(duì) Java 程序開發(fā)的框架,它們可以在一起使用。在本文中,我將詳細(xì)介紹 JavaWeb 和 SpringBoot 的關(guān)系,并探討如何結(jié)合使用這兩個(gè)
    的頭像 發(fā)表于 11-16 10:54 ?1949次閱讀