以前寫過一篇公衆号的授權登入 https://blog.csdn.net/dsn727455218/article/details/65630151,
今天給大家分享一下企業微信的授權登入。
大緻都差不多流程
注意事項:
1.網頁授權及JS-SDK需要在企業微信上配置可信域名
2.企業微信授權登入裡面填寫你的可信域名
調用流程為:
A) 使用者通路第三方服務,第三方服務通過構造OAuth2連結(參數包括目前第三方服務的身份ID,以及重定向URI),将使用者引導到認證伺服器的授權頁
B) 使用者選擇是否同意授權
C) 若使用者同意授權,則認證伺服器将使用者重向到第一步指定的重定向URI,同時附上一個授權碼。
D) 第三方服務收到授權碼,帶上授權碼來源的重定向URI,向認證伺服器申請憑證。
E) 認證伺服器檢查授權碼和重定向URI的有效性,通過後頒發AccessToken(調用憑證)
企業微信OAuth2接入流程
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL0EmNykDN4kjN2gjZhJjYmV2M5gTOwMGO0Q2YwMzN3gTMhF2M0QTNl9CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.png)
使用OAuth2前須知
關于網頁授權的可信域名
REDIRECT_URL中的域名,需要先配置至應用的“可信域名”,否則跳轉時會提示“redirect_uri參數錯誤”。
要求配置的可信域名,必須與通路連結的域名完全一緻。舉個例子:
假定重定向通路的連結是:
http://mail.qq.com:8080/cgi-bin/helloworld:
配置域名 是否正确 原因
mail.qq.com:8080 配置域名與通路域名完全一緻
email.qq.com 配置域名必須與通路域名完全一緻
support.mail.qq.com 配置域名必須與通路域名完全一緻
*.qq.com 不支援泛域名設定
mail.qq.com 配置域名必須與通路域名完全一緻,包括端口号
假定配置的可信域名是 mail.qq.com:
通路連結 是否正确 原因
https://mail.qq.com/cgi-bin/helloworld 配置域名與通路域名完全一緻 http://mail.qq.com/cgi-bin/redirect 配置域名與通路域名完全一緻,與協定頭/連結路徑無關 https://exmail.qq.com/cgi-bin/helloworld配置域名必須與通路域名完全一緻
關于UserID機制
UserId用于在一個企業内唯一辨別一個使用者,通過網頁授權接口可以擷取到目前使用者的UserId資訊,如果需要擷取使用者的更多資訊可以調用 通訊錄管理 - 成員接口 來擷取。
緩存方案建議
通過OAuth2.0驗證接口擷取成員身份會有一定的時間開銷。對于頻繁擷取成員身份的場景,建議采用如下方案:
1、企業應用中的URL連結直接填寫企業自己的頁面位址
2、成員操作跳轉到步驟1的企業頁面時,企業背景校驗是否有辨別成員身份的cookie資訊,此cookie由企業生成
3、如果沒有比對的cookie,則重定向到OAuth驗證連結,擷取成員的身份資訊後,由企業背景植入辨別成員身份的cookie資訊
4、根據cookie擷取成員身份後,再進入相應的頁面
構造網頁授權連結
如果企業需要在打開的網頁裡面攜帶使用者的身份資訊,第一步需要構造如下的連結來擷取code參數:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=CORPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect參數說明:
參數 必須 說明
appid 是 企業的CorpID
redirect_uri 是 授權後重定向的回調連結位址,請使用urlencode對連結進行處理
response_type 是 傳回類型,此時固定為:code
scope 是
應用授權作用域。
snsapi_base:靜默授權,可擷取成員的基礎資訊;
snsapi_userinfo:靜默授權,可擷取成員的詳細資訊,但不包含手機、郵箱;
snsapi_privateinfo:手動授權,可擷取成員的詳細資訊,包含手機、郵箱。
state 否 重定向後會帶上state參數,企業可以填寫a-zA-Z0-9的參數值,長度不可超過128個位元組
wechat_redirect 是 終端使用此參數判斷是否需要帶上身份資訊
agentid 否 企業應用的id。
當scope是snsapi_userinfo或snsapi_privateinfo時,該參數必填。
注意redirect_uri的域名必須與該應用的可信域名一緻。
員工點選後,頁面将跳轉至 redirect_uri?code=CODE&state=STATE,企業可根據code參數獲得員工的userid。code長度最大為512位元組。
權限說明:
企業無限制;第三方使用snsapi_privateinfo的scope時,應用必須有’成員敏感資訊授權’的權限。
根據code擷取成員資訊
請求方式:GET(HTTPS)
請求位址:
https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=ACCESS_TOKEN&code=CODE參數 必須 說明
access_token 是 調用接口憑證
code 是 通過成員授權擷取到的code,最大為512位元組。每次成員授權帶上的code将不一樣,code隻能使用一次,5分鐘未被使用自動過期。
跳轉的域名須完全比對access_token對應應用的可信域名。
傳回結果:
a) 當使用者為企業成員時傳回示例如下:
{
"errcode":0,
"errmsg":"ok",
"UserId":"USERID",
"DeviceId":"DEVICEID",
"user_ticket":"USER_TICKET",
"expires_in":7200
}
參數 說明
errcode 傳回碼
errmsg 對傳回碼的文本描述内容
UserId 成員UserID
DeviceId 手機裝置号(由企業微信在安裝時随機生成,删除重裝會改變,更新不受影響)
user_ticket 成員票據,最大為512位元組。
scope為snsapi_userinfo或snsapi_privateinfo,且使用者在應用可見範圍之内時傳回此參數。
後續利用該參數可以擷取使用者資訊或敏感資訊。
expires_in user_token的有效時間(秒),随user_ticket一起傳回
非企業成員授權時傳回示例如下:
"OpenId":"OPENID",
"DeviceId":"DEVICEID"
OpenId 非企業成員的辨別,對目前企業唯一
出錯傳回示例:
"errcode":40029,
"errmsg":"invalid code"
實作代碼:
package com.eqiao.bidata.weixin.controller;
import com.eqiao.bidata.weixin.common.AccessToken;
import com.eqiao.bidata.weixin.common.Result;
import com.eqiao.bidata.weixin.common.WeiXinQiYeConstants;
import com.eqiao.bidata.weixin.common.WeiXinQiYeUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.UnsupportedEncodingException;
/**
- 單純實作OAuth2驗證,不使用注解及攔截器
-
Created by zhaoxinguo on 2017/7/11.
*/
@Controller
public class SimpleOAuth2Controller {
private Logger logger = LoggerFactory.getLogger(SimpleOAuth2Controller.class);
/**
* 拼接網頁授權連結
* 此處步驟也可以用頁面連結代替
* @return
*/
@RequestMapping(value = { "/oauth2wx.do" })
public String Oauth2API(HttpServletRequest request){
//擷取項目域名
String requestUrl = request.getServerName();
String contextPath = request.getContextPath();
logger.info("domain name: " + requestUrl + " project name: " + contextPath);
//拼接微信回調位址
String backUrl ="http://" + requestUrl + contextPath + "/oauth2me.do";
String redirect_uri = "";
try {
redirect_uri = java.net.URLEncoder.encode(backUrl, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
logger.error("ecdoe error: " + e.getMessage());
}
String oauth2Url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeiXinQiYeConstants.CORPID + "&redirect_uri=" + redirect_uri
+ "&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect";
return "redirect:" + oauth2Url;
}
/**
* 授權回調請求處理
* @return
*/
@RequestMapping(value = { "/oauth2me.do" })
public String oAuth2Url(HttpServletRequest request, @RequestParam String code){
// 調用擷取access_token的接口
AccessToken accessToken = WeiXinQiYeUtil.access_token();
HttpSession session = request.getSession();
if (accessToken != null && accessToken.getAccess_token() != null) {
// 調用擷取使用者資訊的接口
String UserId = getMemberGuidByCode(accessToken.getAccess_token(), code, WeiXinQiYeConstants.AGENTID);
logger.info("UserId: " + UserId);
if (UserId != null) {
session.setAttribute("UserId", UserId);
logger.info("UserId放入session成功!");
}
}
// 這裡簡單處理,存儲到session中
return "user/result";
}
/**
* 調用接口擷取使用者資訊
*
* @param token
* @param code
* @return
*/
public String getMemberGuidByCode(String token, String code, String agentId) {
logger.info("code==" + code + " token=" + token + " agentId=" +agentId);
Result result = WeiXinQiYeUtil.oAuth2GetUserByCode(token, code, agentId);
logger.info("result= " + result);
if (result.getErrcode().equals("0")) {
if (result.getUserId() != null && result.getUserId().length() > 0) {
// 此處可以通過微信授權用code還錢的Userid查詢自己本地伺服器中的資料
logger.info("result.getUserId(): " + result.getUserId());
return result.getUserId();
}
}
return "";
}
* OAuth2驗證接口根據code擷取成員資訊
*
* @param token
* @param code
* @return
*/
public static Result oAuth2GetUserByCode(String token, String code, String agentId) {
Result result = new Result();
String menuUrl = WeiXinQiYeConstants.GET_OAUTH2_URL.replace("ACCESS_TOKEN", token).replace("CODE", code).replace("AGENTID", agentId + "");
String userinfo = JHttpUtils.doGet(menuUrl);
logger.info("userinfo: " + userinfo);
JSONObject jsonObject = null;
if (userinfo != null) {
try {
jsonObject = JSONObject.fromObject(userinfo);
logger.info("jsonObject: " + jsonObject);
if (jsonObject.getString("UserId") != null && jsonObject.getString("UserId").length() > 0) {
result.setErrmsg(jsonObject.getString("errmsg"));
result.setErrcode(jsonObject.getString("errcode"));
result.setUserId(jsonObject.getString("UserId"));
} else {
result.setErrmsg(jsonObject.getString("errmsg"));
result.setErrcode(jsonObject.getString("errcode"));
}
} catch (Exception e) {
result.setErrmsg("accessToken 逾時......");
result.setErrcode("42001");
}
}
return result;
}
一個完整的流程就是這樣。
如遇到問題歡迎進群308742428
喜歡的朋友可以關注下,粉絲也缺。