1. 程式人生 > >Java—企業微信網頁版登陸認證詳解

Java—企業微信網頁版登陸認證詳解

背景

本來自己系統已經有了企業微信網頁版登陸認證功能,但功能是別的同事寫的,而且已經離職了。剛好,有另一個專案組同事來請教,所有就把該認證功能單獨抽離出來,新寫了一個springboot專案給那同事,並且已經聯調好了。

注意:該認證功能比較依賴企業微信的配置,所以就把大致程式碼講解一下,如果有真實配置,直接把程式碼挪用就好。

企業微信網頁版登陸認證(官網API oauth流程圖)

1、建立一個springboot專案(專案建立就不說了),結構圖如下

2、pom.xml中的配置如下

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.oysept.qyweixin</groupId>
  <artifactId>oysept-qyweixin</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>
  
  <!-- springboot父級jar -->
  <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>1.5.9.RELEASE</version>
       <relativePath/>
  </parent>
  
  	<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!-- json處理jar -->
        <dependency>
		    <groupId>com.alibaba</groupId>
		    <artifactId>fastjson</artifactId>
		    <version>1.2.49</version>
		</dependency>
		
    </dependencies>
    
    <build>
        <plugins>
        	<!-- 打成jar時,需要指定一個啟動類 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.oysept.qyweixin.ApplicationEntry</mainClass>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    
</project>

3、ApplicationEntry啟動類程式碼如下

package com.oysept.qyweixin;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 啟動類
 * @author ouyangjun
 */
@SpringBootApplication
public class ApplicationEntry {

	public static void main(String[] args) {
		
		SpringApplication.run(ApplicationEntry.class, args);
	}

}

4、application.properties配置檔案如下

注意:qyweixin.corpid:可以從企業微信中獲取,由於用的是公司真實的,所以就不貼上來了

           qyweixin.corpsecret:可以從企業微信中獲取,由於用的是公司真實的,所以就不貼上來了

           qyweixin.redirect_uri:localhost使用時一定要換成真實的可信域名,不然訪問的時候,微信會提示域名不可信

                                                可信域名需要在企業微信—>應用程式中新增配置

server.port=7788

qyweixin.corpid=
qyweixin.corpsecret=

qyweixin.redirect_uri=http://localhost:7788/qyweixin/oauth?url=http://localhost:7788/qyweixin/home

5、logback.xml配置內容如下(日誌配置主要是為了更好監控異常)

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>

	<!-- 在根目錄下/app/qyweixin/logs生成日誌檔案 -->
    <property name="rootPath" value="/app/qyweixin/logs" />
    <property name="baseFile" value="qyweixin"></property>
    <property name="log.root.level" value="INFO"></property>

    <!-- 檔案輸出日誌 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>${rootPath}/${baseFile}.log</File>
        <!-- 日誌檔案rolling策略 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${rootPath}/${baseFile}_%d{yyyy-MM-dd}.log.gz
            </FileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>

        <!-- 日誌輸出格式 -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <Pattern>%X{logthreadId} [%date{yyyy-MM-dd HH:mm:ss.SSS}] %level %logger{36} %line - %msg%n
            </Pattern>
        </encoder>
    </appender>

    <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
        <discardingThreshold>0</discardingThreshold>
        <queueSize>10000</queueSize>
        <appender-ref ref="FILE" />
    </appender>

    <root level="INFO">
        <appender-ref ref="ASYNC" />
    </root>

</configuration>

6、HttpInvoker工具類程式碼如下(主要用於http請求)

package com.oysept.qyweixin.utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;

/**
 * http請求工具類
 * @author ouyangjun
 *
 */
public class HttpInvoker {
	
	private final static Logger LOGGER = LoggerFactory.getLogger(HttpInvoker.class);
	
