RealNameAuthorizeServiceImpl.java
14.4 KB
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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
package com.yohoufo.user.service.impl;
import com.yoho.core.config.ConfigReader;
import com.yoho.error.exception.ServiceException;
import com.yoho.tools.common.beans.ApiResponse;
import com.yohoufo.dal.user.IUserAuthorizeHistoryDao;
import com.yohoufo.dal.user.IUserAuthorizeInfoDao;
import com.yohoufo.dal.user.model.UserAuthorizeHistory;
import com.yohoufo.dal.user.model.UserAuthorizeInfo;
import com.yohoufo.user.cache.CacheService;
import com.yohoufo.user.common.EnumBankBackCode;
import com.yohoufo.user.requestVO.RealNameAuthorizeReqVO;
import com.yohoufo.user.responseVO.AuthorizeResultRespVO;
import com.yohoufo.user.service.IRealNameAuthorizeService;
import com.yohoufo.user.service.risk.GraphVerifyService;
import lombok.Data;
import net.sf.json.JSONObject;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.codec.digest.HmacUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 用户身份实名认证
*/
@Service("realNameAuthorizeServiceImpl")
public class RealNameAuthorizeServiceImpl implements IRealNameAuthorizeService {
private Logger logger = LoggerFactory.getLogger(RealNameAuthorizeServiceImpl.class);
private final static DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
//请求实名认证银联接口url 测试环境
private final String requestUrl="http://58.247.0.18:29015/v1/datacenter/smartverification/bankcard/verify";
//请求实名认证银联接口url 生产环境
//private final String requestUrl="https://api-mop.chinaums.com/v1/datacenter/smartverification/bankcard/verify";
ExecutorService executeService = Executors.newFixedThreadPool(10);
@Autowired
private IUserAuthorizeInfoDao userAuthorizeInfoDao;
@Autowired
private IUserAuthorizeHistoryDao userAuthorizeHistoryDao;
@Resource(name="authorizeBankRestTemplate")
private RestTemplate restTemplate;
@Resource(name = "core-config-reader")
private ConfigReader configReader;
@Autowired
private CacheService cacheService;
@Autowired
private GraphVerifyService graphVerifyService;
public UserAuthorizeInfo getValidAuthorizeInfo(int uid){
// 从redis缓存中获取
UserAuthorizeInfo authorizeInfo = cacheService.getUserAuthorizeInfo(uid);
if(null != authorizeInfo){
return authorizeInfo;
}
//如果不存在,则从数据库获取
authorizeInfo= userAuthorizeInfoDao.selectValidAuthorizeInfoByUid(uid);
if(authorizeInfo!=null){
//保存到redis
try{
cacheService.setUserAuthorizeInfo( authorizeInfo);
}catch(Exception e){
logger.warn("set valid authorize info to redis error. uid={}", uid);
}
}
return authorizeInfo;
}
/**
* 实名身份认证
*/
public JSONObject authorizeRealNameWithBank(RealNameAuthorizeReqVO reqVO) throws ServiceException {
int uid=reqVO.getUid();
String cardNo=reqVO.getCardNo();
String certNo=reqVO.getCertNo();
String name=reqVO.getName();
//组织报文
JSONObject msgContentParams=constructMsg(cardNo,certNo,name);
//根据报文体,生成HTTP报文头的认证内容 (open-body-sig 方式)
String authorizationContentByOpenBodySig=generateAuthorizationByOpenBodySig(msgContentParams);
//请求返回
PostBankResult responseResult = postRequest(msgContentParams,authorizationContentByOpenBodySig);
//返回结果处理:请求成功入认证信息库,请求失败记录的redis,后续超过一定次数则开启验证码
long ts=getLocalDateTime().toEpochSecond(ZoneOffset.of("+8"));
if(responseResult.isSucFlag()){
UserAuthorizeInfo userAuthorizeInfo =new UserAuthorizeInfo();
userAuthorizeInfo.setUid(uid);
userAuthorizeInfo.setValidStatus(1);
userAuthorizeInfo.setCardNo(cardNo);
userAuthorizeInfo.setCertNo(certNo);
userAuthorizeInfo.setCertName(name);
userAuthorizeInfo.setCreateTime(ts);
userAuthorizeInfo.setUpdateTime(ts);
userAuthorizeInfoDao.insert(userAuthorizeInfo);
}else{
//访问失败记录redis,保存半小时,失败超过一定次数打开验证码开关
recordFailTimesAndOpenGraphVerify(reqVO);
}
//访问银联接口记录日志
recordHistory(uid,cardNo,certNo,name,responseResult,ts);
JSONObject jo=new JSONObject();
jo.put("sucFlag",responseResult.isSucFlag());
jo.put("errInfo",responseResult.getErrInfo());
return jo;
}
/**
* 把失败写入redis,过期时间为30分钟
* 超过一定次数需要通知uic开启图像验证码
*/
public void recordFailTimesAndOpenGraphVerify(RealNameAuthorizeReqVO reqVO){
Long failedNum=cacheService.incrementAuthorizeFailNum(reqVO.getUid());
if(failedNum.intValue()>=getAuthorizeGraphVerifyLimit()){
graphVerifyService.triggerUfoGraphVerifySwitch(reqVO.getApp_type(),reqVO.getClient_type(), reqVO.getApp_version(), reqVO.getFromPage(),
reqVO.getUdid(), reqVO.getDegrees());
}
}
/**
* 获取图像验证码开启验证次数
*/
public int getAuthorizeGraphVerifyLimit() {
int time = configReader.getInt("ufo.passport.authorize.graph.verify.count", 3);
logger.info("RealNameAuthorizeServiceImpl getGraphVerifyLimit result is {}", time);
return time;
}
/**
* 组织银联实名认证的请求报文
*/
private JSONObject constructMsg(String cardNo, String certNo, String name){
JSONObject msgContentParams = new JSONObject();
msgContentParams.put("cardNo",cardNo);//卡号
msgContentParams.put("certNo",certNo);//证件号
msgContentParams.put("certType","01"); //证件类型:01居民身份证,只支持身份证
msgContentParams.put("name",name);//证件姓名
msgContentParams.put("personalMandate","1"); //个人是否授权,1表示授权,0表示未授权,只能是授权用户
return msgContentParams;
}
/**
* 请求银联接口,获取返回信息
* 捕获所有异常
*/
private PostBankResult postRequest(JSONObject msgContentParams,String authorizationContentByOpenBodySig){
PostBankResult result=new PostBankResult();
try{
//组成post的请求参数
JSONObject dataParams=new JSONObject();
dataParams.put("data",msgContentParams);
//header
MultiValueMap<String, String> headers = new LinkedMultiValueMap();
headers.set("Content-Type", "application/json; charset=UTF-8");
headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
headers.set("Authorization",authorizationContentByOpenBodySig);
//body
HttpEntity<JSONObject> bodyEntity = new HttpEntity<>(dataParams, headers);
logger.info("RealNameAuthorizeServiceImpl authorizeRealNameWithBank request message {} ,headers {} ,body {}",msgContentParams,headers,bodyEntity);
//post
ResponseEntity<JSONObject> responseEntity = restTemplate.postForEntity(requestUrl, bodyEntity, JSONObject.class);
logger.info("RealNameAuthorizeServiceImpl authorizeRealNameWithBank response entity {} ",responseEntity);
//处理结果
JSONObject jo=responseEntity.getBody();
if(jo==null){
result.setSucFlag(false);
result.setBackMsg("请求银联返回内容为空");
return result;
}
result.setBackMsg(jo.toString());
result.setStatusCode(responseEntity.getStatusCodeValue());
//实名认证成功的应答码:"0000"
if(responseEntity.getStatusCodeValue()==200&&jo!=null&&"0000".equals(jo.getString("errCode"))){
result.setSucFlag(true);
result.setErrCode("0000");
result.setErrInfo("请求银联实名认证成功");
}else{
result.setSucFlag(false);
result.setErrCode(jo==null?"":jo.getString("errCode"));
result.setErrInfo(jo==null?"":jo.getString("errInfo"));
}
}catch (Exception e){
logger.error("RealNameAuthorizeServiceImpl authorizeRealNameWithBank response msgContentParams {} ,error",msgContentParams,e);
//记录错误码
result.setSucFlag(false);
if(e instanceof HttpClientErrorException){
HttpClientErrorException errorException=(HttpClientErrorException)e;
result.setStatusCode(errorException.getStatusCode().value());
result.setBackMsg(errorException.getResponseBodyAsString());
try{
JSONObject jo=JSONObject.fromObject(result.getBackMsg());
result.setErrCode(jo==null?"":jo.getString("errCode"));
result.setErrInfo(jo==null?"":jo.getString("errInfo"));
}catch (Exception jError){
logger.error("change to json error {} ",result.getBackMsg(),jError);
}
}else if(e instanceof HttpServerErrorException){
HttpServerErrorException errorException=(HttpServerErrorException)e;
result.setStatusCode(errorException.getStatusCode().value());
result.setBackMsg(errorException.getResponseBodyAsString());
try{
JSONObject jo=JSONObject.fromObject(errorException.getResponseBodyAsString());
result.setErrCode(jo==null?"":jo.getString("errCode"));
result.setErrInfo(jo==null?"":jo.getString("errInfo"));
}catch (Exception jError){
logger.error("change to json error {} ",result.getBackMsg(),jError);
}
}else if(e instanceof RestClientException){
RestClientException errorException = (RestClientException)e;
result.setBackMsg(errorException.getMessage());
}else{
result.setBackMsg("error happen unknown reason");
}
}
return result;
}
@Data
private static class PostBankResult {
private boolean sucFlag;
private int statusCode;
private String errCode;
private String errInfo;
private String backMsg;
}
/**
* 无论成功还是失败,都把访问记录日志表
*/
private void recordHistory(int uid,String cardNo,String certNo,String name,PostBankResult responseResult ,long ts){
UserAuthorizeHistory history=new UserAuthorizeHistory();
history.setUid(uid);
history.setCardNo(cardNo);
history.setCertNo(certNo);
history.setCertName(name);
history.setResponseCode(responseResult.getStatusCode());
history.setResponseContent(responseResult.getBackMsg());
history.setBackErrorCode(responseResult.getErrCode());
history.setBackErrorInfo(responseResult.getErrInfo());
history.setFirstErrorCode(EnumBankBackCode.getFirstCodeBySecondCode(responseResult.getErrCode()));
history.setCreateTime(ts);
history.setUpdateTime(ts);
//最后记录日志 ,异步
executeService.execute(()->{
try{
userAuthorizeHistoryDao.insert(history);
}catch (Exception e){
logger.error("RealNameAuthorizeServiceImpl authorizeRealNameWithBank userAuthorizeHistoryDao insert history {} error {}",history,e);
}
});
}
/**
* 生成授权内容
* 内容生成算法请参考接口文档
* @param msgContentParams 请求报文
*/
public String generateAuthorizationByOpenBodySig(JSONObject msgContentParams){
//产品ID,由商务提供
String appId="2c909a515499b76b01549a01d2730000";
String appKey="keyqinchao";
//当前时间戳 ,要求格式 yyyyMMddHHmmss
String timestamp=getLocalDateTime().format(df);
//随机的正整数,不超过128位
int nonce = (new Random()).nextInt(100000);
//timestamp="20180911154812";
//nonce=26713;
/******************** 根据报文体,生成签名 **********/
//1.报文内容转字节数组 ,sha256加密转16进制后再转小写
String sha256_hex_lower= DigestUtils.sha256Hex(msgContentParams.toString()).toLowerCase();
//2.生成代签名字符串
String prepare_sig_str=appId+timestamp+nonce+sha256_hex_lower;
//3.HmacSHA256签名 ,并base64编码
String signature= Base64.encodeBase64String(HmacUtils.hmacSha256(appKey, prepare_sig_str));
/****************************************************/
//得到授权内容
StringBuilder sb=new StringBuilder("OPEN-BODY-SIG ");
sb.append("AppId=\"").append(appId).append("\", ");
sb.append("Timestamp=\"").append(timestamp).append("\", ");
sb.append("Nonce=\"").append(nonce).append("\", ");
sb.append("Signature=\"").append(signature).append("\"");
return sb.toString();
}
private LocalDateTime getLocalDateTime(){
LocalDateTime now=LocalDateTime.now();
return now;
}
}