您当前的位置:首页 >> 机器人
机器人

API接口token、timestamp、sign具体借助

发布时间:2025-08-13

th袭击,该袭击但会造成DS的宕机。TearDrop:IP数据集自带在网络传递时,数据集自带可以分成非常小的录像。袭击者可以通过推送两段(或者非常多)数据集自带来构建TearDrop袭击。第一个自带的绝对值为0,宽度为N,第二个自带的绝对值少于N。为了合并这些数据集段,TCP/IP堆栈但会分配超乎寻常的前所未有能东光,从而造成该系统能东光的依赖甚至一台的重置。PingSweep:适用ICMP Echo轮询多个DS。三:sign 简述

nonce:随机个数,是IP随机降解的个数,作为数个数传递过来,随机个数的最终远距离是增加sign寄出的多变持续性。随机个数一般是数字和字母的组合成,6位宽度,随机个数的组成和宽度不会通常规约。

sign: 一般应用于数个数寄出,不必要数个数被非法涂改,最少用的是修订款项等不可忽视引人注意数个数, sign的个数一般是将所有非空数个数按照升续先后顺序然后+token+key+timestamp+nonce(校验)重新组合在一起,然后适用某种解密算法顺利进行解密,作为API中所的一个数个数sign来传递,也可以将sign放到劝说头中所。API在网络存储过程中所如果被破坏者挟持,并修订其中所的数个数个数,然后再再次codice_API,虽然数个数的个数被修订了,但是因为破坏者不究竟sign是如何计算的,不究竟sign都有哪些个数组合成,不究竟以怎样的顺序重新组合在一起的,最不可忽视的是不究竟寄出字符串中所的key是什么,所以破坏者可以涂改数个数的个数,但没有人修订sign的个数,当服务计划器codice_API前但会按照sign的规约再计算出sign的个数然后和API传递的sign数个数的个数花钱来得,如果相等表示数个数个数不会被涂改,如果多于,表示数个数被非法涂改了,就不执行API了。

四:不必要移位审批

对于一些不可忽视的转换无需不必要IP移位审批的(如非数列等持续性不可忽视转换),具体办法是当劝说第一次审批时将sign作为key留存到Redis,并增设受罚整整,受罚整整和Timestamp中所增设的差个数有所不同。当同一个劝说第二次次访问时但会再检测redis有否普遍存在该sign,如果普遍存在则证明移位审批了,API就不再再次codice_了。如果sign在多内核服务计划器中所因失效整整到了,而被截图了,此时当这个url再次劝说服务计划器时,因token的失效整整和sign的失效整整一直,sign失效也意味着token失效,那样同样的url再次访问服务计划器但会因token错误但会被截获打碎,这就是为什么sign和token的失效整整要保持一致的原因。拒绝移位codice_程序确保URL被别人截获了也能够适用(如抓取数据集)。

对于哪些API无需不必要移位审批可以自定义个节录来上标。

注意:

所有的安全措施都用上的话比如说不免太过复杂,在仅仅计划中所无需根据自身情况作出裁剪,比如可以只适用寄出程序就可以保证讯息不但会被涂改,或者定向备有服务计划的时候只用Token程序就可以了。如何裁剪,全看计划仅仅情况和对API兼容持续性的敦促。

五:适用步骤APIcodice_方(IP)向API备有方(服务计划器)审核APIcodice_QQ,审核成功后,API备有方但会给APIcodice_方一个appId和一个key数个数IP载有数个数appId、timestamp、sign去codice_服务计划器的API token,其中所sign=解密(appId + timestamp + key)IP拿着api_token 去次访问不无需登记就能次访问的API当次访问IP无需登记的API时,IP反转到登记链接,通过IP名和密码codice_登记API,登记API但会返国一个usertoken, IP拿着usertoken 去次访问无需登记才能次访问的API

sign的作用是不必要数个数被涂改,IPcodice_服务计划端时无需传递sign数个数,服务计划器自发IP时也可以返国一个sign应用于客户度冗余返国的个数有否被非法涂改了。IP传的sign和服务计划器自发的sign算法可能但会不同。