	/**
	 * 微信官方允許一天刷2000次 所以擬定2分鐘重新整理一次 24*60 / 2
	 * @param sUrl
	 * @param sMethod
	 * @param sOutput
	 * @return
	 */
	public static JSONObject exec(String sUrl, String sMethod, String sOutput) {
		JSONObject json = null;
		StringBuffer buffer = new StringBuffer();

		HttpURLConnection con = null;
		try {
			URL url = new URL(sUrl);
			con = (HttpURLConnection) url.openConnection();
			con.setDoOutput(true);
			con.setDoInput(true);
			con.setUseCaches(false);
			con.setRequestMethod(sMethod);
			// connection.setConnectTimeout(60000);

			con.setReadTimeout(60000);
			con.setConnectTimeout(60000);

			if (sOutput != null) {
				OutputStream os = con.getOutputStream();
				try {
					os.write(sOutput.getBytes("UTF-8"));
				} catch (Exception e) {
					LOGGER.info("HttpInvoker exec error: {}", e);
				} finally {
					if (os != null) {
						try {
							os.close();
						} catch (IOException e) {
							LOGGER.info("HttpInvoker exec error: {}", e);
						}
					}
				}
			}

			InputStream is = null;
			InputStreamReader inputReader = null;
			BufferedReader reader = null;
			try {
				is = con.getInputStream();
				inputReader = new InputStreamReader(is, "UTF-8");
				reader = new BufferedReader(inputReader);
				String temp;
				while ((temp = reader.readLine()) != null) {
					buffer.append(temp);
				}
			} catch (Exception e) {
				LOGGER.info("HttpInvoker exec error: {}", e);
			} finally {
				if (reader != null) {
					try {
						reader.close();
					} catch (IOException e) {
						LOGGER.info("HttpInvoker exec error: {}", e);
					}
				}
				if (inputReader != null) {
					try {
						inputReader.close();
					} catch (IOException e) {
						LOGGER.info("HttpInvoker exec error: {}", e);
					}
				}
				if (is != null) {
					try {
						is.close();
					} catch (IOException e) {
						LOGGER.info("HttpInvoker exec error: {}", e);
					}
				}
			}

			// con.disconnect();
			json = JSONObject.parseObject(buffer.toString());
			
			if (json != null) {
				LOGGER.info("OK, http連線Url: {}, 返回資料,json: {}", sUrl, json);
			} else {
				LOGGER.info("return json is null, http連線Url: {}, 返回資料,json: {}", sUrl, json);
			}
		} catch (IOException e) {
			LOGGER.info("HttpInvoker exec error: {}", e);
		} finally {
			if (con != null) {
				con.disconnect();
			}
		}

		return json;
	}

}

7、WeChatAccessTokenUtils程式碼內容如下(主要用於獲取access_token)

package com.oysept.qyweixin.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;

/**
 * 獲取access_token工具類
 * @author ouyangjun
 *
 */
public class WeChatAccessTokenUtils {
	
	private final static Logger LOGGER = LoggerFactory.getLogger(WeChatAccessTokenUtils.class);
	
	/**
	 * @param sCorpId 企業號的標識
	 * @param sSecret 管理組憑證金鑰
	 * @return
	 * @throws @description: 獲取AccessToken
	 * @author: ouyangjun
	 * @date: 
	 */
	public static String getAccessToken(String corpid, String corpsecret) {
		//獲取路徑,然後替換具體的引數
		String GET_ACCESS_TOKEN_URL = WeChatUtils.QY_WEIXIN_ACCESS_TOKEN;
		String requestUrl = GET_ACCESS_TOKEN_URL.replace("CORPID", corpid).replace("SECRECT", corpsecret);
		JSONObject json = HttpInvoker.exec(requestUrl, "GET", null);

		String token = null;
		if (json != null) {
			try {
				token = json.getString("access_token");

				// 列印訊息
				String message = String.format("獲取token成功  access token: %s, expire_in: %s", json.getString("access_token"),json.getInteger("expires_in"));
				LOGGER.info(message);
			} catch (Exception e) {
				String error = String.format("獲取token失敗    errcode: %s ,errmsg: %s", json.getInteger("errcode"),json.getString("errmsg"));
				LOGGER.info(error);
			}
		}

		return token;
	}
	
}

8、WeChatUtils程式碼內容如下(主要配置了企業微信一些請求url)

package com.oysept.qyweixin.utils;

/**
 * weixin url工具類
 * @author ouyangjun
 */
public class WeChatUtils {
	
	// access_token獲取地址
	public final static String QY_WEIXIN_ACCESS_TOKEN = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=CORPID&corpsecret=SECRECT";
	
	// 根據appid獲取code
	public final static String QY_WEIXIN_OAUTH_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=CORPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&agentid=AGENTID&state=STATE#wechat_redirect";
	
	// 根據access_token和code獲取使用者基本資訊
	public final static String QY_WEIXIN_USERINFO_URL = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=ACCESS_TOKEN&code=CODE";
	
}

