Security design scheme of third party application obtaining data

In the actual business, it is inevitable to interact and transfer data with the third-party system, so how to ensure the security of data in the transmission process (anti stealing)? In addition to the https protocol, can we add a general set of algorithms and specifications to ensure the security of transmission?

 

Now let's discuss some common security methods of API design, which may not be the best and have more powerful implementation methods, but this is my own experience sharing

 

1, About token

 

Token: access token, which is used in the interface to identify the identity and credentials of the interface caller and reduce the number of user name and password transfers. In general, the client (interface caller) needs to apply for an interface calling account from the server first. The server will give an appId and a key, which are used for parameter signature. Note that the key is saved to the client, and some security processing needs to be done to prevent leakage.

 

The value of token is generally UUID. After the server generates token, it needs to take token as key and save some information associated with token as value to the cache server (redis). When a request comes, the server will go to the cache server to check whether the token exists. If it exists, the interface will be called. There is no return interface error. It is usually implemented by interceptor or filter , tokens are divided into two types:

 

  • API token (interface token): used to access interfaces that do not require user login, such as login, registration, some basic data acquisition, etc. To obtain the interface token, you need to replace appId, timestamp and sign, sign = encrypt (timestamp+key)

  • User token: used to access the interface after user login, such as: get my basic information, save, modify, delete, etc. To obtain a user token, you need to change the user name and password

 

About the timeliness of Token: token can be one-time or effective in a period of time. The specific use depends on the business needs.

 

In general, the interface is better to use https protocol. If http protocol is used, the Token mechanism is just to reduce the possibility of being hacked. In fact, it can only prevent gentlemen from being villains.

 

Generally, three parameters, token, timestamp and sign, will be passed as parameters in the interface at the same time. Each parameter has its own purpose.

 

2, Introduction to timestamp

 

Timestamp: timestamp, which is the current timestamp when the client calls the interface. The timestamp is used to prevent DoS attacks.

 

When the hacker hijacks the url of the request to DOS attack, each time the interface is called, the interface will judge the difference between the current system time of the server and the timestamp passed in the interface. If the difference exceeds a certain set time (if it is 5 minutes), the request will be blocked. If it is within the set timeout range, DoS attack cannot be prevented. Timestamp mechanism can only reduce the time of DoS attack and shorten the attack time. If the hacker modifies the value of the time stamp, it can be handled through the sign signature mechanism.

 

DoS

 

DOS is the abbreviation of Denial of Service, that is, Denial of Service. The attack of DOS is called DoS attack, which aims to make computers or networks unable to provide normal services. The most common DoS attacks are network bandwidth attacks and connectivity attacks.

 

DoS attack refers to the intentional attack on the defect of network protocol implementation or the brutal exhaustion of the resources of the attacked object directly through barbaric means. The purpose is to make the target computer or network unable to provide normal services or resource access, so that the service system of the target system stops responding or even collapses, but this attack does not include the invasion of the target server or the target network equipment. These service resources include network bandwidth, file system space capacity, open processes or allowed connections. This kind of attack will lead to the lack of resources. No matter how fast the computer processes, how large the memory capacity and how fast the network bandwidth are, the consequences of this attack cannot be avoided.

 

  • Pingflood: this attack sends a large number of ping packets to the destination host in a short time, resulting in network congestion or host resource exhaustion.

     

  • Synflood: this attack sends SYN packets to the destination host with multiple random source host addresses, but does not respond after receiving the SYN ACK of the destination host. In this way, the destination host establishes a large number of connection queues for these source hosts. Moreover, because no ACK has been received, these queues are maintained all the time, resulting in a large consumption of resources and unable to provide services to normal requests.

     

  • Smurf: the attack sends a packet with a specific request (such as ICMP response request) to the broadcast address of a subnet, and disguises the source address as the host address to be attacked. All hosts on the subnet respond to the broadcast packet request and send the packet to the attacked host, which makes the host be attacked.

     

  • Land based: the attacker sets the source address and destination address of a packet as the address of the target host, and then sends the packet to the attacked host through IP spoofing. This packet can cause the attacked host to fall into a dead cycle due to trying to establish a connection with itself, thus greatly reducing the system performance.

     

  • Ping of Death: according to the specification of TCP/IP, the maximum length of a packet is 65536 bytes. Although the length of a packet cannot exceed 65536 bytes, the stack of multiple fragments of a packet can be achieved. When a host receives a packet with a length greater than 65536 bytes, it is attacked by Ping of Death, which will cause the host to shut down.

     

  • Teardrop: when IP packets are transmitted over the network, they can be divided into smaller segments. An attacker can implement a teardrop attack by sending two (or more) packets. The offset of the first packet is 0, the length is N, and the offset of the second packet is less than N. In order to merge these data segments, the TCP/IP stack will allocate extraordinary huge resources, resulting in the lack of system resources and even the restart of the machine.

     

  • PingSweep: polling multiple hosts using ICMP Echo.

 

