日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java接口签名(Signature)实现方案续

發布時間:2025/3/8 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java接口签名(Signature)实现方案续 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、前言

  由于之前寫過的一片文章 (java接口簽名(Signature)實現方案?)收獲了很多好評,此次來說一下另一種簡單粗暴的簽名方案。相對于之前的簽名方案,對body、paramenter、path variable的獲取都做了簡化的處理。也就是說這種方式針所有數據進行了簽名,并不能指定某些數據進行簽名。

二、簽名規則

  1、線下分配appid和appsecret,針對不同的調用方分配不同的appid和appsecret

  2、加入timestamp(時間戳),10分鐘內數據有效

  3、加入流水號nonce(防止重復提交),至少為10位。針對查詢接口,流水號只用于日志落地,便于后期日志核查。 針對辦理類接口需校驗流水號在有效期內的唯一性,以避免重復請求。

  4、加入signature,所有數據的簽名信息。

  以上紅色字段放在請求頭中。

三、簽名的生成

  signature?字段生成規則如下。

 ? 1、數據部分

  Path Variable:按照path中的字典順序將所有value進行拼接

  Parameter:按照key=values(多個value按照字典順序拼接)字典順序進行拼接

  Body:從request inputstream中獲取保存為String形式

? ? ? ?

  如果存在多種數據形式,則按照body、parameter、path variable的順序進行再拼接,得到所有數據的拼接值。

  上述拼接的值記作 Y。

  2、請求頭部分

  X=”appid=xxxnonce=xxxtimestamp=xxx”

  3、生成簽名

  最終拼接值=XY

  最后將最終拼接值按照如下方法進行加密得到簽名。

  signature=org.apache.commons.codec.digest.HmacUtils.AesEncodeUtil(app secret, 拼接的值);

四、簽名算法實現

  注:省去了X=”appid=xxxnonce=xxxtimestamp=xxx”這部分。

  1、自定義Request對象

  為什么要自定義request對象,因為我們要獲取request inputstream(默認只能獲取一次)。

public class BufferedHttpServletRequest extends HttpServletRequestWrapper {private ByteBuf buffer;private final AtomicBoolean isCached = new AtomicBoolean();public BufferedHttpServletRequest(HttpServletRequest request, int initialCapacity) {super(request);int contentLength = request.getContentLength();int min = Math.min(initialCapacity, contentLength);if (min < 0) {buffer = Unpooled.buffer(0);} else {buffer = Unpooled.buffer(min, contentLength);}}@Overridepublic ServletInputStream getInputStream() throws IOException {//Only returning data from buffer if it is readonly, which means the underlying stream is EOF or closed.if (isCached.get()) {return new NettyServletInputStream(buffer);}return new ContentCachingInputStream(super.getInputStream());}public void release() {buffer.release();}private class ContentCachingInputStream extends ServletInputStream {private final ServletInputStream is;public ContentCachingInputStream(ServletInputStream is) {this.is = is;}@Overridepublic int read() throws IOException {int ch = this.is.read();if (ch != -1) {//Stream is EOF, set this buffer to readonly state buffer.writeByte(ch);} else {isCached.compareAndSet(false, true);}return ch;}@Overridepublic void close() throws IOException {//Stream is closed, set this buffer to readonly statetry {is.close();} finally {isCached.compareAndSet(false, true);}}@Overridepublic boolean isFinished() {throw new UnsupportedOperationException("Not yet implemented!");}@Overridepublic boolean isReady() {throw new UnsupportedOperationException("Not yet implemented!");}@Overridepublic void setReadListener(ReadListener readListener) {throw new UnsupportedOperationException("Not yet implemented!");}} }

