springmvc 自定義訊息轉換器完整例子
問題描述:
最近在專案中對接第三方介面,採用http協議,post方法,協議型別:Content-Type: application/json;charset=utf-8,將使用者名稱和密碼等資訊放在header中,用於驗證請求。將業務資料放到body體中,並使用3DES加密。
- 請求報文樣例如下:
POST /api/GetParkingPaymentInfo HTTP/1.1
Content-Type: application/json;charset=utf-8
user: 123453
pwd: qwerew
Host: 220.160.112.124:9096
Content-Length: 43
Expect: 100-continue
{"data":"DkTwRsUUza33A8/TvrocXI3r+Az1T7bt"}
每個介面都是此加密驗證方式,但是我不想再每個controller方法中都校驗解密一次,故而想到使用springmvc 的自定義訊息轉換器,在訊息轉換器中先解密,然後將報文轉換為對應的java物件,controller入參直接是java物件,這樣校驗使用者名稱密碼和解密就可以單獨處理了。
驗證使用者名稱和密碼,使用攔截器實現
因為使用者名稱和密碼放到了header中,可以在攔截器中獲取請求頭,判斷使用者名稱和密碼是否正確。
- 建立攔截器
@Component
public class KeyTopInterceptor extends HandlerInterceptorAdapter {
private static final Logger log = LoggerFactory.getLogger(AuthInterceptor.class);
private static final String MIME_JSON = "application/json;charset=UTF-8";
@Value("${keytop.user}")
private String ktuser;
@Value("${keytop.pwd}")
private String ktpwd;
//在請求進入controller前進行攔截
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String user = request.getHeader("user");
String pwd = request.getHeader("pwd");
String host = request.getHeader("Host");
log.info("===校驗科託請求頭中的使用者名稱和密碼,url={},user={},pwd={},host={}",request.getRequestURI(),user,pwd,host);
if(ktuser.equals(user) && ktpwd.equals(pwd)){
return true;
}else{
log.info("===校驗科託失敗,配置的使用者名稱和密碼與傳遞的不一致,配置的ktuser={},ktpwd={}",ktuser,ktpwd);
//根據介面要求返回錯誤資訊
PrintWriter writer = response.getWriter();
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-type", MIME_JSON);
response.setContentType(MIME_JSON);
BaseKeyTopRes<?> baseKeyTopRes = new BaseKeyTopRes<>();
baseKeyTopRes.setFaileInfo("user or pwd incorrectness");
response.setStatus(HttpStatus.OK.value());
writer.write(JSONObject.toJSON(baseKeyTopRes).toString());
writer.close();
return false;
}
}
}
- 配置攔截器
@Configuration
@EnableWebMvc
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
@Autowired
private KeyTopInterceptor keyTopInterceptor;
/**
* 新增攔截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
//新增科託攔截器
registry.addInterceptor(keyTopInterceptor)
.addPathPatterns("/keytop/**");
}
}
body體解密,轉換為java物件
例如有個介面的data欄位為:{“data”:{"platno":"A1234"}},獲取到的引數為加密之後的:{"data":"DkTwRsUUza33A8/TvrocXI3r+Az1T7bt"}。
- 建立訊息轉換器
為了使建立的訊息轉換器只轉換本次業務新增的介面,建立一個請求基類bean物件,沒有任何欄位,只是實現Serializable介面,作為其他業務的父類,例如:BaseKeyTopReq
public class BaseKeyTopReq implements Serializable{
}
建立訊息轉換器如下:
public class KeyTopMsgConverter extends AbstractHttpMessageConverter<BaseKeyTopReq> {
private static final Logger logger = LoggerFactory.getLogger(KeyTopMsgConverter.class);
//科託3DES加解密需要的key
private String ktkey;
//科託3DES加解密需要的偏移量
private String ktiv;
public KeyTopMsgConverter(MediaType supportedMediaType,String ktkey,String ktiv) {
super(supportedMediaType);
this.ktiv=ktiv;
this.ktkey=ktkey;
}
/**
* 如果支援 true支援
* 會呼叫 readInternal 將http訊息 轉換成方法中被@RequestBody註解的引數
* 會呼叫writeInternal 將被@ResponseBody註解的返回物件轉換成資料位元組響應給瀏覽器
*/
@Override
protected boolean supports(Class<?> clazz) {
//判斷父類是否為BaseKeyTopReq,如果是則使用該轉換器
if(clazz.getSuperclass() == BaseKeyTopReq.class){
return true;
}
return false;
}
/**
*解析請求的引數
*/
@Override
protected BaseKeyTopReq readInternal(Class<? extends BaseKeyTopReq> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
//獲取body資訊
InputStream is=inputMessage.getBody();
BufferedReader br=new BufferedReader(new InputStreamReader(is));
StringBuilder stringBuilder = new StringBuilder();
br.lines().forEach(item->stringBuilder.append(item));
logger.info("科託解密之前資料:"+stringBuilder.toString());
JSONObject jsonObject = JSON.parseObject(stringBuilder.toString());
String data = jsonObject.getString("data");
//解密
try {
String desString = ThreeDESUtil.getDesString(data,ktkey,ktiv);
logger.info("科託解密之後資料:"+desString);
//將解密出來的資訊轉換為java物件,注意該物件必須繼承BaseKeyTopReq
return JSONObject.parseObject(desString,clazz);
} catch (Exception e) {
logger.error("科託解密失敗",e);
throw new BizException(ErrorType.DECODE_ERROR);
}
}
/**
* 響應給物件的引數
* 將方法被@ResponseBody註解的返回物件轉換成資料位元組響應給瀏覽器
*/
@Override
protected void writeInternal(BaseKeyTopReq baseKeyTopReq, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
}
}
- 配置轉換器
@Configuration
@EnableWebMvc
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
@Value("${keytop.key}")
private String ktkey;
@Value("${keytop.iv}")
private String ktiv;
/**
* 擴充套件訊息轉換器
* 注意不能使用configureMessageConverters方法,使用configureMessageConverters方法,則只包含你新增的,springmvc預設的訊息轉換器沒有了。
* @param converters
*/
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//增加科託訊息轉換器
KeyTopMsgConverter converter = new KeyTopMsgConverter(MediaType.APPLICATION_JSON,ktkey,ktiv);
converters.add(0,converter);//將自定義的設定為優先順序最高
}
}
使用測試
例如有個介面PostFreeParkingSpace,data欄位資訊為:plateNo,json格式。
則可以建立一個PostFreeParkingSpaceReq物件,繼承BaseKeyTopReq。
//響應介面對應的物件
public class PostFreeParkingSpaceReq extends BaseKeyTopReq {
private String plateNo;
public String getPlateNo() {
return plateNo;
}
public void setPlateNo(String plateNo) {
this.plateNo = plateNo;
}
}
則介面呼叫方,傳送的body體資料為:{"data":"DkTwRsUUza33A8/TvrocXI3r+Az1T7bt"},經過訊息轉換器解密(只解密data內容)之後為:{"plateNo":"A12345"},然後將該json字串轉換為java物件。
則在controller中入參物件裡面就有值了。
- controller
@RestController
@RequestMapping("/keytop")
public class KeyTopController {
private static final Logger logger = LoggerFactory.getLogger(PubParkingController.class);
@RequestMapping(value = "/PostFreeParkingSpace", method = RequestMethod.POST)
public String PostFreeParkingSpace(@RequestBody PostFreeParkingSpaceReq spaceReq) {
logger.info("科託空閒車位上報:" + JSON.toJSONString(spaceReq));
/**此時從入參物件中獲取的plateNo值則為A12345,已經解密完且轉換成了對應的實體物件*/
return spaceReq.getPlateNo();
}
}
相關文章
- 自定義值轉換器
- SpringMVC原始碼剖析5:訊息轉換器HttpMessageConverter與@ResponseBody註解SpringMVC原始碼HTTP
- Django(6)自定義路由轉換器Django路由
- 用於日期轉換的訊息轉換器
- 【SpringMVC】HttpMessageConverter報文資訊轉換器SpringMVCHTTP
- Spring Boot之自定義JSON轉換器Spring BootJSON
- ros|自定義訊息型別ROS型別
- Mybatis使用小技巧-自定義型別轉換器MyBatis型別
- RabbitMQ 入門(三)SpringAMQP訊息轉換器MQSpringGAM
- WIN32傳送自定義訊息Win32
- 09.AutoMapper 之自定義型別轉換器(Custom TypeAPP型別
- 【Flask】路由裝飾器、路徑傳參、自定義路由轉換器Flask路由
- 自定義SpringMVC部分實現SpringMVC
- SpringMVC(二)處理器方法繫結形參(簡單型別和註解@RequestParam,pojo型別)、自定義型別轉換器、springMVC亂碼解決SpringMVC型別POJO
- MyBatis使用自定義TypeHandler轉換型別MyBatis型別
- Laravel 5.5 Validator 自定義錯誤返回訊息Laravel
- SpringMVC自定義相容性HandlerSpringMVC
- Laravel 使用 Easywechat 書寫自定義模板訊息丶廣播訊息頻道Laravel
- ROS2/C++ 自定義訊息型別ROSC++型別
- 自定義視訊播放器播放器
- C#自定義控制元件—轉換開關C#控制元件
- 如何在Mac上設定自定義鎖屏訊息?Mac
- 企業微信hook,自定義工具,收發訊息Hook
- gin自定義驗證器&轉化中文
- 訊息粘包 和 訊息不完整 問題
- 胖哥學SpringMVC:請求方式轉換過濾器配置SpringMVC過濾器
- 自定義Navigator切換fragmentFragment
- 在 Linux 伺服器關機前向使用者顯示一條自定義訊息Linux伺服器
- 訊息佇列Rabbitmq的交換器型別佇列MQ型別
- 如何在丟失的Mac上設定自定義鎖屏訊息Mac
- SpringMVC的亂碼與時間轉換SpringMVC
- 第11章 使用類——型別轉換(二)將自定義型別轉換為內建型別型別
- Mac使用技巧_蘋果鎖屏介面如何自定義鎖屏訊息?Mac蘋果
- Log4Net配置詳解及輸出自定義訊息類示例
- 直播平臺開發,TabLayout的使用和自定義紅點訊息提示TabLayout
- 如何使用JSR303驗證及自定義訊息統一處理JS
- iOS 訊息轉發iOS
- 巧用fastjson自定義序列化類實現欄位的轉換ASTJSON