前言:
最近在研究一個功能,在系統中綁定用戶微信號,綁定好用戶賬號後,用戶就可以直接掃碼進入系統。講到這裏先來看一個下面這個完整的項目例子。
例子:
在系統當中的業務邏輯是這樣的。
- 第一步,用戶點擊綁定微信,生成一個二維碼。
- 第二步,用戶掃完二維碼後出現關注公衆號的頁面。
- 第三步,用戶關注該公衆號出現微信掃碼的界面
- 第四步,用戶掃完這個微信後,彈出獲取微信用戶信息的框。用戶同意以後即可綁定成功,下次就可用微信掃碼登錄系統。
- 第五步,綁定成功。
業務介紹:
上面是一個完整的項目例子,但是我這邊博客我會將這個功能拆分開。
上面這個例子可以這麼來做。
第一種情況 ,
用戶有系統的賬號和密碼 ,在個人設置中有一個綁定微信賬號功能。
1)
第一步,用戶用手機掃描這個公衆號的二維碼,要用戶關注公衆號,
2)
如果需要強制用戶關注公衆號的話,我們可以配置微信開發者設置,做接收微信事件功能,這樣用戶是否關注了該公衆號一目瞭然,並且在這個接收微信事件功能接口裏面,我們可以獲取到關注的當前用戶的openID , 根據openid 可以獲取微信用戶信息,在這之前我寫過一個獲取微信用戶列表的功能,當後臺獲取到掃碼的openId 以後 ,我就從數據庫中獲取當前用戶信息,將當前用戶信息保存到redis中,通過接口將這個redis中的數據返回給前端,如果數據庫中沒有當前用戶信息,我就將數據保到數據庫中。這樣前端就能判斷用戶能否進入下一步操作。
3)
掃描完公衆號的二維碼以後,我們可以生成一個帶參數的二維碼,指引用戶授權,獲取當前用戶的信息 一般情況下可以返回微信用戶暱稱等等,這個時候我們是在系統內部掃碼的。我們可以將暱稱和openId 跟當前用戶關聯起來,這樣在登錄界面的時候,用戶可以憑藉 微信掃碼登錄到系統中。
在這裏有一點需要注意的是,剛開始我是將用戶信息保存到session中的,在接收事件方法中我能夠獲取到保存到Session中的值,但是我新寫一個接口的時候,發現session中的值爲Null ,後面才知道這是倆個不同的客戶端,用戶關注公衆號是手機端,而我在瀏覽器上訪問這個用戶信息的時候,是瀏覽器端,倆個sessionId 都不一樣,自然數據就獲取不到。
第二種情況,
用戶沒有系統的賬號和密碼,那麼是在登錄界面有一個微信掃碼的功能。
當用戶掃描這個二維碼以後,我們就需要根據當前用戶的openId 查詢一下用戶信息,提示他是否需要綁定已有賬號 ,或者重新註冊一個新賬號,已新賬號身份登錄,並且將用戶信息和微信用戶信息綁定起來。
1.這篇文章先給大家演示一下如何生成一個公衆號的二維碼。
開發步驟:
步驟1:
首先我們需要生成一個帶參數的二維碼,關於二維碼有2種,一種是臨時二維碼,一種是永久二維碼。
- 1.臨時二維碼:有過期時間,最長可以設置爲在二維碼生成的30天(即2592000秒)後過期,生成較多數量。臨時二維碼主要用於帳 號綁定等不要求二維碼永久保存的業務場景。
- 2.永久二維碼:是無過期時間的,但數量較少(目前爲最多10萬個)。永久二維碼主要用於適用於帳號綁定、用戶來源統計等場景。
獲取帶參數的二維碼的過程包括兩步:
- 1.首先創建二維碼ticket。
- 2.然後憑藉ticket到指定URL換取二維碼。
步驟2:
1. 首先創建二維碼Ticket 以下是請求地址和參數。
2.通過Ticket換取二維碼,以下是請求地址和請求參數內容
WechatController
@ApiOperation(value = "1.8 綁定微信賬號")
@GetMapping(value = "/bindWechatAccount")
public ResultData bindWechatAccount() {
return weiService.bindWechatAccount();
}
WeiService
@Override
public ResultData bindWechatAccount() {
ResultData resultData = new ResultData();
try {
//第一步:向微信公衆平臺發送請求,獲取Ticket
String accessToken = WeiXinParamesUtil.getAccessToken2("weixin");
String createQrCodeUrl = WeiXinParamesUtil.createQrCodeUrl;
createQrCodeUrl = createQrCodeUrl.replace("TOKEN", accessToken);
//傳入參數。
String postParam = "{\"action_name\": \"QR_LIMIT_SCENE\", \"action_info\": {\"scene\": {\"scene_id\": 123}}}";
//發送請求 獲取Ticket
JSONObject jsonObject = SendRequest.sendPost(createQrCodeUrl, postParam);
System.out.println("111----" + jsonObject);
String ticket = jsonObject.getString("ticket");
// url : 二維碼圖片解析後的地址,開發者可根據該地址自行生成需要的二維碼圖片
String url = jsonObject.getString("url");
//第二步: 通過Ticket換取二維碼。
String getWehatCodeUrl = WeiXinParamesUtil.getWeiQrCodeUrl;
getWehatCodeUrl = getWehatCodeUrl.replace("TICKET", ticket);
resultData.setResult("true");
resultData.setMessage("返回微信公衆號二維碼成功");
//返回生成的二維碼圖片
System.out.println("111----"+getWehatCodeUrl);
resultData.setStr(getWehatCodeUrl);
} catch (Exception e) {
resultData.setResult("false");
resultData.setMessage("返回微信公衆號二維碼失敗");
logger.error("返回微信公衆號二維碼失敗", e);
}
return resultData;
}
WeiXinParamesUtil
當前這個類屬於微信公衆平臺的參數配置類,其中有很多參數是我當前這篇文章不需要的,可以自行刪除。
package com.bos.util;
import com.alibaba.fastjson.JSONObject;
import com.bos.common.CommenUtil;
import com.bos.data.model.WeiUserInfoModel;
import com.bos.qiWechat.AesException;
import com.bos.qiWechat.WXBizMsgCrypt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* 微信公衆平臺的參數
* @param
* @return
*/
public class WeiXinParamesUtil {
private Logger logger = LoggerFactory.getLogger(WeiXinParamesUtil.class);
/**
* 獲取微信公衆平臺accessToken
* @param
* @return
*/
public static String getWeiAccessToken ="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
public static String getUserInfoList = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
public static String getUserList = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID";
public static String updateUserRemark = "https://api.weixin.qq.com/cgi-bin/user/info/updateremark?access_token=ACCESS_TOKEN";
public static String open_access_token_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={appid}&secret={secret}&code={code}&grant_type=authorization_code";
/**
* 客戶接口發送消息
* @param
* @return
*/
public static String sendCustomerMsgUrl="https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN";
/**
* 上傳臨時文件素材到微信服務器上
* @param
* @return
*/
public static String uploadMaterialToWeiUrl="https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE";
/**
* 從微信服務器上下載臨時文件素材
* @param
* @return
*/
public static String downloadMaterialFromWeiUrl="https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID";
/**
* 獲取access_token的接口地址(GET) 限200(次/天)
* @param
* @return
*/
public static String access_token_url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={corpId}&corpsecret={corpsecret}";
public static String open_id = "gh_b920df4d06d0";
/**
* 微信掃碼之後獲取用戶基本信息的地址
* @param
* @return
*/
public static String getuserinfo_url = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";
/**
* 發送消息給微信公衆平臺的url
* @param
* @return
*/
public static String sendMessageToWei_url="https://api.weixin.qq.com/cgi-bin/message/send?access_token=ACCESS_TOKEN";
/**
* 獲取微信公衆平臺Ticket
*/
public static String createQrCodeUrl="https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN";
/**
* 生成推廣的二維碼
*/
public static String getWeiQrCodeUrl="https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET";
/**
* 生成小程序的二維碼
*/
public static String CREATE_APPLET_CODE = "https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token=ACCESS_TOKEN";
/**
* 通過code 獲取 access_token
*/
public static String GET_ACCESSTOKEN = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={appid}&secret={secret}&code={code}&grant_type=authorization_code";
/**
* 檢驗AccessToken是否有效
*/
public static String CHECK_OUT_ACCESSTOKEN = "https://api.weixin.qq.com/sns/auth?access_token={access_token}&openid={open_id}";
/**
* 微信開放平臺獲取用戶信息
*/
public static String GET_SNS_USER_INFO = "https://api.weixin.qq.com/sns/userinfo?access_token={access_token}&openid={open_id}";
/**
* 刷新AccessToken
*/
public static String REFRESH_TOKEN = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid={appid}&grant_type={refresh_token}&refresh_token={refresh_token}";
//------------微信公衆平臺的參數
/**
* appId
* @param
* @return
*/
public static String APPID;
/**
* secret
* @param
* @return
*/
public static String SECRET;
//----------------微信開放平臺的參數
/**
* openAppid
* @param
* @return
*/
public static String OPENAPPID;
/**
* appSecret
* @param
* @return
*/
public static String OPENSECRET;
/**
* 回調地址 redirect_uri
* @param
* @return
*/
public static String REDIRECT_URI;
/**
* scope
* @param
* @return
*/
public static String SCOPE;
/**
* 模板id
* @param
* @return
*/
public static String TEMPLATEID;
public static String TOKEN="weixinEbo";
public static String ENCODEING_AES_KEY="MUPgLCxZND6ER0wON5FhdfGLtzzbOI7O5P23B2EWFrM";
/**
* 微信加密簽名
*/
private String msg_signature;
/**
* 時間戳
*/
private String timestamp;
/**
* 隨機數
*/
private String nonce;
static {
APPID = "替換成你的APPid";
SECRET = "替換成你的Secret";
SCOPE = "snsapi_userinfo";
REDIRECT_URI = "snsapi_userinfo";
TEMPLATEID = "********";
OPENAPPID="************";
OPENSECRET="**************";
}
}
/**
* 獲取微信公衆平臺的access_token
* @param type
* @return
*/
public static String getAccessToken2(String type) {
String url = "";
if ("weixin".equals(type)) {
url = getWeiAccessToken.replace("APPID", WeiXinParamesUtil.APPID).replace("APPSECRET", WeiXinParamesUtil.SECRET);
}
JSONObject departmentJson = SendRequest.sendGet2(url);
return departmentJson.getString("access_token");
}
}
SendRequest
// 發送post請求(返回json)
public static JSONObject sendPost(String url, String param) {
PrintWriter out = null;
BufferedReader in = null;
JSONObject jsonObject = null;
String result = "";
try {
URL realUrl = new URL(url);
// 打開和URL之間的連接
URLConnection conn = realUrl.openConnection();
// 發送POST請求必須設置如下兩行
conn.setDoOutput(true);
conn.setDoInput(true);
// conn.addRequestProperty("Cookie", "stay_login=1 smid=DumpWzWQSaLmKlFY1PgAtURdV_u3W3beoei96zsXkdSABwjVCRrnnNBsnH1wGWI0-VIflgvMaZAfli9H2NGtJg id=EtEWf1XZRLIwk1770NZN047804");//設置獲取的cookie
// 獲取URLConnection對象對應的輸出流(設置請求編碼爲UTF-8)
out = new PrintWriter(new OutputStreamWriter(conn.getOutputStream(), "UTF-8"));
// 發送請求參數
out.print(param);
// flush輸出流的緩衝
out.flush();
// 獲取請求返回數據(設置返回數據編碼爲UTF-8)
in = new BufferedReader(
new InputStreamReader(conn.getInputStream(), "UTF-8"));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
jsonObject = JSONObject.parseObject(result);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return jsonObject;
}
package com.bos.common;
import com.bos.data.model.TShowColumnModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.List;
import java.util.Map;
/**
* @author tanghh
*/
public class ResultData {
/**
* "true" or "false"
*/
@ApiModelProperty(value = "後臺返回的查詢結果,true=查詢成功,false=查詢失敗")
private String result;
/**
* 消息提示
*/
@ApiModelProperty(value = "後臺返回的消息,如'保存成功'。用作前端的提示消息")
private String message;
/**
* 消息提示
*/
@ApiModelProperty(value = "一個字符串")
private String str;
/**
* 存放一個實體
*/
private Object object;
/**
* 主數據集合
*/
private PageData pageData;
/**
*其他數據
*/
private List<Map> otherData;
/**
*整形
*/
private Integer number;
/**
*存放list集合的map
*/
private Map<String, List> mapList;
/**
*
*/
private Map<Object, Object> map;
/**
*存放一個list集合
*/
private List list;
/**
*專用於存放表頭字段的集合
*/
private List fieldList;
/**
*專用於存放視圖集合
*/
private List viewList;
/**
*專用於存放報表集合
*/
private List reportList;
/**
* 專用於存每個頁面的page
* @param
* @return
*/
private String page;
/**
* 專用於存不同頁面的module
* @param
* @return
*/
private String module;
/**
* 存儲當前數據的上一級數據(第一級)
* @param
* @return
*/
private String level1;
/**
* 存儲當前數據的上一級的上一級數據(第二級)
* @param
* @return
*/
private String level2;
/**
* 存儲當前數據的上一級的上一級的上一級數據(第三級)
* @param
* @return
*/
private String level3;
/**
* map2
*/
private Map<Object, Object> map2;
private Map<String,List<TShowColumnModel>> mapCloumnList;
public List getReportList() {
return reportList;
}
public void setReportList(List reportList) {
this.reportList = reportList;
}
public List getViewList() {
return viewList;
}
public void setViewList(List viewList) {
this.viewList = viewList;
}
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
public Map<String, List<TShowColumnModel>> getMapCloumnList() {
return mapCloumnList;
}
public void setMapCloumnList(Map<String, List<TShowColumnModel>> mapCloumnList) {
this.mapCloumnList = mapCloumnList;
}
public ResultData() {
this.message = "查詢成功";
this.result = "true";
}
/**
* 用於刪除成功
* @param count
*/
public ResultData(int count) {
if(count>0){
this.message = "刪除成功";
this.result = "true";
}else {
this.message = "刪除失敗";
this.result = "false";
}
}
public ResultData(Object object) {
this.object=object;
this.message = "查詢成功";
this.result = "true";
}
/**
* 用於添加刪除提示
* @param type
* @param flag
*/
public ResultData(String type,boolean flag) {
if(flag==false){
//保存和修改時候的提示
if(Constant.SAVE.equals(type)){
this.message = "保存失敗";
}else{
this.message = "刪除失敗";
}
this.result = "false";
}else {
if(Constant.SAVE.equals(type)){
this.message = "保存成功";
}else{
this.message = "刪除成功";
}
this.result = "true";
}
}
public ResultData(String result, String message, PageData pageData) {
this.result = result;
this.message = message;
this.pageData = pageData;
}
public ResultData(String result, String message, PageData pageData, List<Map> otherData) {
this.result = result;
this.message = message;
this.pageData = pageData;
this.otherData = otherData;
}
public ResultData(String result, String message, PageData pageData, List<Map> otherData, Map<String, List> mapList) {
this.result = result;
this.message = message;
this.pageData = pageData;
this.otherData = otherData;
this.mapList = mapList;
}
public ResultData(String result, String message) {
this.result = result;
this.message = message;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public PageData getPageData() {
return pageData;
}
public void setPageData(PageData pageData) {
this.pageData = pageData;
}
public List<Map> getOtherData() {
return otherData;
}
public void setOtherData(List<Map> otherData) {
this.otherData = otherData;
}
public Map<String, List> getMapList() {
return mapList;
}
public void setMapList(Map<String, List> mapList) {
this.mapList = mapList;
}
public Map<Object, Object> getMap() {
return map;
}
public void setMap(Map<Object, Object> map) {
this.map = map;
}
public List getFieldList() {
return fieldList;
}
public void setFieldList(List fieldList) {
this.fieldList = fieldList;
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public String getPage() {
return page;
}
public void setPage(String page) {
this.page = page;
}
public String getModule() {
return module;
}
public void setModule(String module) {
this.module = module;
}
public String getLevel1() {
return level1;
}
public void setLevel1(String level1) {
this.level1 = level1;
}
public String getLevel2() {
return level2;
}
public void setLevel2(String level2) {
this.level2 = level2;
}
public String getLevel3() {
return level3;
}
public void setLevel3(String level3) {
this.level3 = level3;
}
public Map<Object, Object> getMap2() {
return map2;
}
public void setMap2(Map<Object, Object> map2) {
this.map2 = map2;
}
}
ResultData
步驟3:
訪問這個接口,http://localhost:8088/bindWechatAccount
getWehatCodeUrl 爲一個url ,這個url可以直接在網頁上展示也可以下載下來。我這裏的話先在網頁上訪問一下。
將上面這個url 複製到 瀏覽器上,掃描這個二維碼。
步驟4:
掃描上圖中的公衆號二維碼 關注公衆號。
到此,本篇文章就寫完啦 ,如果覺得小編寫的不錯的話,不妨給小編一個贊吧,如果有什麼問題的話,歡迎評論區留言。一起加油。