sunshine
2024-11-05 00b4581009af28098da4286a8ef9f4d72c7c5728
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
package com.nova.sankuai.infra.utils;
 
import com.nova.sankuai.domain.vo.common.MessageCode;
import com.nova.sankuai.domain.vo.common.MessageImageCode;
import com.nova.sankuai.domain.vo.common.MessageVerifyVo;
import com.nova.sankuai.infra.config.CommonException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
 
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.regex.Pattern;
 
import static cn.hutool.core.util.ReUtil.isMatch;
 
/**
 * @author weikangdi
 */
public class MessageCodeUtil {
 
    public static Map<String, MessageCode> CODE_LIST = new HashMap<>();
 
    public static Map<String, MessageImageCode> IMAGE_CODE_LIST = new HashMap<>();
 
    public static Map<String, MessageVerifyVo> PHONE_VERIFY_LIST = new HashMap<>();
 
    public static Map<String, MessageVerifyVo> IP_VERIFY_LIST = new HashMap<>();
 
    public static final int COUNT_LIMIT = 30;
 
    public static final long EXPIRED_TIME = 300000L;
    public static final long INTERVAL_TIME = 90000L;
 
    private static final String REGEX_MOBILE_EXACT = "^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$";
    private static final Pattern PATTERN_REGEX_MOBILE_EXACT = Pattern.compile(REGEX_MOBILE_EXACT);
 
 
    public static MessageCode getCode(String phone) {
        return CODE_LIST.get(phone);
    }
 
    public static void setCode(String phone, MessageCode code) {
        if (CODE_LIST.get(phone) == null) {
            CODE_LIST.put(phone, code);
        } else {
            CODE_LIST.replace(phone, code);
        }
    }
 
    public static void checkCode(String phone, String code) {
        MessageCode messageCode = MessageCodeUtil.getCode(phone);
        if (messageCode == null) {
            throw new CommonException("无效的验证码");
        }
        if (messageCode.getCreateTime() != null && messageCode.getCreateTime() < (System.currentTimeMillis() - EXPIRED_TIME)) {
            throw new CommonException("验证码已过期");
        }
        if (!code.equals(messageCode.getCode())) {
            throw new CommonException("验证码错误");
        }
    }
 
    public static MessageCode createCode(String phone) {
        MessageCode messageCode = null;
        //随机生成验证码
        String code = "";
        Random random = new Random();
        for (int i = 0; i < 6; i++) {
            int r = random.nextInt(10);
            code = code + r;
        }
        //生成短信验证码对象
        messageCode = new MessageCode();
        messageCode.setPhone(phone);
        messageCode.setCode(code);
        messageCode.setCreateTime(System.currentTimeMillis());
        return messageCode;
    }
 
    public static MessageImageCode getImageCode(String phone) {
        return IMAGE_CODE_LIST.get(phone);
    }
 
    public static void setImageCode(String phone, MessageImageCode code) {
        if (IMAGE_CODE_LIST.get(phone) == null) {
            IMAGE_CODE_LIST.put(phone, code);
        } else {
            IMAGE_CODE_LIST.replace(phone, code);
        }
    }
 
 
    public static MessageVerifyVo getPhoneVerify(String phone) {
        return PHONE_VERIFY_LIST.get(phone);
    }
 
    public static void setPhoneVerify(String phone, String ip) {
        MessageVerifyVo messageVerifyVo = PHONE_VERIFY_LIST.get(phone);
        if (messageVerifyVo == null) {
            messageVerifyVo = new MessageVerifyVo();
            messageVerifyVo.setPhone(phone);
            messageVerifyVo.setCount(1);
            messageVerifyVo.setIp(ip);
            messageVerifyVo.setLastApplyTime(System.currentTimeMillis());
            PHONE_VERIFY_LIST.put(phone, messageVerifyVo);
        } else {
            messageVerifyVo.setCount(messageVerifyVo.getCount() + 1);
            messageVerifyVo.setLastApplyTime(System.currentTimeMillis());
            PHONE_VERIFY_LIST.replace(phone, messageVerifyVo);
        }
    }
 
    public static MessageVerifyVo getIpVerify(String ip) {
        return IP_VERIFY_LIST.get(ip);
    }
 
