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();
}
}
相關文章
- 自定義訊息獲取訊息(轉)
- VC增加自定義訊息 (轉)
- SpringMVC原始碼剖析(五)-訊息轉換器HttpMessageConverterSpringMVC原始碼HTTP
- 自定義值轉換器
- 自定義訊息和對訊息的理解
- 形形色色的自定義訊息(上) (轉)
- 形形色色的自定義訊息(下) (轉)
- 用於日期轉換的訊息轉換器
- 自定義NSNotification訊息中心
- Django(6)自定義路由轉換器Django路由
- ros|自定義訊息型別ROS型別
- SpringMVC原始碼剖析5:訊息轉換器HttpMessageConverter與@ResponseBody註解SpringMVC原始碼HTTP
- JavaRetrofit2使用–自定義轉換器Java
- Native Query的自定義轉換器
- VC自定義訊息postmessage用法(訊息響應函式)函式
- WIN32傳送自定義訊息Win32
- Jquery實現自定義訊息彈窗jQuery
- Mybatis使用小技巧-自定義型別轉換器MyBatis型別
- Spring Boot之自定義JSON轉換器Spring BootJSON
- RabbitMQ 入門(三)SpringAMQP訊息轉換器MQSpringGAM
- 自定義視訊播放器播放器
- ROS2/C++ 自定義訊息型別ROSC++型別
- Laravel 5.5 Validator 自定義錯誤返回訊息Laravel
- 企業微信hook,自定義工具,收發訊息Hook
- 【Flask】路由裝飾器、路徑傳參、自定義路由轉換器Flask路由
- Laravel 使用 Easywechat 書寫自定義模板訊息丶廣播訊息頻道Laravel
- 自定義SpringMVC部分實現SpringMVC
- 如何在Mac上設定自定義鎖屏訊息?Mac
- 如何給FineReport設定自定義訊息提醒工具
- 訊息粘包 和 訊息不完整 問題
- MyBatis使用自定義TypeHandler轉換型別MyBatis型別
- C# 自定義 implicit和explicit轉換C#
- SpringMVC(二)處理器方法繫結形參(簡單型別和註解@RequestParam,pojo型別)、自定義型別轉換器、springMVC亂碼解決SpringMVC型別POJO
- SpringMVC自定義相容性HandlerSpringMVC
- 自定義view之模擬qq訊息拖拽刪除效果View
- PostgreSQL自定義自動型別轉換(CAST)SQL型別AST
- 【SpringMVC】HttpMessageConverter報文資訊轉換器SpringMVCHTTP
- 09.AutoMapper 之自定義型別轉換器(Custom TypeAPP型別