本文轉載自:https://blog.csdn.net/qq_34190023/article/details/81185143
微信開放平台第三方授權登陸開發文檔(PC網頁端)
當微信開放平台開發第三方授權登陸(一):開發前期准備完成后,已經獲取到應用的AppID和AppSecret、且已經成功申請到微信登陸功能。可以進行第三方登陸授權開發。
網站應用微信登錄是基於OAuth2.0協議標准構建的微信OAuth2.0授權登錄系統。
一、需求
根據需求,需要擁有第三方微信登錄功能,並獲取到用戶信息。
二、開發流程
1.網站應用:(微信客戶端掃碼授權登陸)
1)第三方發起微信授權登錄請求,微信用戶允許授權第三方應用后,微信會拉起應用或重定向到第三方網站,並且帶上授權臨時票據code參數;
2)通過code參數加上AppID和AppSecret等,通過API換取access_token;
3)通過access_token進行接口調用,獲取用戶基本數據資源或幫助用戶實現基本操作。
網站應用第三方授權登陸獲取用戶信息
三、開發使用的技術及工具
1、使用IDEA2017.2進行開發
2、使用SpringBoot進行快速開發
3、使用redis進行緩存。
4、使用fastJson對json數據進行處理
5、使用Maven對項目進行管理
四、具體實現步驟
1、網站應用
創建工程
打開IDEA,新建一個工程,選擇Spring Initializr,Next。然后填寫工程基本信息
選擇SpringBoot的版本已經需要添加的依賴,也可以直接跳過,手動添加依賴。由於是web工程,需要添加web相關依賴。模板引擎采用springboot推薦的thymeleaf。最后點擊確認,進入工程。
添加相關依賴:
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.5</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.4</version> </dependency> <!-- 添加httpclient支持 --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.2</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.8.1</version> <type>jar</type> </dependency>
添加配置文件
SpringBoot默認會將resources下的application名字開頭的properties作為配置文件。所以只需要添加application.properties就可以了
由於是使用thymeleaf作為模板引擎。可以添加相應的配置:
#thymelea模板配置 spring.thymeleaf.prefix=classpath:/templates/ spring.thymeleaf.suffix=.html spring.thymeleaf.mode=HTML5 spring.thymeleaf.encoding=UTF-8 spring.thymeleaf.content-type=text/html spring.thymeleaf.cache=false spring.resources.chain.strategy.content.enabled=true spring.resources.chain.strategy.content.paths=/**
為了讓系統更具有通用性,分別創建application-dev.properties、application-prod.properties對不同的環境進行配置。
在application.properties中,使用spring.profiles.active進行配置環境的選擇。
如:spring.profiles.active = prod
如:application-prod.properties
## 微信開放平台 # APPID wechat.open.appid = # APPSECRET wechat.open.appsecret = # 回調地址 wechat.open.redirect_uri =
新建javaBean:WeChatUserInfo.java
@Getter @Setter @ToString public class WeChatUserInfo { String openid; String nickname; Integer sex; String province; String city; String country; String headimgurl; String privilege; String unionid; }
新建WeChatOpenLoginController.java實現微信開放平台第三方登錄的主要邏輯。
根據文檔進行編寫代碼。
1)請求獲取Code
前提:應用已經獲取相應的網頁授權作用域(scope=snsapi_login)
開發:第三方網站引導用戶打開鏈接
https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
參數說明:
注意:若提示“該鏈接無法訪問”,請檢查參數是否填寫錯誤,如redirect_uri的域名與審核時填寫的授權域名不一致或scope不為snsapi_login。
@RequestMapping("/login") public String openWeChatLogin(HttpServletRequest httpServletRequest) { // 防止csrf攻擊(跨站請求偽造攻擊) String state = UUID.randomUUID().toString().replaceAll("-", ""); // 采用redis等進行緩存state 使用sessionId為key 30分鍾后過期,可配置 RedisPoolUtil.setEx("wechat-open-state-" + httpServletRequest.getSession().getId(), state, Integer.parseInt(env.getProperty("wechat.open.exTime", "1800"))); String url = "https://open.weixin.qq.com/connect/qrconnect?" + "appid=" + env.getProperty("wechat.open.pc.appid").trim() + "&redirect_uri=" + env.getProperty("application.url") + env.getProperty("wechat.open.pc.redirect_uri").trim() + "&response_type=code" + "&scope=snsapi_login" + "&state=" + state + // 由后台自動生成 "#wechat_redirect"; return "redirect:" + url; }
2)用戶同意授權與否
用戶允許授權后,將會重定向到redirect_uri的網址上,並且帶上code和state參數
redirect_uri?code=CODE&state=STATE
若用戶禁止授權,則不會重定向到我們提供的回調地址中
成功授權后,將獲得Code,通過Code可以獲取access_token
3)獲取access_token
通過上述方法獲取的code獲取access_token.
Http Get請求
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
參數說明:
請求后,返回成功的json串為(樣例):
{ "access_token":"ACCESS_TOKEN", // 接口調用憑證 "expires_in":7200, // access_token接口調用憑證超時時間,單位(秒) "refresh_token":"REFRESH_TOKEN", //用戶刷新access_token "openid":"OPENID", //授權用戶唯一標識 "scope":"SCOPE", //用戶授權的作用域,使用逗號(,)分隔 "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL" //當且僅當該網站應用已獲得該用戶的userinfo授權時,才會出現該字段 }
失敗返回的樣例:
{ "errcode": 40029, "errmsg": "invalid code" }
失敗可能原因: 暫時不明
@RequestMapping("/callback/pc") public String openWeChatCallback(HttpServletRequest httpServletRequest, Model model) { String code = httpServletRequest.getParameter("code"); String state = httpServletRequest.getParameter("state"); String url = null; // 判斷state是否合法 String stateStr = RedisPoolUtil.get("wechat-open-state-" + httpServletRequest.getSession().getId()); if (StringUtils.isEmpty(code) || StringUtils.isEmpty(stateStr) || !state.equals(stateStr)) { throw new WechatParamException("非法參數,請重新登陸", "/"); } url = "https://api.weixin.qq.com/sns/oauth2/access_token?" + "appid=" + env.getProperty("wechat.open.pc.appid").trim() + "&secret=" + env.getProperty("wechat.open.pc.appsecret").trim() + "&code=" + code + "&grant_type=authorization_code"; JSONObject wechatAccessToken = HttpClientUtils.httpGet(url); if (wechatAccessToken.get("errcode") != null) { throw new WechatParamException("獲取accessToken失敗", "/wechat/open/login"); } String accessToken = (String) wechatAccessToken.get("access_token"); String openid = (String) wechatAccessToken.get("openid"); String unionid = (String) wechatAccessToken.get("unionid"); if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(openid) || StringUtils.isEmpty(unionid)) { throw new WechatParamException("獲取accessToken失敗", "/wechat/open/login"); } // TODO:根據Openid或Unionid對數據庫進行查詢,如果查詢到對應的用戶數據,則不需要再向微信服務器發送請求去返回數據。 // TODO: 建議使用Unionid作為查詢條件。 WeChatUserInfo weChatUserInfo = null; wechatAccessToken = null; // FIXME: 這里應該是從數據庫中查詢獲取用戶信息邏輯。 if (wechatAccessToken == null) { // 新用戶 weChatUserInfo = getUserInfoByAccessToken(accessToken); // 數據庫插入的操作 } if (weChatUserInfo != null) { model.addAttribute(weChatUserInfo); return "wechatUser"; } throw new WechatParamException("獲取用戶信息失敗", "/wechat/open/login"); }
4)通過access_token調用接口獲取用戶個人信息(UnionID機制)
前提:
1. access_token有效且未超時;
2. 微信用戶已授權給第三方應用帳號相應接口作用域(scope)。
Http Get請求:
https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID
返回成功的json結果(樣例):
{ "openid":"OPENID", //普通用戶的標識,對當前開發者帳號唯一 "nickname":"NICKNAME", //普通用戶昵稱 "sex":1, //普通用戶性別,1為男性,2為女性 "province":"PROVINCE", //普通用戶個人資料填寫的省份 "city":"CITY", //普通用戶個人資料填寫的城市 "country":"COUNTRY", //國家,如中國為CN "headimgurl": "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0", //用戶頭像,最后一個數值代表正方形頭像大小(有0、46、64、96、132數值可選,0代表640*640正方形頭像),用戶沒有頭像時該項為空 "privilege":[ //用戶特權信息,json數組,如微信沃卡用戶為(chinaunicom) "PRIVILEGE1", "PRIVILEGE2" ], "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL" //用戶統一標識。針對一個微信開放平台帳號下的應用,同一用戶的unionid是唯一的 }
失敗JSON樣例:
{ "errcode": 40003, "errmsg": "invalid openid" }
注意:在用戶修改微信頭像后,舊的微信頭像URL將會失效,因此開發者應該自己在獲取用戶信息后,將頭像圖片保存下來,避免微信頭像URL失效后的異常情況
最好保存用戶unionID信息,以便以后在不同應用中進行用戶信息互通。
private WeChatUserInfo getUserInfoByAccessToken(String accessToken) { if (StringUtils.isEmpty(accessToken)) { return null; //"accessToken為空"; } String get_userInfo_url = null; get_userInfo_url = "https://api.weixin.qq.com/sns/userinfo?" + "access_token=" + accessToken + "&openid=" + env.getProperty("wechat.open.pc.appid").trim(); String userInfo_result = HttpClientUtils.httpGet(get_userInfo_url, "utf-8"); if (!userInfo_result.equals("errcode")) { WeChatUserInfo weChatUserInfo = JSON.parseObject(userInfo_result, new TypeReference<WeChatUserInfo>() { }); // TODO: 需要把頭像信息下載到文件服務器,然后替換掉頭像URL。微信的或許不可靠,假設微信用戶更換了頭像,舊頭像URL是否會保存?而這個URL信息卻存放在我們的數據庫中,不可靠 return weChatUserInfo; } return null; //"獲取用戶信息失敗" }
5)刷新access_token
由於access_token有效期(目前為2個小時)較短,當access_token超時后,可以使用refresh_token進行刷新,access_token刷新結果有兩種:
1. 若access_token已超時,那么進行refresh_token會獲取一個新的access_token,新的超時時間;
2. 若access_token未超時,那么進行refresh_token不會改變access_token,但超時時間會刷新,相當於續期access_token。
refresh_token擁有較長的有效期(30天),當refresh_token失效的后,需要用戶重新授權。
請求方法:
獲取第一步的code后,請求以下鏈接進行refresh_token:
https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
參數說明:
成功返回的結果:
{ "access_token":"ACCESS_TOKEN", //接口調用憑證 "expires_in":7200, // access_token接口調用憑證超時時間,單位(秒) "refresh_token":"REFRESH_TOKEN", //用戶刷新access_token "openid":"OPENID", //授權用戶唯一標識 "scope":"SCOPE" //用戶授權的作用域,使用逗號(,)分隔 }
失敗樣例:
{ "errcode": 40030, "errmsg": "invalid refresh_token" }
注意:
1、Appsecret 是應用接口使用密鑰,泄漏后將可能導致應用數據泄漏、應用的用戶數據泄漏等高風險后果;存儲在客戶端,極有可能被惡意竊取(如反編譯獲取Appsecret);
2、access_token 為用戶授權第三方應用發起接口調用的憑證(相當於用戶登錄態),存儲在客戶端,可能出現惡意獲取access_token 后導致的用戶數據泄漏、用戶微信相關接口功能被惡意發起等行為;
3、refresh_token 為用戶授權第三方應用的長效憑證,僅用於刷新access_token,但泄漏后相當於access_token 泄漏,風險同上。
建議將secret、用戶數據(如access_token)放在App雲端服務器,由雲端中轉接口調用請求。
五、測試結果
1、網站應用
首先開啟redis。然后運行代碼。由於SpringBoot會使用內置的Tomcat容器進行管理,直接運行就可以了,然后點擊微信登錄。彈出掃碼登錄窗口,手機掃碼同意登錄后就可以獲取到用戶信息了.
六、應用關鍵參數位置
APPID和APPSecret以及回調地址位置,注:需要修改授權回調域,即回調地址