9、WeixinEntryController介面程式碼如下

package com.oysept.qyweixin.controller;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.tomcat.util.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alibaba.fastjson.JSONObject;
import com.oysept.qyweixin.utils.HttpInvoker;
import com.oysept.qyweixin.utils.WeChatAccessTokenUtils;
import com.oysept.qyweixin.utils.WeChatUtils;

/**
 * 由於微信請求需要轉發,所以就不用@RestController
 * 請求地址: http://localhost:7788/qyweixin/entry
 *         http://127.0.0.1:7788/qyweixin/entry
 * @author ouyangjun
 */
@Controller
@RequestMapping(value = "/qyweixin")
public class WeixinEntryController {
	
	// 日誌列印工具類
	private final static Logger LOGGER = LoggerFactory.getLogger(WeixinEntryController.class);
	
	// @Value註解是用於讀取application.properties檔案中的配置
	// 重定向地址
	@Value("${qyweixin.corpid:NaN}")
	private String corpid;
	
	// 重定向地址
	@Value("${qyweixin.corpsecret:NaN}")
	private String corpsecret;
		
	// 重定向地址
	@Value("${qyweixin.redirect_uri:NaN}")
	private String redirectURI;
	
	/**
	 * 該系統首頁測試介面
	 * @return
	 */
	@RequestMapping(value = "/home")
	@ResponseBody
	public String home(){
		return "oysept qyweixin home!";
	}

	/**
	 * 微信入口,該地址可以配置在企業微信中,從企業微信中點選選單訪問該介面
	 * 作用: 根據企業微信corpid得到獲取code的url
	 * @return
	 */
	@RequestMapping(value = "/entry")
	public String weixinEntry(HttpServletRequest request, HttpServletResponse response){
		LOGGER.info("weixinEntry corpid: {}, redirectURI: {}, ", this.corpid, this.redirectURI);
		
		// 重定向的地址,需要是一個可以信任的域名,不然提示域名不可信
		String redirect_uri = "";
		try {
			// redirect_uri
			redirect_uri = URLEncoder.encode(this.redirectURI, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			LOGGER.info("weixinEntry redirect_uri error: {}", e);
		}
		
		// 微信認證地址,該地址會獲取一個code,並且該地址只能在微信客戶端中開啟
		String oauthUrl = WeChatUtils.QY_WEIXIN_OAUTH_URL
				.replace("CORPID", this.corpid)
				.replace("REDIRECT_URI", redirect_uri);
		// 需要把該地址複製到微信客戶端中開啟連結, 微信會根據redirect_uri引數自動跳轉地址
		LOGGER.info("weixinEntry oauthUrl: {}", oauthUrl);
		
		// 重定向,該地址一定要在微信客戶端裡才能開啟
		return "redirect:" + oauthUrl;
	}
	
	/**
	 * 使用者資訊認證登陸介面
	 * @param url
	 * @param code
	 * @return
	 */
	@RequestMapping(value = "/oauth")
	public String oauthWeiXin(@RequestParam(value="url") String url,
			@RequestParam(value="code") String code,
			HttpServletRequest request, HttpServletResponse response){
		LOGGER.info("oauthWeiXin corpid: {}, corpsecret: {}, ", this.corpid, this.corpsecret);
// 在url後面拼接引數,randomNumber是為了防止微信快取
		if (!StringUtils.isEmpty(url)) {
			url += "?userId=USERID&randomNumber="+System.currentTimeMillis();
		}
		LOGGER.info("oauthWeiXin url: {}, code: {}", url, code);
		
		// 獲取access_token json字串
		String token = WeChatAccessTokenUtils.getAccessToken(this.corpid, this.corpsecret);
		LOGGER.info("oauthWeiXin token: {}", token);
		
		String userId = "";
		// 根據access_token和code獲取使用者基本資訊
		if (!StringUtils.isEmpty(token)) {
			
			// 獲取使用者基本資訊url,code只能用一次,一次之後就會失效
			String userinfo_url = WeChatUtils.QY_WEIXIN_USERINFO_URL
					.replace("ACCESS_TOKEN", token)
					.replace("CODE", code);
			
			// 獲取使用者資訊
			JSONObject userJson = HttpInvoker.exec(userinfo_url, "GET", null);
			LOGGER.info("oauthWeiXin userJson: {}", userJson);
			if(!StringUtils.isEmpty(userJson)) {
				// 企業微信返回的json中有UserId引數
				userId = String.valueOf(userJson.get("UserId"));
				
				// 該判斷是為了相容,容錯
				if (userId == null && userJson.containsKey("userId")) {
					userId = userJson.getString("userId");
				}
				LOGGER.info("oauthWeiXin userId: {}", userId);
			}
		}
		
		// 使用者ID用Base64編碼,是為了防止使用者中出現的特殊字元
		String redirect_uri = url.replace("USERID", Base64.encodeBase64String(userId.getBytes()));
		LOGGER.info("oauthWeiXin redirect_uri: {}", redirect_uri);
		// 重定向首頁地址
		return "redirect:" + redirect_uri;
	}
	
}

10、具體訪問方式如下