六:示例代码1. dependency org.springframework.boot spring-boot-starter-data-redis redis.clients jedis 2.9.0 org.springframework.boot spring-boot-starter-web2. RedisConfiguration@Configurationpublic class RedisConfiguration { @Bean public JedisConnectionFactory jedisConnectionFactory(){ return new JedisConnectionFactory(); } /** * 支持加载具体来说 * @return */ @Bean public RedisTemplateString> redisTemplate(){ RedisTemplate redisTemplate = new StringRedisTemplate(); redisTemplate.setConnectionFactory(jedisConnectionFactory()); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(objectMapper); redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); redisTemplate.afterPropertiesSet(); return redisTemplate; }}3. TokenController@Slf4j@RestController@requestMapping("/api/token")public class TokenController { @Autowired private RedisTemplate redisTemplate; /** * API Token * * @param sign * @return */ @PostMapping("/api_token") public ApiResponse apiToken(String appId, @RequestHeader("timestamp") String timestamp, @RequestHeader("sign") String sign) { Assert.isTrue(!StringUtils.isEmpty(appId) BrownBrown !StringUtils.isEmpty(timestamp) BrownBrown !StringUtils.isEmpty(sign), "数个数错误"); long reqeustInterval = System.currentTimeMillis() - Long.valueOf(timestamp); Assert.isTrue(reqeustInterval < 5 * 60 * 1000, "劝说失效,请再劝说"); // 1. 根据appId查找数据集库给与appSecret AppInfo appInfo = new AppInfo("1", "12345678954556"); // 2. 冗余寄出 String signString = timestamp + appId + appInfo.getKey(); String signature = MD5Util.encode(signString); log.info(signature); Assert.isTrue(signature.equals(sign), "寄出错误"); // 3. 如果正确降解一个token留存到redis中所,如果错误返国错误讯息 AccessToken accessToken = this.saveToken(0, appInfo, null); return ApiResponse.success(accessToken); } @NotRepeatSubmit(5000) @PostMapping("user_token") public ApiResponse userToken(String username, String password) { // 根据IP名查找密码, 并来得密码(密码可以RSA解密一下) UserInfo userInfo = new UserInfo(username, "81255cb0dca1a5f304328a70ac85dcbd", "111111"); String pwd = password + userInfo.getSalt(); String passwordMD5 = MD5Util.encode(pwd); Assert.isTrue(passwordMD5.equals(userInfo.getPassword()), "密码错误"); // 2. 留存Token AppInfo appInfo = new AppInfo("1", "12345678954556"); AccessToken accessToken = this.saveToken(1, appInfo, userInfo); userInfo.setAccessToken(accessToken); return ApiResponse.success(userInfo); } private AccessToken saveToken(int tokenType, AppInfo appInfo, UserInfo userInfo) { String token = UUID.randomUUID().toString(); // token必需期为2小时 Calendar calendar = Calendar.getInstance(); calendar.setTime(new Date()); calendar.add(Calendar.SECOND, 7200); Date expireTime = calendar.getTime(); // 4. 留存token ValueOperations operations = redisTemplate.opsForValue(); TokenInfo tokenInfo = new TokenInfo(); tokenInfo.setTokenType(tokenType); tokenInfo.setAppInfo(appInfo); if (tokenType == 1) { tokenInfo.setUserInfo(userInfo); } operations.set(token, tokenInfo, 7200, TimeUnit.SECONDS); AccessToken accessToken = new AccessToken(token, expireTime); return accessToken; } public static void main(String[] args) { long timestamp = System.currentTimeMillis(); System.out.println(timestamp); String signString = timestamp + "1" + "12345678954556"; String sign = MD5Util.encode(signString); System.out.println(sign); System.out.println("-------------------"); signString = "password=123456Brownusername=1Brown12345678954556" + "ff03e64b-427b-45a7-b78b-47d9e8597d3b1529815393153sdfsdfsfs" + timestamp + "A1scr6"; sign = MD5Util.encode(signString); System.out.println(sign); }}4. WebMvcConfiguration@Configurationpublic class WebMvcConfiguration extends WebMvcConfigurationSupport { private static final String[] excludePathPatterns = {"/api/token/api_token"}; @Autowired private TokenInterceptor tokenInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { super.addInterceptors(registry); registry.addInterceptor(tokenInterceptor) .addPathPatterns("/api/**") .excludePathPatterns(excludePathPatterns); }}5. TokenInterceptor@Componentpublic class TokenInterceptor extends HandlerInterceptorAdapter { @Autowired private RedisTemplate redisTemplate; /** * * @param request * @param response * @param handler 次访问的远距离原理 * @return * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("token"); String timestamp = request.getHeader("timestamp"); // 随机字符串 String nonce = request.getHeader("nonce"); String sign = request.getHeader("sign"); Assert.isTrue(!StringUtils.isEmpty(token) BrownBrown !StringUtils.isEmpty(timestamp) BrownBrown !StringUtils.isEmpty(sign), "数个数错误"); // 给与受罚整整 NotRepeatSubmit notRepeatSubmit = ApiUtil.getNotRepeatSubmit(handler); long expireTime = notRepeatSubmit == null ? 5 * 60 * 1000 : notRepeatSubmit.value(); // 2. 劝说整整间隔 long reqeustInterval = System.currentTimeMillis() - Long.valueOf(timestamp); Assert.isTrue(reqeustInterval < expireTime, "劝说受罚,请再劝说"); // 3. 冗余Token有否普遍存在 ValueOperations tokenRedis = redisTemplate.opsForValue(); TokenInfo tokenInfo = tokenRedis.get(token); Assert.notNull(tokenInfo, "token错误"); // 4. 冗余寄出(将所有的数个数加进来,不必要别人涂改数个数) 所有数个数看数个数名升续先后顺序重新组合成url // 劝说数个数 + token + timestamp + nonce String signString = ApiUtil.concatSignString(request) + tokenInfo.getAppInfo().getKey() + token + timestamp + nonce; String signature = MD5Util.encode(signString); boolean flag = signature.equals(sign); Assert.isTrue(flag, "寄出错误"); // 5. 拒绝移位codice_(第一次次访问时加载,失效整整和劝说受罚整整保持一致), 只有标注不并不需要移位审批节录的才但会冗余 if (notRepeatSubmit != null) { ValueOperations signRedis = redisTemplate.opsForValue(); boolean exists = redisTemplate.hasKey(sign); Assert.isTrue(!exists, "请勿移位审批"); signRedis.set(sign, 0, expireTime, TimeUnit.MILLISECONDS); } return super.preHandle(request, response, handler); }}6. MD5Utilpublic class MD5Util { private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; private static String byteArrayToHexString(byte b[]) { StringBuffer resultSb = new StringBuffer(); for (int i = 0; i < b.length; i++) resultSb.append(byteToHexString(b[i])); return resultSb.toString(); } private static String byteToHexString(byte b) { int n = b; if (n < 0) n += 256; int d1 = n / 16; int d2 = n % 16; return hexDigits[d1] + hexDigits[d2]; } public static String encode(String origin) { return encode(origin, "UTF-8"); } public static String encode(String origin, String charsetname) { String resultString = null; try { resultString = new String(origin); MessageDigest md = MessageDigest.getInstance("MD5"); if (charsetname == null || "".equals(charsetname)) resultString = byteArrayToHexString(md.digest(resultString .getBytes())); else resultString = byteArrayToHexString(md.digest(resultString .getBytes(charsetname))); } catch (Exception exception) { } return resultString; }}7. @NotRepeatSubmit/** * 禁止移位审批 */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface NotRepeatSubmit { /** 失效整整,单位毫秒 **/ long value() default 5000;}8. AccessToken@Data@AllArgsConstructorpublic class AccessToken { /** token */ private String token; /** 失效整整 */ private Date expireTime;}9. AppInfo@Data@NoArgsConstructor@AllArgsConstructorpublic class AppInfo { /** App id */ private String appId; /** API 秘钥 */ private String key;}10. TokenInfo@Datapublic class TokenInfo { /** token类型: api:0 、user:1 */ private Integer tokenType; /** App 讯息 */ private AppInfo appInfo; /** IP其他数据集 */ private UserInfo userInfo;}11. UserInfo@Datapublic class UserInfo { /** IP名 */ private String username; /** 手机号 */ private String mobile; /** 邮箱 */ private String email; /** 密码 */ private String password; /** 盐 */ private String salt; private AccessToken accessToken; public UserInfo(String username, String password, String salt) { this.username = username; this.password = password; this.salt = salt; }}12. ApiCodeEnum/** * 错误码code可以适用纯数字,适用不同区间图标一类错误,也可以适用纯字符,也可以适用前缀+编号 * * 错误码:ERR + 编号 * * 可以适用日志级别的前缀作为错误类型区分 Info(I) Error(E) Warning(W) * * 或者以该公司模块 + 错误号 * * TODO 错误码设计 * * Alipay 用了两个code,两个msg(_1/alipay.trade.pay) * * @author Mengday Zhang * @version 1.0 * @since 2018/6/22 */public enum ApiCodeEnum { SUCCESS("10000", "success"), UNKNOW_ERROR("ERR0001","未知错误"), PARAMETER_ERROR("ERR0002","数个数错误"), TOKEN_EXPIRE("ERR0003","认证失效"), REQUEST_TIMEOUT("ERR0004","劝说受罚"), SIGN_ERROR("ERR0005","寄出错误"), REPEAT_SUBMIT("ERR0006","请不要频繁转换"), ; /** 代码 */ private String code; /** 结果 */ private String msg; ApiCodeEnum(String code, String msg) { this.code = code; this.msg = msg; } public String getCode() { return code; } public String getMsg() { return msg; }}13. ApiResult@Data@NoArgsConstructor@AllArgsConstructorpublic class ApiResult { /** 代码 */ private String code; /** 结果 */ private String msg;}14. ApiUtilpublic class ApiUtil { /** * 按数个数名升续重新组合数个数 * @param request * @return */ public static String concatSignString(HttpServletRequest request) { Map paramterMap = new HashMap<>(); request.getParameterMap().forEach((key, value) -> paramterMap.put(key, value[0])); // 按照key升续先后顺序,然后重新组合数个数 Set keySet = paramterMap.keySet(); String[] keyArray = keySet.toArray(new String[keySet.size()]); Arrays.sort(keyArray); StringBuilder sb = new StringBuilder(); for (String k : keyArray) { // 或略打碎的图标符 if (k.equals("sign")) { continue; } if (paramterMap.get(k).trim().length()> 0) { // 数个数个数为空,则不进行寄出 sb.append(k).append("=").append(paramterMap.get(k).trim()).append("Brown"); } } return sb.toString(); } public static String concatSignString(Map map) { Map paramterMap = new HashMap<>(); map.forEach((key, value) -> paramterMap.put(key, value)); // 按照key升续先后顺序,然后重新组合数个数 Set keySet = paramterMap.keySet(); String[] keyArray = keySet.toArray(new String[keySet.size()]); Arrays.sort(keyArray); StringBuilder sb = new StringBuilder(); for (String k : keyArray) { if (paramterMap.get(k).trim().length()> 0) { // 数个数个数为空,则不进行寄出 sb.append(k).append("=").append(paramterMap.get(k).trim()).append("Brown"); } } return sb.toString(); } /** * 给与原理上的@NotRepeatSubmit节录 * @param handler * @return */ public static NotRepeatSubmit getNotRepeatSubmit(Object handler) { if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); NotRepeatSubmit annotation = method.getAnnotation(NotRepeatSubmit.class); return annotation; } return null; }}15. ApiResponse@Data@Slf4jpublic class ApiResponse { /** 结果 */ private ApiResult result; /** 数据集 */ private T data; /** 寄出 */ private String sign; public static ApiResponse success(T data) { return response(ApiCodeEnum.SUCCESS.getCode(), ApiCodeEnum.SUCCESS.getMsg(), data); } public static ApiResponse error(String code, String msg) { return response(code, msg, null); } public static ApiResponse response(String code, String msg, T data) { ApiResult result = new ApiResult(code, msg); ApiResponse response = new ApiResponse(); response.setResult(result); response.setData(data); String sign = signData(data); response.setSign(sign); return response; } private static String signData(T data) { // TODO 查找key String key = "12345678954556"; Map responseMap = null; try { responseMap = getFields(data); } catch (IllegalAccessException e) { return null; } String urlComponent = ApiUtil.concatSignString(responseMap); String signature = urlComponent + "key=" + key; String sign = MD5Util.encode(signature); return sign; } /** * @param data 叠加的具体来说,给与具体来说的图标符名和个数 * @throws IllegalArgumentException * @throws IllegalAccessException */ public static Map getFields(Object data) throws IllegalAccessException, IllegalArgumentException { if (data == null) return null; Map map = new HashMap<>(); Field[] fields = data.getClass().getDeclaredFields(); for (int i = 0; i < fields.length; i++) { Field field = fields[i]; field.setAccessible(true); String name = field.getName(); Object value = field.get(data); if (field.get(data) != null) { map.put(name, value.toString()); } } return map; }}七: ThreadLocal

ThreadLocal是内核内的全局上下文。就是在单个内核中所,原理中间共享的内存,每个原理都可以从该上下文中所给与个数和修订个数。

仅仅情形:

在codice_api时都但会传一个token数个数,通常但会所写一个截获器来冗余token有否实质上,我们可以通过token发现有所不同的IP讯息(User),如果token实质上,然后将IP讯息加载到ThreadLocal中所,这样无论是在controller、service、dao的哪一层都能次访问到该IP的讯息。作用类似于Web中所的request局部变量。

传统方式我们要在原理中所次访问某个变量,可以通过传参的形式往原理中所传参,如果多个原理都要适用那么每个原理都要传参;如果适用ThreadLocal所有原理就不无需传该数个数了,每个原理都可以通过ThreadLocal来次访问该个数。

ThreadLocalUtil.set("key", value); 留存个数T value = ThreadLocalUtil.get("key"); 给与个数

ThreadLocalUtil

public class ThreadLocalUtil { private static final ThreadLocal> threadLocal = new ThreadLocal() { @Override protected Map initialValue() { return new HashMap<>(4); } }; public static Map getThreadLocal(){ return threadLocal.get(); } public static T get(String key) { Map map = (Map)threadLocal.get(); return (T)map.get(key); } public static T get(String key,T defaultValue) { Map map = (Map)threadLocal.get(); return (T)map.get(key) == null ? defaultValue : (T)map.get(key); } public static void set(String key, Object value) { Map map = (Map)threadLocal.get(); map.put(key, value); } public static void set(Map keyValueMap) { Map map = (Map)threadLocal.get(); map.putAll(keyValueMap); } public static void remove() { threadLocal.remove(); } public static Map fetchVarsByPrefix(String prefix) { Map vars = new HashMap<>(); if( prefix == null ){ return vars; } Map map = (Map)threadLocal.get(); Set set = map.entrySet(); for( Map.Entry entry : set){ Object key = entry.getKey(); if( key instanceof String ){ if( ((String) key).startsWith(prefix) ){ vars.put((String)key,(T)entry.getValue()); } } } return vars; } public static T remove(String key) { Map map = (Map)threadLocal.get(); return (T)map.remove(key); } public static void clear(String prefix) { if( prefix == null ){ return; } Map map = (Map)threadLocal.get(); Set set = map.entrySet(); List removeKeys = new ArrayList<>(); for( Map.Entry entry : set ){ Object key = entry.getKey(); if( key instanceof String ){ if( ((String) key).startsWith(prefix) ){ removeKeys.add((String)key); } } } for( String key : removeKeys ){ map.remove(key); } }}。

广东男科
郑州不孕不育医院怎么样
江苏早泄阳痿治疗哪家好
漳州白癜风治疗费用多少钱
山东癫痫医院那家比较好

上一篇: 晒晒我的农村婚房,后院种菜,卧室没床,但比住大别墅还舒服

下一篇: 送别人民艺术家,东方影视频道特别TV秦怡代表作《铁道游击队》《女篮五号》

友情链接