    public static void setIpVerify(String phone, String ip) {
        MessageVerifyVo messageVerifyVo = IP_VERIFY_LIST.get(ip);
        if (messageVerifyVo == null) {
            messageVerifyVo = new MessageVerifyVo();
            messageVerifyVo.setPhone(phone);
            messageVerifyVo.setCount(1);
            messageVerifyVo.setIp(ip);
            messageVerifyVo.setLastApplyTime(System.currentTimeMillis());
            IP_VERIFY_LIST.put(ip, messageVerifyVo);
        } else {
            messageVerifyVo.setCount(messageVerifyVo.getCount() + 1);
            messageVerifyVo.setLastApplyTime(System.currentTimeMillis());
            IP_VERIFY_LIST.replace(ip, messageVerifyVo);
        }
    }
 
    public static boolean sendVerify(String phone, String ip, Logger logger) {
        MessageCode code = getCode(phone);
        MessageVerifyVo ipVerify = getIpVerify(ip);
        MessageVerifyVo phoneVerify = getPhoneVerify(phone);
        if (phoneVerify != null) {
            Long lastApplyTime = phoneVerify.getLastApplyTime() + INTERVAL_TIME;
            if (System.currentTimeMillis() < lastApplyTime) {
                logger.error("登录验证码错误日志: 该手机号在验证码有效期内重复发送验证码,用户手机:" + phone + " 用户IP:" + ip);
                return false;
            }
            if (phoneVerify.getCount() >= COUNT_LIMIT) {
                logger.error("登录验证码错误日志: 该手机号已达到每日上限,用户手机:" + phone + " 用户IP:" + ip);
                return false;
            }
        }
        if (ipVerify != null) {
            Long lastApplyTime = ipVerify.getLastApplyTime() + INTERVAL_TIME;
            if (System.currentTimeMillis() < lastApplyTime) {
                logger.error("登录验证码错误日志: 该IP在验证码有效期内重复发送验证码,用户手机:" + phone + " 用户IP:" + ip);
                return false;
            }
        }
        if (code != null) {
            long time = code.getCreateTime() + INTERVAL_TIME;
            if (System.currentTimeMillis() < time) {
                logger.error("登录验证码错误日志: 验证期内重复发送验证码,用户手机:" + phone + " 用户IP:" + ip);
                return false;
            }
        }
        return true;
    }
 
    public static boolean verifyPhone(String phone) {
        if (!isMatch(PATTERN_REGEX_MOBILE_EXACT, phone)) {
            return false;
        }
        return true;
    }
 
    public static ResponseEntity getGenerateImgCode(String phone) throws IOException {
        ImageVerifyCode vCode = new ImageVerifyCode();
        BufferedImage image = vCode.getImage();
        String img = ImageVerifyCode.image2Base64(image);
        String verifyCode = vCode.getEncodedText();
        HashMap<String, Object> body = new HashMap<>();
        body.put("verifyCode", verifyCode);
        MessageCode code = MessageCodeUtil.getCode(phone);
        if (code == null) {
            body.put("isShow", false);
        } else {
            body.put("isShow", true);
        }
        body.put("img", img);
        MessageImageCode messageImageCode = new MessageImageCode(phone, verifyCode, System.currentTimeMillis());
        setImageCode(phone, messageImageCode);
        ResultJson<Map<String, Object>> respBody = ResultJson.ok(body);
        HttpHeaders headers = new HttpHeaders();
        headers.set("verify-code", verifyCode);
        return new ResponseEntity<>(respBody, headers, HttpStatus.OK);
    }
 
    public static boolean verifyImageCode(String phone, String verifyCode, String inputVerifyCode) {
        MessageImageCode imageCode = getImageCode(phone);
        if (imageCode == null) {
            return false;
        }
        IMAGE_CODE_LIST.remove(phone);
        if (!imageCode.getVerifyCode().equals(verifyCode)) {
            return false;
        }
        if (System.currentTimeMillis() - imageCode.getCreateTime() > 3 * 60 * 1000) {
            // 验证码超时
            return false;
        }
        if (!StringUtils.equals(verifyCode, ImageVerifyCode.encode(inputVerifyCode))) {
            // 图形验证码校验失败
            return false;
        }
        return true;
    }
 
    public static void clean() {
        PHONE_VERIFY_LIST = new HashMap<>();
        IP_VERIFY_LIST = new HashMap<>();
    }
}