One . background
1. Docking with the front end API Interface , If it is captured by a third party and maliciously tampers with parameters , It could lead to data leakage , It can even be tampered with
2. Interface with third-party companies , If the third party gets your interface document , But the interface does not have security verification , Is very unsafe
I'm mainly around timestamps ,token, The signature has three parts to guarantee API Interface security
Two . Request process
1. After the user successfully logs in to the site , The server will return a token, This parameter must be included in any operation of the user , You can put this parameter directly into header in .
2. The client uses the parameters and token Generate a signature sign, Send it to the server as a parameter , The server is using the same method to generate sign Check for tampering .
3. But there are still problems , May be malicious unrestricted access , At this point, we need to introduce a timestamp parameter , If the timeout is invalid .
4. The server needs to token, Signature , Time stamp for verification , Only token It works , The timestamp did not time out , The signature is valid to be released .
Concept :
(1) Open interface
There are no restrictions on , Simple and crude access , This kind of interface is generally used in open application platform , Check the weather , Check express , As long as you input the correct parameters, call , You can get the information you need , We can change the parameter value at will .
(2)Token Certification acquisition
After the user logs in successfully , Will get a ticket value , This parameter is required for any subsequent interface access . We put it in redis Inside , Valid for 10 minute , stay ticket About to time out , Life without perception . Extend the use time , If the user doesn't do anything in a period of time , You need to log back into the system .
(3)Sign Signature
Put all the parameters together , After adding the system secret key , Conduct MD5 The calculation generates a sign Signature , Prevent parameters from being tampered maliciously , The background generates the secret key in the same way , Make a signature comparison .
(4) Repeat the visit
Introduce a timestamp parameter , Ensure that the interface is only valid for one minute , Need to be consistent with client time .
(5) Interceptor
Each request comes with these three parameters , We all need to verify , Only when all three parameters meet our requirements , To allow data to be returned or manipulated .
3、 ... and . Specific code implementation
1. Write to get tiket The interface of
/** * obtain tiket * @param receiveRequest * @return */ @ResponseBody @RequestMapping(value = "/gettiket",method = RequestMethod.POST) public String gettiket(@RequestBody String data){ String result = ""; String msg = ""; try{ log.info("gettiket, The parameter for ==="+data); JdbcTemplate jdbcTemplate = new JdbcTemplate(); String userTocken = UUID.randomUUID().toString(); //cache.put(userTocken, userMap);// Database mode or redis The way , Here we use database String insert_user_token_sql = "insert into user_token(pk_user_token,userid,user_token) VALUES (?,?,?)"; long pk_user_token = KeyUtils.nextId();// Primary key jdbcTemplate.executeUpdate(insert_user_token_sql, new Object[]{ pk_user_token,"111",userTocken }); result = userTocken; msg = "{\"success\" : true,\"errorCode\" : \"200\", \"errorMsg\" : \" Query complete \", \"tiket\" :" +result + "}"; log.info("msg===="+msg); return msg; }catch(Exception e){ msg = "{\"success\" : true,\"errorCode\" : \"500\", \"errorMsg\" : \" Query complete \", \"data\" :" +e + "}"; return msg; } }
2. Server side validation
Main program entry
Map<String, String> paramMap = new HashMap<>(); String time = DateUtils.formatDate("yyyy-MM-dd HH:mm:ss.SSS"); paramMap.put("time", time); String ticket = "056a3d29-eed3-4ee9-80aa-c03321d5302f"; paramMap.put("ticket", ticket);//userTock For the first time I ask for your single point url From time to time userTocken String serviceCode = "cs_demo";// The key corresponding to the target system String sign = null; try { sign = SignUtils.sing(paramMap, serviceCode, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } log.info("sign==="+sign); CheckPerService checkPerService= new CheckPerService(); Boolean istrue = checkPerService.TicketSignAndTime( ticket, sign, time, serviceCode); log.info("istrue==="+istrue);
Tool class SignUtils
package tcc.test.utill; import org.apache.log4j.Logger; import org.springframework.util.DigestUtils; import java.io.UnsupportedEncodingException; import java.util.*; public class SignUtils { private static final Logger a = Logger.getRootLogger(); public SignUtils() { } public static String getContent(Map params) { List keys = new ArrayList(params.keySet()); Collections.sort(keys); String prestr = ""; boolean first = true; for(int i = 0; i < keys.size(); ++i) { String key = (String)keys.get(i); if (!"sign".equals(key) && !"_r".equals(key) && !"_result_type".equals(key) && !"_".equals(key)) { String value = String.valueOf(params.get(key)); if (value != null && value.trim().length() != 0) { if (first) { prestr = prestr + key + "=" + value; first = false; } else { prestr = prestr + "&" + key + "=" + value; } } } } a.info(" Encrypted string :" + prestr); return prestr; } public static String sing(Map Params, String key, String charset) throws UnsupportedEncodingException { String signStr = null; signStr = DigestUtils.md5DigestAsHex((getContent(Params) + key).getBytes(charset)); return signStr; } public static void main(String[] args) throws Exception { Map paramMap = new HashMap<String,String>(); paramMap.put("name","tcc"); paramMap.put("age","24"); String serviceCode = "siruinet"; String sing = SignUtils.sing(paramMap, serviceCode, "UTF-8"); System.out.println(sing); } }
Permission verification tool class
package tcc.test.utill; import com.alibaba.druid.util.StringUtils; import com.util.FieldList; import jos.engine.core.jdbc.JdbcTemplate; import jos.engine.des.util.DesEncryptUtils; import org.apache.log4j.Logger; import java.util.HashMap; import java.util.Map; /** * Copyright (C) @2022 * * @author: tcc * @version: 1.0 * @date: 2022/1/31 * @time: 2:08 * @description: */ public class CheckPerService{ private static final Logger log = Logger.getRootLogger(); /* Interface permission verification method 1 ticket: Notes sign: Signature time: Time stamp serviceCode: Service code */ public static boolean TicketSignAndTime(String ticket, String sign, String time, String serviceCode){ time = time; ticket = ticket; sign = sign; Map<String, String> paramMap = new HashMap<>(); paramMap.put("time", time); paramMap.put("ticket", ticket);//ticket Get for the first call ticket Interface data serviceCode = serviceCode;// The key corresponding to the target system String qm = DesEncryptUtils.sing(paramMap, serviceCode, "UTF-8"); log.info("qm==="+qm); if (!StringUtils.equals(sign, qm)) { // Key verification error log.info(" Incorrect signature "); return false; } log.info(" The signature is correct "); JdbcTemplate jdbcTemplate = new JdbcTemplate(); String qr_user_token_sql = "select count(1) as count from user_token where user_token = ?";// Later it will be changed to redis FieldList file_token = jdbcTemplate.queryField(qr_user_token_sql, new Object[]{ ticket }); int count = Integer.parseInt(file_token.get("count")); if(count<1){ return false; } return true; } /* Interface permission verification method 2 name: user name pwd: password */ public static boolean UnmAndPwd(String name,String pwd){ JdbcTemplate jdbcTemplate = new JdbcTemplate("mzdb"); String qr_user_token_sql = "select count(1) as count from bd_user where USERNAME = ? and USERPASS = ?";// Later it will be changed to redis FieldList file_token = jdbcTemplate.queryField(qr_user_token_sql, new Object[]{ name,pwd }); int count = Integer.parseInt(file_token.get("count")); if(count<1){ return false; } return true; } }