Recently, due to the needs of the project, it is necessary to realize the function of wechat code scanning in the project. Wechat code scanning login is a very common technology now. Code scanning is used in many places, which is not only convenient and fast, but also relatively safe. After referring to some big guys' articles, I share my understanding with you. You are welcome to correct the shortcomings. Thank you.
preparation
1. Using wechat login is to register a developer account on the wechat open platform and have it verified by the bank. After passing the verification, you need to create a website application and a domain name as a callback for wechat. After creating the website application, you will get an appid and appsecret, configure the callback domain, and end some pre requirements.
Wechat development platform link https://open.weixin.qq.com/
2. Understand the wechat login process and related technologies
These are introduced in the development documents. Here I will tell you about my own understanding of the wechat login process
First, the official gives the basic flow chart, but the details are not detailed. The following figure is the official flow chart
1. Get code
First, the user needs to initiate a wechat code scanning login request in the third-party application, and then the third-party application sends a request to the wechat open platform through OAuth2.0 to pull up the code scanning login request. The wechat development platform will return a code scanning interface to the third-party application through the callback field of the network application, and the user will scan the code to confirm login, The wechat development platform will pull up or redirect to a third party and carry a temporary bill (code)
2. Get the token (access) through code_ token)
After the third-party application obtains the code returned by the wechat development platform, it obtains the token from the wechat open platform according to the connection provided by it
After obtaining the code of the first step, request the following link to obtain access_token:
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
Because appid and other parameters are very verbose, set them as constants to avoid errors
/** * Get token constant class * @author */ public class WxConstants { public static final String APPID = "Own website application APPID "; public static final String SECRET = "Own website application SECRET "; /** * Get token token through code appid secret */ public static final String GET_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code"; /** * Get user information */ public static final String GET_USER_URL = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID"; }
Return Description:
{ "access_token": "ACCESS_TOKEN", "expires_in": 7200, "refresh_token": "REFRESH_TOKEN", "openid": "OPENID", "scope": "SCOPE", "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL" } parameter explain access_token Interface call voucher expires_in access_token Timeout time of interface call voucher, unit (s) refresh_token User refresh access_token openid Unique ID of authorized user scope Scope of user authorization, using commas(,)separate
Refresh access_ Valid period of token
access_token is the calling voucher for calling the authorization relationship interface_ The validity period of the token (currently 2 hours) is short, when access_ After the token times out, you can use refresh_ Refresh with token, access_ There are two types of token refresh results:
1. if access_token Timeout, proceed refresh_token Will get a new access_token,New timeout; 2. if access_token If not, proceed refresh_token Will not change access_token,But the timeout will refresh, which is equivalent to renewal access_token.
refresh_token has a long validity period (30 days), when refresh_ After the token becomes invalid, the user needs to re authorize.
Request method
After obtaining the code of the first step, request the following link to refresh_token:
https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
Parameter description parameter Is it necessary explain appid yes Apply unique identification grant_type yes fill refresh_token refresh_token yes Fill in through access_token Obtained refresh_token parameter
3. Realize wechat code scanning login
Get access_ After token, we can get the relevant information of the logged in user and operate. Getting the user data is the beginning of this function. How to realize the user code scanning login requires us to complete the data operation ourselves.
In this project, a table is used to store all the data of user code scanning login, and some tables are associated with it, such as user information table, user login table and other tables that need this data.
After the user scans the code and obtains the user data, the bank judges whether it is the first login or an existing account. If it is an existing account, it only needs to associate the relevant information of wechat login with its corresponding user information. If it is the first login, it needs to register an account in the system to store the user's information.
/** * Wechat login * @param map * @return */ @Override public Ajaxresult wechat(Map<String, String> map) { String code=map.get("code"); System.out.println(code); if (StringUtils.isEmpty(code)){ throw new MyExpection("System error"); } //Get token token through code appid secret String url= WxConstants.GET_TOKEN_URL.replace("APPID", WxConstants.APPID) .replace("SECRET", WxConstants.SECRET) .replace("CODE",code); //Send request and get token String obj = HttpClientUtils.httpGet(url); System.out.println(obj); // Convert json string to json object JSONObject jsonObject = JSONObject.parseObject(obj); //Get token and openid String token = jsonObject.getString("access_token"); String openid = jsonObject.getString("openid"); System.out.println(openid+"=========================================="); //Query the object through openid and userid. If the object is not empty, it indicates that it has been logged in. If the object is empty, it indicates that it has not been logged in WxUser wxUser = wxUserMapper.findByOpenId(openid); // Get user information at the first scan String param = "?token="+token+"&openid="+openid; //Judge whether the object exists and userId exists if (wxUser !=null && wxUser.getUser_id()!=null){ //Get user information LoginInfo loginInfo=loginInfoMapper.findByUserId(wxUser.getUser_id()); // 4,1 get token String userToken = UUID.randomUUID().toString(); // Store the user information in redis, and the expiration time is 30 minutes redisTemplate.opsForValue().set(userToken, loginInfo, 30, TimeUnit.MINUTES); Map<String,Object> wxMap=new HashMap<>(); wxMap.put("token", userToken); // Set the user name and password to null loginInfo.setPassword(""); loginInfo.setSalt(""); // Object to be serialized wxMap.put("loginInfo",loginInfo); return Ajaxresult.me().setResultObj(wxMap); } return Ajaxresult.me().setSuccess().setResultObj(param); } /** * Wechat registration binding * @param map * @return */ @Override public Map<String, Object> binder(Map<String, String> map) { String phone = map.get("phone"); String verifyCode = map.get("verifyCode"); String accessToken = map.get("accessToken"); String openId = map.get("openId"); //1. Null verification if(StringUtils.isEmpty(phone)){ throw new MyExpection("The phone number cannot be blank. Please enter the phone number"); } //2. Verification code verification //Get verification code Object obj = redisTemplate.opsForValue().get("binder:"+phone); String time = obj.toString().split(":")[1]; //1. Is the verification code expired if(System.currentTimeMillis() - Long.valueOf(time)>180*1000){ throw new MyExpection("The verification code has expired. Please obtain the verification code again"); }else { // 2. Judge whether the verification codes are consistent //Get verification code String code = obj.toString().split(":")[0]; if (! verifyCode.equals(code)){ throw new MyExpection("Verification code error, please re-enter"); } } //3. Judge whether the mobile phone number has been registered User user = userMapper.findByPhone(phone); LoginInfo loginInfo=null; User userTemp=null; //Registered if (user!=null){ loginInfo=loginInfoMapper.findByUserId(userTemp.getId()); System.out.println(loginInfo); userTemp=user; }else { //Not registered, save three tables loginInfo user wxUser userTemp=initUser(phone); loginInfo=initLoginInfo(userTemp); //Store the data in the corresponding table loginInfo.setType(1); loginInfoMapper.add(loginInfo); //Store information in the user table userTemp.setLogininfo(loginInfo); userMapper.add(userTemp); } //Save information to wxUser String replace = HttpClientUtils.httpGet(WxConstants.GET_USER_URL .replace("ACCESS_TOKEN", accessToken) .replace("OPENID", openId)); // System.out.println(obj+"======================================="); //Add information to wxUser WxUser wxUser=initWxuser(replace); //binding wxUser.setUser_id(userTemp.getId()); //Save to table wxUserMapper.add(wxUser); //Save data to redis String userToken = UUID.randomUUID().toString(); // Store the user information in redis, and the expiration time is 30 minutes redisTemplate.opsForValue().set(userToken, loginInfo, 30, TimeUnit.MINUTES); Map<String,Object> binderMap=new HashMap<>(); binderMap.put("token", userToken); // Set the user name and password to null loginInfo.setPassword(""); loginInfo.setSalt(""); // Object to be serialized binderMap.put("loginInfo",loginInfo); return binderMap; }