3, About sign

 

nonce: random value is the value generated randomly by the client. It is passed as a parameter. The purpose of random value is to increase the variability of sign signature. Random value is generally a combination of numbers and letters, with 6-digit length. There is no fixed rule for the composition and length of random value.

 

Sign: it is generally used for parameter signature to prevent illegal tampering of parameters. The most common is to modify important sensitive parameters such as amount, Generally, the value of sign is to sort all non empty parameters according to the ascending sequence, then + token + key + timestamp + nonce (random number) is spliced together, and then some encryption algorithm is used for encryption, which is passed as a parameter sign in the interface, or put the sign in the request header.

 

In the process of network transmission, if the interface is hijacked by the hacker, and the parameter value is modified, and then the interface is called again. Although the parameter value is modified, the hacker does not know how the sign is calculated, what the value of the sign is made up of, what the sequence is spliced together, and the most important thing is that he does not know what the key in the signature string is , so the hacker can tamper with the value of the parameter, but can't modify the value of the sign. Before the server calls the interface, it will recalculate the value of the sign according to the rules of the sign, and then compare it with the value of the sign parameter passed by the interface. If it is equal, it means that the parameter value has not been tampered with. If it is unequal, it means that the parameter has been tampered with illegally, and the interface will not be executed.

 

4, Prevent duplicate submissions

 

For some important operations that need to prevent the client from repeatedly submitting (such as non idempotent important operations), the specific method is to save the sign as the key to redis when the request is submitted for the first time, and set the timeout, which is the same as the difference set in Timestamp.

 

When the same request is accessed for the second time, it will first check whether the sign exists in redis. If it exists, it will prove that the sign is repeatedly submitted, and the interface will no longer be called. If the sign is deleted in the cache server because the expiration time is up, then when the URL requests the server again, because the expiration time of the token and the expiration time of the sign are the same, the sign expiration also means that the token expires, so the same URL revisit server will be blocked because of the token error, which is why the expiration time of the sign and the token should be consistent . The mechanism of rejecting repeated calls ensures that URLs are intercepted by others and cannot be used (such as fetching data).

 

For which interfaces need to prevent duplicate submission, you can customize comments to mark.

 

Note: when all the security measures are used, sometimes they are too complex. In actual projects, they need to be tailored according to their own situations. For example, you can only use the signature mechanism to ensure that the information will not be tampered with, or you can only use the Token mechanism to provide services. How to cut it depends on the actual situation of the project and the requirements for interface security.

 

Five. Use process

 

1. The interface caller (client) applies to the interface provider (server) for the interface calling account. After the application is successful, the interface provider will give the interface caller an appId and a key parameter

 

2. The client carries the parameters appId, timestamp and sign to call the API token of the server, where sign = encryption (appId + timestamp + key)

 

3. The client takes the api_token to access the interface that can be accessed without login

 

4. When accessing the interface that users need to log in, the client will jump to the login page and call the login interface through user name and password. The login interface will return a usertoken, and the client will take the usertoken to access the interface that needs to be logged in

 

The function of sign is to prevent parameters from being tampered with. When the client calls the server, it needs to pass the sign parameter. When the server responds to the client, it can also return a sign to verify whether the returned value has been tampered with illegally. The sign algorithm passed by the client and the sign algorithm responded by the server may be different.

 

6, Sample code

 

1. dependency

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency>    <groupId>redis.clients</groupId>    <artifactId>jedis</artifactId>    <version>2.9.0</version></dependency>

<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-web</artifactId></dependency>

 

2. RedisConfiguration

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
@Configurationpublic class RedisConfiguration {    @Bean    public JedisConnectionFactory jedisConnectionFactory(){        return new JedisConnectionFactory();    }