  替換默認的request對象

@Configuration public class FilterConfig {@Beanpublic RequestCachingFilter requestCachingFilter() {return new RequestCachingFilter();}@Beanpublic FilterRegistrationBean requestCachingFilterRegistration(RequestCachingFilter requestCachingFilter) {FilterRegistrationBean bean = new FilterRegistrationBean(requestCachingFilter);bean.setOrder(1);return bean;} } public class RequestCachingFilter extends OncePerRequestFilter {private static Logger LOGGER = LoggerFactory.getLogger(RequestCachingFilter.class);@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {boolean isFirstRequest = !isAsyncDispatch(request);HttpServletRequest requestToUse = request;if (isFirstRequest && !(request instanceof BufferedHttpServletRequest)) {requestToUse = new BufferedHttpServletRequest(request, 1024);}try {filterChain.doFilter(requestToUse, response);} catch (Exception e) {LOGGER.error("RequestCachingFilter>>>>>>>>>", e);} finally {this.printRequest(requestToUse);if (requestToUse instanceof BufferedHttpServletRequest) {((BufferedHttpServletRequest) requestToUse).release();}}}private void printRequest(HttpServletRequest request) {String body = StringUtils.EMPTY;try {if (request instanceof BufferedHttpServletRequest) {body = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);}} catch (IOException e) {LOGGER.error("printRequest 獲取body異常...", e);}JSONObject requestJ = new JSONObject();JSONObject headers = new JSONObject();Collections.list(request.getHeaderNames()).stream().forEach(name -> headers.put(name, request.getHeader(name)));requestJ.put("headers", headers);requestJ.put("parameters", request.getParameterMap());requestJ.put("body", body);requestJ.put("remote-user", request.getRemoteUser());requestJ.put("remote-addr", request.getRemoteAddr());requestJ.put("remote-host", request.getRemoteHost());requestJ.put("remote-port", request.getRemotePort());requestJ.put("uri", request.getRequestURI());requestJ.put("url", request.getRequestURL());requestJ.put("servlet-path", request.getServletPath());requestJ.put("method", request.getMethod());requestJ.put("query", request.getQueryString());requestJ.put("path-info", request.getPathInfo());requestJ.put("context-path", request.getContextPath());LOGGER.info("Request-Info: " + JSON.toJSONString(requestJ, SerializerFeature.PrettyFormat));}}

  2、簽名切面

@Aspect @Component public class SignatureAspect {private static final Logger LOGGER = LoggerFactory.getLogger(StringUtils.class);@Around("execution(* com..controller..*.*(..)) " +"&& (@annotation(org.springframework.web.bind.annotation.RequestMapping)" +"|| @annotation(org.springframework.web.bind.annotation.GetMapping)" +"|| @annotation(org.springframework.web.bind.annotation.PostMapping)" +"|| @annotation(org.springframework.web.bind.annotation.DeleteMapping)" +"|| @annotation(org.springframework.web.bind.annotation.PatchMapping))")public Object doAround(ProceedingJoinPoint pjp) throws Throwable {try {this.checkSign();return pjp.proceed();} catch (Throwable e) {LOGGER.error("SignatureAspect>>>>>>>>", e);throw e;}}private void checkSign() throws Exception {HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();String oldSign = request.getHeader("X-SIGN");if (StringUtils.isBlank(oldSign)) {throw new RuntimeException("取消簽名Header[X-SIGN]信息");}//獲取body(對應@RequestBody)String body = null;if (request instanceof BufferedHttpServletRequest) {body = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);}//獲取parameters(對應@RequestParam)Map<String, String[]> params = null;if (!CollectionUtils.isEmpty(request.getParameterMap())) {params = request.getParameterMap();}//獲取path variable(對應@PathVariable)String[] paths = null;ServletWebRequest webRequest = new ServletWebRequest(request, null);Map<String, String> uriTemplateVars = (Map<String, String>) webRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);if (!CollectionUtils.isEmpty(uriTemplateVars)) {paths = uriTemplateVars.values().toArray(new String[]{});}try {String newSign = SignUtil.sign(body, params, paths);if (!newSign.equals(oldSign)) {throw new RuntimeException("簽名不一致...");}} catch (Exception e) {throw new RuntimeException("驗簽出錯...", e);}} }

  分別獲取了request inputstream中的body信息、parameter信息、path variable信息。

  3、簽名核心工具類

public class SignUtil {private static final String DEFAULT_SECRET = "1qaz@WSX#$%&";public static String sign(String body, Map<String, String[]> params, String[] paths) {StringBuilder sb = new StringBuilder();if (StringUtils.isNotBlank(body)) {sb.append(body).append('#');}if (!CollectionUtils.isEmpty(params)) {params.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(paramEntry -> {String paramValue = String.join(",", Arrays.stream(paramEntry.getValue()).sorted().toArray(String[]::new));sb.append(paramEntry.getKey()).append("=").append(paramValue).append('#');});}if (ArrayUtils.isNotEmpty(paths)) {String pathValues = String.join(",", Arrays.stream(paths).sorted().toArray(String[]::new));sb.append(pathValues);}String createSign = HmacUtils.hmacSha256Hex(DEFAULT_SECRET, sb.toString());return createSign;}public static void main(String[] args) {String body = "{\n" +"\t\"name\": \"hjzgg\",\n" +"\t\"age\": 26\n" +"}";Map<String, String[]> params = new HashMap<>();params.put("var3", new String[]{"3"});params.put("var4", new String[]{"4"});String[] paths = new String[]{"1", "2"};System.out.println(sign(body, params, paths));}}

五、簽名驗證