              所以需要把該地址複製微信中開啟。

               在微信中開啟成功之後,微信可能會提示“redirect_uri域名不可信”,該原因是因為域名為在企業微信配置。

              如果都OK,引數中url就是認證成功需要跳轉的頁面。

              注意: 通過本地除錯只能看流程走向,具體還需要真實的配置來支撐。

方式二:把WeChatUtils中QY_WEIXIN_OAUTH_URL地址替換成真實的直接訪問,也會遇到“redirect_uri域名不可信”。

              注意:由於根據該地址,會返回一個code,這個code是微信自動生成,並且該code使用一次就會失效。

備註:由於企業微信限制太多,我是用了真實配置測試過程式碼的,有真實配置的,可以直接使用程式碼。

本章完結,待續!

原始碼下載地址: https://gitee.com/ouyangjun_xm/java/attach_files下oysept-qyweixin.rar壓縮包

                      碼雲賬戶: [email protected]     密碼: [email protected]

                      請勿惡意操作,謝謝!

本文說明:該文章屬於原創,如需轉載,請標明文章轉載來源

相關推薦

Java企業網頁登陸認證

背景 本來自己系統已經有了企業微信網頁版登陸認證功能,但功能是別的同事寫的,而且已經離職了。剛好,有另一個專案組同事來請教,所有就把該認證功能單獨抽離出來,新寫了一個springboot專案給那同事,並且已經聯調好了。 注意:該認證功能比較依賴企業微信的配置,所以就把大致

JAVA開發 個人號 小機器人的實現 java封裝網頁

依賴於開源專案,GIT地址:https://github.com/biezhi/wechat-api 程式碼中有一些bug,需要自己改一改,然後就可以構建小機器人了 開發中遇到的棘手的問題,在拉取訊息的時候,視訊(VIDEO)格式的資料拉取不到,為空,經過嗅探,發現在訪問

Java企業開發_09_身份驗證之移動端網頁授權(有完整項目源碼)

.com post請求 ati errcode http nbsp code repl button 註: 源碼已上傳github: https://github.com/shirayner/WeiXin_QiYe_Demo 一、本節要點 1.1 授權回調域(可信

JustAuth 1.15.9 釋出,支援飛書、喜馬拉雅、企業網頁登入

新增 修復並正式啟用 飛書 平臺的第三方登入 AuthToken 類中新增 refreshTokenExpireIn 記錄 refresh token 的有效期 PR 合併 Github #101:支援喜馬拉雅登入 合併 Github #105:支援企業微信網頁授權登入 合

Java企業開發_Exception_01_"errcode":60011,"errmsg":"no privilege to access/modify contact/party/agent "

有用 rtm access cep 企業 json agent tac 增加 微信企業號增加成員時,返回錯誤信息: jsonObject:{"errcode":60011,"errmsg":"no privilege to access/modify contact/

Java企業開發_04_自定義菜單

組裝 sys 測試 搜索 我們 測試類 ray 翻譯 請求 一、本節要點 1.菜單相關實體類的封裝 參考官方文檔中的請求包的內容,對菜單相關實體類進行封裝。 這裏需要格外註意的是,企業微信中請求包的數據是Json字符串格式的,而不是xml格式。關於json序列化的問題

Java企業開發_07_總結一下企業的配置

alt 微信 idt cnblogs 信息 java 域名 ram com 一.企業微信後臺 1.回調url 2.可信域名 3.菜單跳轉按鈕中的鏈接 4.PC端網頁授權 二、代碼內 1.企業微信的配置信息:WeiXinParamesUtil

Java企業開發_10_未驗證域名歸屬,JS-SDK功能受限

校驗 style 現象 -s 解決方案 img ges pps span 1.現象: 在企業微信後臺填寫可信域名後,提示:未驗證域名歸屬,JS-SDK功能受限,如下圖: 點擊“申請域名校驗”後, 註意:域名根目錄 當時一直

Java企業開發_13_異常:com.qq.weixin.mp.aes.AesException: 解密後得到的buffer非法

bst 圖片 crypt javax nature current aps protoc spa 一、異常信息 方法:POST@ echostr是否存在 :false java.lang.IllegalArgumentException: 20 > -36

.netMVC企業網頁授權+註冊全局過濾器

glob for init cgi http 一個 使用 QQ == 微信網頁授權   達到效果:企業應用只能在微信中查看,在瀏覽器中打開企業頁面,顯示無權限! 原理,用session記錄用戶,如果用戶已經通過微信授權,記錄@Session["UserId"],如果用戶

Java-Web網頁支付開發流程以及各種坑

微信網頁支付流程概覽 生成Oauth2.0授權連結獲得code 使用code獲得使用者資訊(OpenId) 向微信統一下單介面提交訂單獲得PrepayID 在JS頁面中向用戶發起支付請求 使用者輸入支付密碼完成支付 使用weixin-

JAVA實現網頁授權的實列程式碼

1、需要有一個公眾號(我這裡用的測試號),拿到AppID和AppSecret; 2、進入公眾號開發者中心頁配置授權回撥域名。具體位置:介面許可權-網頁服務-網頁賬號-網頁授權獲取使用者基本資訊-修改 注意,這裡僅需填寫全域名(如www.qq.com、www.baidu.c

網頁營銷軟體 防撤回 自動同意加好友

微信網頁版營銷軟體,可實現微信自動同意加好友、微信發訊息防撤回、微信群訊息防撤回、好友訊息自動回覆等功能。還可以實現各種群發好友資訊。此外好友發來的圖片等,可以實現自定義的儲存到本地資料夾。詳細的可以看下面的截圖:時崎慕意部落格原文地址:掃描全能王破解版 紙質文件掃描成文字電

利用長輪詢實現網頁掃碼登入

掃碼登入操作過程 手機登入微信,利用“掃一掃”功能掃描網頁上的二維碼手機掃描成功後,提示“登入網頁版微信”;網頁上顯示“成功掃描 請在手機點選確認以登入”手機端點選“登入網頁版微信”,網頁跳轉到使用者的微信操作介面 整個掃碼登入的操作過程還是挺簡單的,而且互動地實時性比較

網頁登入爬蟲小案例

微信登入爬蟲小案例,使用的抓包工具是fiddler import requests import re from PIL import Image import urllib3 urllib3.disable_warnings() from bs4 imp

網頁無法登入

掃描微信網頁二維碼登入時出現錯誤:當前登入環境異常。為了你的帳號安全,暫時不能登入web微信。你可以通過Windows微信、Mac微信或者手機客戶端微信登入導致微信無法登入解決方法:清除瀏覽器的history然後重新登入就可以了不過發現這個方法在每次登入前都要執行,否則問題還

網頁協議分析和實現機器人

開啟首頁,分配一個隨機uuid,根據該uuid獲取二維碼圖片。微信客戶端掃描該圖片,在客戶端確認登入。瀏覽器不停的呼叫一個介面,如果返回登入成功,則呼叫登入介面此時可以獲取聯絡人列表,可以傳送訊息。然後不斷呼叫同步介面。如果同步介面有返回,則可以獲取新訊息,然後繼續呼叫同步介面。Java版實現原始碼:htt

網頁掃碼登入原理

微信網頁版登入步驟 2.  手機掃描二維碼,網頁出現使用者頭像 3.  手機確認登入 4.  網頁加載出使用者微信介面 手機一掃描二維碼,並且同意登入後,網頁上就能及時知道當前二維碼被掃描,使用者同意後,立馬就能加載出聊天介面,實時性非常高, 問題 那麼網頁是如何

網頁訊息 抓包

【獲取全部聯絡人列表】 https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?r=1388976086734 (每次登陸【FromUserName】、【ToUserName】都是變化的) 【微信網頁版訊息提示音】

小程式(6)模板 template

<template name="userTemp">     <view class="user">       <view>姓名:{{item.name}}</view>   &