    /**     * Support for storage objects     * @return     */    @Bean    public RedisTemplate<String, String> redisTemplate(){        RedisTemplate<String, String> 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<AccessToken> apiToken(String appId, @RequestHeader("timestamp") String timestamp, @RequestHeader("sign") String sign) {        Assert.isTrue(!StringUtils.isEmpty(appId) && !StringUtils.isEmpty(timestamp) && !StringUtils.isEmpty(sign), "Parameter error");

        long reqeustInterval = System.currentTimeMillis() - Long.valueOf(timestamp);        Assert.isTrue(reqeustInterval < 5 * 60 * 1000, "Request expired, please request again");

        // 1. Query the database according to appId to obtain appSecret        AppInfo appInfo = new AppInfo("1", "12345678954556");

        // 2. Verification signature        String signString = timestamp + appId + appInfo.getKey();        String signature = MD5Util.encode(signString);        log.info(signature);        Assert.isTrue(signature.equals(sign), "Signature error");

        // 3. If a token is generated correctly and saved in redis, an error message will be returned        AccessToken accessToken = this.saveToken(0, appInfo, null);

        return ApiResponse.success(accessToken);    }



    @NotRepeatSubmit(5000)    @PostMapping("user_token")    public ApiResponse<UserInfo> userToken(String username, String password) {        // Query the password according to the user name, and compare the password (the password can be encrypted by RSA)        UserInfo userInfo = new UserInfo(username, "81255cb0dca1a5f304328a70ac85dcbd", "111111");        String pwd = password + userInfo.getSalt();        String passwordMD5 = MD5Util.encode(pwd);        Assert.isTrue(passwordMD5.equals(userInfo.getPassword()), "Password error");

        // 2. Save 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 is valid for 2 hours        Calendar calendar = Calendar.getInstance();        calendar.setTime(new Date());        calendar.add(Calendar.SECOND, 7200);        Date expireTime = calendar.getTime();

        // 4. Save token        ValueOperations<String, TokenInfo> 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=123456&username=1&12345678954556" + "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 Target method of access     * @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");        // Random string        String nonce = request.getHeader("nonce");        String sign = request.getHeader("sign");        Assert.isTrue(!StringUtils.isEmpty(token) && !StringUtils.isEmpty(timestamp) && !StringUtils.isEmpty(sign), "Parameter error");

        // Get timeout        NotRepeatSubmit notRepeatSubmit = ApiUtil.getNotRepeatSubmit(handler);        long expireTime = notRepeatSubmit == null ? 5 * 60 * 1000 : notRepeatSubmit.value();

        // 2. Request interval        long reqeustInterval = System.currentTimeMillis() - Long.valueOf(timestamp);        Assert.isTrue(reqeustInterval < expireTime, "Request timed out, please request again");

        // 3. Verify whether the Token exists        ValueOperations<String, TokenInfo> tokenRedis = redisTemplate.opsForValue();        TokenInfo tokenInfo = tokenRedis.get(token);        Assert.notNull(tokenInfo, "token error");

        // 4. Verify the signature (add all parameters to prevent others from tampering with the parameters) all parameters are spliced into a url according to the ascending sequence of parameter names        // Request parameter + 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, "Signature error");

        // 5. Reject repeated calls (stored in the first access, and the expiration time is the same as the request timeout time). Only those marked as not allowed to submit comments repeatedly will be verified        if (notRepeatSubmit != null) {            ValueOperations<String, Integer> signRedis = redisTemplate.opsForValue();            boolean exists = redisTemplate.hasKey(sign);            Assert.isTrue(!exists, "Do not submit again");            signRedis.set(sign, 0, expireTime, TimeUnit.MILLISECONDS);        }

        return super.preHandle(request, response, handler);    }}

 

6. Md5util - MD5 tool class, encrypt to generate digital signature

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
public 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 ----- custom annotation to prevent repeated submission.

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
/** * Prohibit duplicate submission */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface NotRepeatSubmit {    /** Expiration time in milliseconds**/    long value() default 5000;}

 

8. AccessToken

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
@Data@AllArgsConstructorpublic class AccessToken {    /** token */    private String token;

    /** Expiration time */    private Date expireTime;}

 

9. AppInfo

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
@Data@NoArgsConstructor@AllArgsConstructorpublic class AppInfo {    /** App id */    private String appId;    /** API Secret key */    private String key;}

 

10. TokenInfo

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
@Datapublic class TokenInfo {    /** token Type: api:0, user:1 */    private Integer tokenType;

    /** App information */    private AppInfo appInfo;

