本来自己系统已经有了企业微信网页版登陆认证功能,但功能是别的同事写的,而且已经离职了。刚好,有另一个项目组同事来请教,所有就把该认证功能单独抽离出来,新写了一个springboot项目给那同事,并且已经联调好了。
注意:该认证功能比较依赖企业微信的配置,所以就把大致代码讲解一下,如果有真实配置,直接把代码挪用就好。
4.0.0
com.oysept.qyweixin
oysept-qyweixin
0.0.1-SNAPSHOT
jar
org.springframework.boot
spring-boot-starter-parent
1.5.9.RELEASE
org.springframework.boot
spring-boot-starter-web
com.alibaba
fastjson
1.2.49
org.springframework.boot
spring-boot-maven-plugin
com.oysept.qyweixin.ApplicationEntry
org.apache.tomcat.maven
tomcat7-maven-plugin
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);
}
}
注意: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
${rootPath}/${baseFile}.log
${rootPath}/${baseFile}_%d{yyyy-MM-dd}.log.gz
30
%X{logthreadId} [%date{yyyy-MM-dd HH:mm:ss.SSS}] %level %logger{36} %line - %msg%n
0
10000
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;
}
}
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;
}
}
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";
}
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;
}
}
方式一:如果直接请求http://localhost:7788/qyweixin/entry地址,浏览器会提示“请在微信客户端打开”。
所以需要把该地址复制微信中打开。
在微信中打开成功之后,微信可能会提示“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]
请勿恶意操作,谢谢!
本文说明:该文章属于原创,如需转载,请标明文章转载来源