  簡單寫了一個包含body參數,parameter參數,path variable參數的controller

@RestController @RequestMapping("example") public class ExampleController {@PostMapping(value = "test/{var1}/{var2}", produces = MediaType.ALL_VALUE)public String myController(@PathVariable String var1, @PathVariable String var2, @RequestParam String var3, @RequestParam String var4, @RequestBody User user) {return String.join(",", var1, var2, var3, var4, user.toString());}private static class User {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return new ToStringBuilder(this).append("name", name).append("age", age).toString();}} }

  通過 簽名核心工具類SignUtil 的main方法生成一個簽名,通過如下命令驗證

curl -X POST \'http://localhost:8080/example/test/1/2?var3=3&var4=4' \-H 'Content-Type: application/json' \-H 'X-SIGN: 4955125a3aa2782ab3def51dc958a34ca46e5dbb345d8808590fb53e81cc2687' \-d '{"name": "hjzgg","age": 26 }'

六、需要源碼

  請關注訂閱號,回復:signature, 便可查看。

  就先分享這么多了,更多分享請關注我們的技術公眾號!!!

轉載于:https://www.cnblogs.com/hujunzheng/p/10178584.html

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的java接口签名(Signature)实现方案续的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 五月伊人网 | www.操.com| 国产一区二区免费视频 | 日韩免费在线 | 91国产视频在线播放 | 欧美日本一区二区 | 国产精品美女一区二区三区 | 曰韩在线 | 亚洲91av| 成人污网站 | 精品在线播放视频 | 91视频久久久久 | 黄视频网站在线 | 美女高潮黄又色高清视频免费 | 亚洲一级Av无码毛片久久精品 | 久久男人 | 一级黄色性生活片 | 亚洲免费观看高清 | 免费亚洲婷婷 | 欧美三级欧美成人高清 | 无套内谢少妇毛片 | 成年人免费观看网站 | 黄色在线观看免费视频 | 久久99激情 | 大陆明星乱淫(高h)小说 | 午夜婷婷在线观看 | 波多野结衣精品视频 | wwwav网站| 中文一区二区在线播放 | 九九精品在线观看 | 午夜精品久久久久久久99老熟妇 | 在线观看免费高清视频 | 成人免费福利 | 久久无码人妻一区二区三区 | 日韩乱淫 | 亚一区 | 亚洲韩国精品 | 久草视频在线资源 | 久久国产一 | 在线麻豆 | 成人91免费视频 | 超碰在97| 欧美一区二区三区久久成人精品 | www.成年人| 日韩第一页在线观看 | 激情噜噜| av在线免| jizzjizz中国精品麻豆 | 亚州av片 | 亚洲天堂资源在线 | 99热这里只有精品在线 | 91性高潮久久久久久久 | 中文字幕第一页在线 | 欧美在线一二三区 | 杨幂国产精品一区二区 | 亚洲一区二区在线看 | 青青草在线免费观看 | 天堂а√在线最新版中文在线 | 国产乱淫精品一区二区三区毛片 | 日韩二区视频 | 欧美在线你懂的 | 国产精品无码久久久久久电影 | 一区二区日韩视频 | 女人一区二区三区 | 色秀av| 日本久久影视 | 性色欲情网站iwww九文堂 | 国产伦精品一区二区三区视频我 | www.黄色片 | 午夜网站在线观看 | 日本熟妇一区二区三区四区 | 欧美tickle狂笑裸体vk | 亚洲福利一区二区三区 | 99日韩| 四虎影院成人 | 99国产揄拍国产精品 | 亚洲 欧美 另类 综合 偷拍 | 成人免费看片98欧美 | se94se欧美 | 亚洲午夜精品视频 | 免费观看av | 色爱综合网 | 成人视屏在线 | 极品粉嫩小仙女高潮喷水久久 | 亚洲精品网站在线 | 中文字幕 国产 | 天天干天天操天天射 | 日韩性生活大片 | 精品亚洲在线 | 超清av在线 | 国产一区二区精品丝袜 | 亚洲com| 免费在线观看a级片 | 一本一道久久a久久综合蜜桃 | 香蕉视频色版 | 欧美色激情 | 足疗店女技师按摩毛片 | 日本国产精品一区 | 一区二区三区四区在线播放 |