    /** User other data */    private UserInfo userInfo;}

 

11. UserInfo

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
@Datapublic class UserInfo {    /** user name */    private String username;    /** cell-phone number */    private String mobile;    /** mailbox */    private String email;    /** password */    private String password;    /** salt */    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

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
/** * Error code can use pure numbers, use different intervals to identify a type of error, use pure characters, or use prefix + number * * Error code: ERR + number * * You can distinguish Info(I) Error(E) Warning(W) by using the prefix of the log level as the error type * * Or business module + error number * * TODO Error code design * * Alipay Two code s and two MSG are used (https://docs.open.alipay.com/api_1 / Alipay. Trade. Pay) */public enum ApiCodeEnum {    SUCCESS("10000", "success"),    UNKNOW_ERROR("ERR0001","unknown error"),    PARAMETER_ERROR("ERR0002","Parameter error"),    TOKEN_EXPIRE("ERR0003","Expired certification"),    REQUEST_TIMEOUT("ERR0004","request timeout"),    SIGN_ERROR("ERR0005","Signature error"),    REPEAT_SUBMIT("ERR0006","Please do not operate frequently"),    ;

    /** code */    private String code;

    /** result */    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 {

    /** code */    private String code;

    /** result */    private String msg;}

 

14. ApiUtil - - this reference Alipay encryption algorithm written. I directly Copy came over.

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
public class ApiUtil {

    /**     * Upgrade splicing parameters by parameter name     * @param request     * @return     */    public static String concatSignString(HttpServletRequest request) {        Map<String, String> paramterMap = new HashMap<>();        request.getParameterMap().forEach((key, value) -> paramterMap.put(key, value[0]));        // Sort by key upgrade, and then splice parameters        Set<String> keySet = paramterMap.keySet();        String[] keyArray = keySet.toArray(new String[keySet.size()]);        Arrays.sort(keyArray);        StringBuilder sb = new StringBuilder();        for (String k : keyArray) {            // Or omitted fields            if (k.equals("sign")) {                continue;            }            if (paramterMap.get(k).trim().length() > 0) {                // If the parameter value is empty, signature is not involved                sb.append(k).append("=").append(paramterMap.get(k).trim()).append("&");            }        }

        return sb.toString();    }

    public static String concatSignString(Map<String, String> map) {        Map<String, String> paramterMap = new HashMap<>();        map.forEach((key, value) -> paramterMap.put(key, value));        // Sort by key upgrade, and then splice parameters        Set<String> 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) {                // If the parameter value is empty, signature is not involved                sb.append(k).append("=").append(paramterMap.get(k).trim()).append("&");            }        }        return sb.toString();    }

    /**     * Get @ NotRepeatSubmit annotation on method     * @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<T> {    /** result */    private ApiResult result;

    /** data */    private T data;

    /** autograph */    private String sign;



    public static <T> 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 <T> 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 <T> String signData(T data) {        // TODO query key        String key = "12345678954556";        Map<String, String> 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 Reflected object, get the field name and value of the object     * @throws IllegalArgumentException     * @throws IllegalAccessException     */    public static Map<String, String> getFields(Object data) throws IllegalAccessException, IllegalArgumentException {        if (data == null) return null;        Map<String, String> 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;    }}

 

7, ThreadLocal

 

ThreadLocal is the global context within a thread. It is the memory shared between methods in a single thread. Each method can get and modify values from the context.

 

Actual case:

 

When the api is called, a token parameter will be passed. Usually, an interceptor will be written to verify whether the token is legal. We can find the corresponding user information (User) through the token. If the token is legal, then the user information will be stored in ThreadLocal, so that the information of the user can be accessed at any layer of the controller, service and dao. It acts like a request scope in the Web.

 

In the traditional way, if we want to access a variable in a method, we can pass a parameter to the method in the form of a parameter. If multiple methods are to be used, then each method needs to pass a parameter. If we use ThreadLocal, all methods do not need to pass a parameter. Each method can access the value through ThreadLocal.

 

  • ThreadLocalUtil.set("key", value); save value

  • T value = ThreadLocalUtil.get("key"); get value

 

ThreadLocalUtil

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
public class ThreadLocalUtil<T> {    private static final ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal() {        @Override        protected Map<String, Object> initialValue() {            return new HashMap<>(4);        }    };



    public static Map<String, Object> getThreadLocal(){        return threadLocal.get();    }

    public static <T> T get(String key) {        Map map = (Map)threadLocal.get();        return (T)map.get(key);    }

    public static <T> 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<String, Object> keyValueMap) {        Map map = (Map)threadLocal.get();        map.putAll(keyValueMap);    }

    public static void remove() {        threadLocal.remove();    }

    public static <T> Map<String,T> fetchVarsByPrefix(String prefix) {        Map<String,T> vars = new HashMap<>();        if( prefix == null ){            return vars;        }        Map map = (Map)threadLocal.get();        Set<Map.Entry> 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> 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<Map.Entry> set = map.entrySet();        List<String> 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);        }    }}

 

Summary: This is some commonly used parameters and use examples in the interaction process of the third-party data interface, hoping to help you a little.

 

Of course, in order to ensure more security, RSA,RSA2, AES and other encryption methods can be added to ensure more security of data, but the only disadvantage is that encryption and decryption consume CPU resources.

Tags: Programming network Redis Spring less

Posted on Tue, 12 May 2020 08:17:32 -0700 by flyingeagle855