Overview
If the API interface docked with the front-end is captured by a third party and maliciously tampered with the parameters, it may cause data leakage or even tamper with the data. I mainly focus on the three parts of timestamp, token, and signature to ensure the security of the API interface. 1. After the user successfully logs in to the site, the server will return a token, and any operation of the user must carry this parameter, which can be directly placed in the header. 2. The client uses the parameters to be sent and the token to generate a signature sign, which is sent to the server as a parameter, and the server uses the same method to generate the sign to check whether it has been tampered with. 3. But there is still a problem, which may be malicious and unrestricted access. At this time, we need to introduce a timestamp parameter, which is invalid if it times out. 4. The server needs to verify the token, signature, and timestamp. Only when the token is valid and the timestamp has not expired can the signature be valid.
Open interface
There are no restrictions, simple and rude access methods. Such interface methods are generally in the open application platform, check the weather, check the express delivery, as long as you enter the correct corresponding parameter call, you can get the information you need, and we can modify it at will The parameter value.
@RestController
@RequestMapping("/token")
public class TokenSignController {
@Autowired
private TokenSignService tokenSignService;
@RequestMapping(value = "openDemo",method = RequestMethod.GET)
public List<PersonEntity> openDemo(int personId){
return tokenSignService.getPersonList(personId);
}
}
Token authentication acquisition After the user logs in successfully, a ticket value will be obtained, which is required for any subsequent interface access. We put it in redis, the validity period is 10 minutes, when the ticket is about to expire, it will continue to live without perception. Extend the usage time. If the user does not perform any operation within a period of time, he needs to log in to the system again. Extension: Remember the practice of token security certification
@RequestMapping(value = "login",method = RequestMethod.POST)
public JSONObject login(@NotNull String username, @NotNull String password){
return tokenSignService.login(username,password);
}
Login operation, check whether there is this user, the user name and password match to log in successfully.
public JSONObject login(String username,String password){
JSONObject result = new JSONObject();
PersonEntity personEntity = personDao.findByLoginName(username);
if (personEntity == null || (personEntity != null && !personEntity.getPassword().equals(password))){
result.put("success",false);
result.put("ticket","");
result.put("code","999");
result.put("message","Username and password do not match");
return result;
}
if (personEntity.getLoginName().equals(username) && personEntity.getPassword().equals(password)){
String ticket = UUID.randomUUID().toString();
ticket = ticket.replace("-","");
redisTemplate.opsForValue().set(ticket,personEntity.getLoginName(),10L, TimeUnit.MINUTES);
result.put("success",true);
result.put("ticket",ticket);
result.put("code",200);
result.put("message","login successful");
return result;
}
result.put("success",false);
result.put("ticket","");
result.put("code","1000");
result.put("message","Unknown exception, please try again");
return result;
}
Sign
/**
* @param request
* @return
*/
public static Boolean checkSign(HttpServletRequest request,String sign){
Boolean flag= false;
//Check if the sigin is expired
Enumeration<?> pNames = request.getParameterNames();
Map<String, String> params = new HashMap<String, String>();
while (pNames.hasMoreElements()) {
String pName = (String) pNames.nextElement();
if("sign".equals(pName)) continue;
String pValue = (String)request.getParameter(pName);
params.put(pName, pValue);
}
System.out.println("The present sign-->>" + sign);
System.out.println("Verified sign-->>" + getSign(params,secretKeyOfWxh));
if(sign.equals(getSign(params, secretKeyOfWxh))){
flag = true;
}
return flag;
}
public static long getTimestamp(){
long timestampLong = System.currentTimeMillis();
long timestampsStr = timestampLong / 1000;
return timestampsStr;
}
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
private RedisTemplate redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws IOException {
JSONObject jsonObject = new JSONObject();
String ticket = request.getParameter("ticket");
String sign = request.getParameter("sign");
String ts = request.getParameter("ts");
if (StringUtils.isEmpty(ticket) || StringUtils.isEmpty(sign) || StringUtils.isEmpty(ts)){
jsonObject.put("success",false);
jsonObject.put("message","args is isEmpty");
jsonObject.put("code","1001");
PrintWriter printWriter = response.getWriter();
printWriter.write(jsonObject.toJSONString());
return false;
}
//If redis has a ticket, it is considered a legitimate request
if (redisTemplate.hasKey(ticket)){
System.out.println(redisTemplate.opsForValue().getOperations().getExpire(ticket));
String values = (String) redisTemplate.opsForValue().get(ticket);
//Determine whether the ticket is about to expire, and continue the operation
if (redisTemplate.opsForValue().getOperations().getExpire(ticket) != -2 && redisTemplate.opsForValue().getOperations().getExpire(ticket) < 20){
redisTemplate.opsForValue().set(ticket,values,10L, TimeUnit.MINUTES);
}
System.out.println(SignUtils.getTimestamp());
//Judging whether repeated access, there is a time window period for replay attacks
if (SignUtils.getTimestamp() - Long.valueOf(ts) > 600){
jsonObject.put("success",false);
jsonObject.put("message","Overtime to connect to server");
jsonObject.put("code","1002");
PrintWriter printWriter = response.getWriter();
printWriter.write(jsonObject.toJSONString());
return false;
}
//Verify the signature
if (!SignUtils.checkSign(request,sign)){
jsonObject.put("success",false);
jsonObject.put("message","sign is invalid");
jsonObject.put("code","1003");
PrintWriter printWriter = response.getWriter();
printWriter.write(jsonObject.toJSONString());
return false;
}
return true;
}else {
jsonObject.put("success",false);
jsonObject.put("message","ticket is invalid,Relogin.");
jsonObject.put("code","1004");
PrintWriter printWriter = response.getWriter();
printWriter.write(jsonObject.toJSONString());
}
return false;
}
}