注: 源码已上传github: https://github.com/shirayner/WeiXin_QiYe_Demo

一、本节要点

1.1 授权回调域(可信域名)

在开始使用网页授权之前,需要先设置一下授权回调域。这里瞬间想到之前做JSSDK的时候,也设置过一个域名。二者本质上都是设置可信域名。

当用户授权完毕之后,请求将重定向到此域名(或者子域名)下的执行者(jsp页面或者servlet等)。如何设置授权回调域,请见第二节。

1.2 获取Code

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

可将根据此链接生成一个view菜单按钮,用户点击此按钮时,将跳转到授权页面,授权成功后,页面重定向到指定的REDIRECT_URI页面,同时带上code和state请求参数,即页面将跳转至 redirect_uri?code=CODE&state=STATE页面。

1.3 移动端网页授权流程

(1)用户点击1.2中菜单按钮,跳转至授权页面

(2)用户授权成功,页面重定向到 redirect_uri?code=CODE&state=STATE 页面

(3)接收code,根据code获取成员信息(UserId,user_ticket)

(4)拿到UserId后可选择去根据UserId获取成员详细信息, 参见Java企业微信开发_02_通讯录同步 中的 Contacts_UserService类

(5) 拿到 user_ticket后可选择去使用user_ticket获取成员详情(其中包括用户头像)

在此我们只关注于打通 企业微信官方文档中 移动端网页授权 相关的接口,这是基础,至于实际工作中企业是如何去具体实现他们自己的授权业务,暂时不在我们讨论的范围内。

二、代码实现

2.1设置可信域名(授权回调域)

登录企业微信后台—>企业应用—>自建应用中的你的具体应用—>企业微信授权登录—>Web网页

2.2 生成菜单按钮

添加一个用于网页授权的菜单按钮,运行MenuTest.testCreateMenu()方法生成按钮。

在已经成功生成菜单按钮时,有时可能出现菜单没有及时更新的情况,这时可以通过取消关注企业号,再重新关注企业号来解决这个问题。

(1)MenuService.java

package com.ray.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import com.google.gson.Gson;
import com.ray.pojo.menu.Button;
import com.ray.pojo.menu.CommonButton;
import com.ray.pojo.menu.ComplexButton;
import com.ray.pojo.menu.Menu;
import com.ray.pojo.menu.ViewButton;
import com.ray.util.WeiXinUtil; import net.sf.json.JSONObject; public class MenuService {
private static Logger log = LoggerFactory.getLogger(MenuService.class);
// 菜单创建(POST) 限100(次/天)
public static String create_menu_url = "https://qyapi.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN&agentid=AGENTID"; /**
* 1.创建菜单
*
* @param menu 菜单实例
* @param accessToken 有效的access_token
* @return 0表示成功,其他值表示失败
*/
public void createMenu(String accessToken,Menu menu,int agentId) { //1.获取json字符串:将Menu对象转换为json字符串
Gson gson = new Gson();
String jsonMenu =gson.toJson(menu); //使用gson.toJson(user)即可将user对象顺序转成json
System.out.println("jsonMenu:"+jsonMenu); //2.获取请求的url
create_menu_url = create_menu_url.replace("ACCESS_TOKEN", accessToken)
.replace("AGENTID", String.valueOf(agentId)); //3.调用接口,发送请求,创建菜单
JSONObject jsonObject = WeiXinUtil.httpRequest(create_menu_url, "POST", jsonMenu);
System.out.println("jsonObject:"+jsonObject.toString()); //4.错误消息处理
if (null != jsonObject) {
if (0 != jsonObject.getInt("errcode")) {
log.error("创建菜单失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));
}
} } /**
* 2.组装菜单数据
*
* @return
*/
public Menu getMenu() {
/* ViewButton btn11 = new ViewButton();
btn11.setName("添加报销单");
btn11.setType("view");
btn11.setUrl("http://5nffqn.natappfree.cc/WeiXin_QiYe_Demo/uploadExpenseAccaout.jsp");
*/
ViewButton btn11 = new ViewButton();
btn11.setName("JSSDK多图上传");
btn11.setType("view");
btn11.setUrl("http://zvuntx.natappfree.cc/WeiXin_QiYe_Demo/JSSDKUploadPics.jsp"); ViewButton btn21 = new ViewButton();
btn21.setName("JSSDK测试(全)");
btn21.setType("view");
btn21.setUrl("http://zvuntx.natappfree.cc/WeiXin_QiYe_Demo/jsapiTicktAll.jsp"); ViewButton btn22 = new ViewButton();
btn22.setName("PC端网页授权");
btn22.setType("view");
btn22.setUrl("https://open.work.weixin.qq.com/wwopen/sso/qrConnect?appid=ww92f5da92bb24696e&agentid=1000002&redirect_uri=http%3A%2F%2Fzvuntx.natappfree.cc%2FWeiXin_QiYe_Demo%2FPCAuthorization.jsp&state=state"); ViewButton btn23 = new ViewButton();
btn23.setName("移动端网页授权");
btn23.setType("view");
btn23.setUrl("https://open.weixin.qq.com/connect/oauth2/authorize?appid=ww92f5da92bb24696e&redirect_uri=http%3A%2F%2Fzvuntx.natappfree.cc%2FWeiXin_QiYe_Demo%2FMTAuthorization.jsp&response_type=code&scope=snsapi_privateinfo&agentid=1000002&state=hec#wechat_redirect"); CommonButton btn12 = new CommonButton();
btn12.setName("扫一扫");
btn12.setType("click");
btn12.setKey("12"); CommonButton btn13 = new CommonButton();
btn13.setName("翻译功能");
btn13.setType("click");
btn13.setKey("13"); ViewButton btn14 = new ViewButton();
btn14.setName("上传图片");
btn14.setType("view");
btn14.setUrl("http://5nffqn.natappfree.cc/WeiXin_SanFenBaiXue/uploadimg.jsp"); ViewButton btn15 = new ViewButton();
btn15.setName("上传图片2");
btn15.setType("view");
btn15.setUrl("http://5nffqn.natappfree.cc/WeiXin_SanFenBaiXue/index2.jsp"); CommonButton btn24 = new CommonButton();
btn24.setName("人脸识别");
btn24.setType("click");
btn24.setKey("24"); CommonButton btn25 = new CommonButton();
btn25.setName("聊天唠嗑");
btn25.setType("click");
btn25.setKey("25"); CommonButton btn31 = new CommonButton();
btn31.setName("Q友圈");
btn31.setType("click");
btn31.setKey("31"); CommonButton btn33 = new CommonButton();
btn33.setName("幽默笑话");
btn33.setType("click");
btn33.setKey("33"); CommonButton btn34 = new CommonButton();
btn34.setName("用户反馈");
btn34.setType("click");
btn34.setKey("34"); CommonButton btn35 = new CommonButton();
btn35.setName("关于我们");
btn35.setType("click");
btn35.setKey("35"); ViewButton btn32 = new ViewButton();
btn32.setName("周边搜索");
btn32.setType("view");
btn32.setUrl("http://liufeng.gotoip2.com/xiaoqrobot/help.jsp"); ComplexButton mainBtn1 = new ComplexButton();
mainBtn1.setName("正在做功能");
mainBtn1.setSub_button(new Button[] { btn11, btn12, btn13, btn14, btn15 }); ComplexButton mainBtn2 = new ComplexButton();
mainBtn2.setName("测试");
mainBtn2.setSub_button(new Button[] { btn21, btn22, btn23, btn24, btn25 }); ComplexButton mainBtn3 = new ComplexButton();
mainBtn3.setName("更多");
mainBtn3.setSub_button(new Button[] { btn31, btn33, btn34, btn35, btn32 }); /**
* 这是企业号目前的菜单结构,每个一级菜单都有二级菜单项<br>
*
* 在某个一级菜单下没有二级菜单的情况,menu该如何定义呢?<br>
* 比如,第三个一级菜单项不是“更多体验”,而直接是“幽默笑话”,那么menu应该这样定义:<br>
* menu.setButton(new Button[] { mainBtn1, mainBtn2, btn33 });
*/
Menu menu = new Menu();
menu.setButton(new Button[] { mainBtn1, mainBtn2, mainBtn3 }); return menu;
} }

(2)MenuTest.java

package com.ray.test;

import org.junit.Test;

import com.ray.pojo.menu.Menu;
import com.ray.service.MenuService;
import com.ray.util.WeiXinParamesUtil;
import com.ray.util.WeiXinUtil; public class MenuTest { @Test
public void testCreateMenu(){
//1.组装菜单
MenuService ms=new MenuService();
Menu menu=ms.getMenu(); //2.获取access_token:根据企业id和应用密钥获取access_token
String accessToken=WeiXinUtil.getAccessToken(WeiXinParamesUtil.corpId, WeiXinParamesUtil.agentSecret).getToken();
System.out.println("accessToken:"+accessToken); //3.创建菜单
ms.createMenu( accessToken, menu, WeiXinParamesUtil.agentId); } }

2.3 移动端网页授权业务类——MTAuthorizationService.java

包括两个方法:

(1)根据code获取成员信息

(2)使用userTicket获取成员详情

package com.ray.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import com.google.gson.Gson;
import com.ray.pojo.UserTicket;
import com.ray.util.WeiXinUtil; import net.sf.json.JSONObject; /**
* @desc : 移动端网页授权Service
*
* @author: shirayner
* @date : 2017年9月14日 下午12:18:38
*/
public class MTAuthorizationService {
private static Logger log = LoggerFactory.getLogger(MenuService.class); public static final String GET_USERINFO_URL = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=ACCESS_TOKEN&code=CODE"; public static final String GET_USERDETAIL_URL = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserdetail?access_token=ACCESS_TOKEN"; /** 1.根据code获取成员信息
* @desc :GET请求、
* 成员信息包括
* UserId 成员UserID
* DeviceId 手机设备号(由企业微信在安装时随机生成,删除重装会改变,升级不受影响)
* user_ticket 成员票据,最大为512字节。scope为snsapi_userinfo或snsapi_privateinfo,且用户在应用可见范围之内时返回此参数。
* 后续利用该参数可以获取用户信息或敏感信息。
* expires_in user_token的有效时间(秒),随user_ticket一起返回
*
* @param accessToken
* @param code void
*/
public JSONObject getUserInfo(String accessToken,String code) {
//1.获取请求的url
String get_userInfo_url=GET_USERINFO_URL.replace("ACCESS_TOKEN", accessToken)
.replace("CODE", code); //2.调用接口,发送请求,获取成员信息
JSONObject jsonObject = WeiXinUtil.httpRequest(get_userInfo_url, "GET", null);
System.out.println("jsonObject:"+jsonObject.toString()); //3.错误消息处理
if (null != jsonObject) {
if (0 != jsonObject.getInt("errcode")) {
log.error("获取成员信息失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));
}
} return jsonObject; }
/** 2.使用userTicket获取成员详情
* @desc :POST请求
*
* @param accessToken
* @param userTicket
* @return JSONObject
*/
public JSONObject getUserDetail(String accessToken,UserTicket userTicket) {
//1.获取请求地址
String get_userDetail_url=GET_USERDETAIL_URL.replace("ACCESS_TOKEN", accessToken); //2.准备好请求包体
Gson gson = new Gson();
String jsonUserTicket =gson.toJson(userTicket); //使用gson.toJson(user)即可将user对象顺序转成json
System.out.println("jsonUserTicket:"+jsonUserTicket); //2.调用接口,发送请求,获取成员信息
JSONObject jsonObject = WeiXinUtil.httpRequest(get_userDetail_url, "POST", jsonUserTicket);
System.out.println("jsonObject:"+jsonObject.toString()); //3.错误消息处理
if (null != jsonObject) {
if (0 != jsonObject.getInt("errcode")) {
log.error("获取成员信息失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));
}
} return jsonObject; } }

2.4前端页面——MTAuthorization.jsp

授权成功后将跳转到这个页面。在这个页面里调用了MTAuthorizationService中的方法来获取用户信息

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<%@page language="java" import="com.ray.pojo.*"%>
<%@page language="java" import="com.ray.service.*"%>
<%@page language="java" import="com.ray.util.*"%>
<%@page language="java" import="net.sf.json.JSONObject"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>移动端网页授权</title> <script src="/js/jquery.js"></script>
<script src="http://rescdn.qqmail.com/node/ww/wwopenmng/js/sso/wwLogin-1.0.0.js"></script>
</head>
<body> <%
String code= request.getParameter("code");
String state=request.getParameter("state"); MTAuthorizationService mts=new MTAuthorizationService();
String accessToken=WeiXinUtil.getAccessToken(WeiXinParamesUtil.corpId, WeiXinParamesUtil.agentSecret).getToken();
//获取成员信息
JSONObject userInfo=mts.getUserInfo(accessToken, code); //获取成员详情
UserTicket userTicket=new UserTicket();
userTicket.setUser_ticket(userInfo.getString("user_ticket")); JSONObject userDetail=mts.getUserDetail(accessToken, userTicket); %>
hello,这里是第三方应用
code=<%= code%> <br>
state=<%= state%> <br>
userInfo=<%= userInfo.toString()%> <br><br>
userTicket=<%= userInfo.getString("user_ticket")%> <br><br>
userDetail=<%= userDetail.toString()%> <br><br> </body>
</html>

Java企业微信开发_09_身份验证之移动端网页授权(有完整项目源码)的更多相关文章

  1. Java企业微信开发_09_素材管理之下载微信临时素材到本地服务器

    一.本节要点 1.获取临时素材接口 请求方式:GET(HTTPS) 请求地址:https://qyapi.weixin.qq.com/cgi-bin/media/get?access_token=AC ...

  2. Java企业微信开发_10_未验证域名归属,JS-SDK功能受限

    1.现象: 在企业微信后台填写可信域名后,提示:未验证域名归属,JS-SDK功能受限,如下图: 点击“申请域名校验”后, 注意:域名根目录 当时一直不清楚这个域名根目录在哪里,最后让我给试出来了 2. ...

  3. Java企业微信开发_01_接收消息服务器配置

    一.准备阶段 需要准备事项: 1.一个能在公网上访问的项目: 见:Java微信公众平台开发_01_本地服务器映射外网 2.一个企业微信账号: 去注册:(https://work.weixin.qq.c ...

  4. Java企业微信开发_03_通讯录同步

    一.本节要点 1.获取通讯录密钥 获取方式: 登录企业微信—>管理工具—>通讯录同步助手—>开启“API接口同步”  ; 开启后,即可看到通讯录密钥,也可设置通讯录API的权限:读取 ...

  5. Java企业微信开发_07_素材管理之上传本地临时素材文件

    一.本节要点 1.临时素材有效期 media_id是可复用的,同一个media_id可用于消息的多次发送(3天内有效) 2.上传文件时的http请求里都有啥 具体原理可参看: 为什么上传文件的表单需要 ...

  6. Java企业微信开发_05_消息推送之发送消息(主动)

    一.本节要点 1.发送消息与被动回复消息 (1)流程不同:发送消息是第三方服务器主动通知微信服务器向用户发消息.而被动回复消息是 用户发送消息之后,微信服务器将消息传递给 第三方服务器,第三方服务器接 ...

  7. Java企业微信开发_06_素材管理之上传本地临时素材文件至微信服务器

    一.本节要点 1.临时素材有效期 media_id是可复用的,同一个media_id可用于消息的多次发送(3天内有效) 2.上传文件时的http请求里都有啥 具体原理可参看: 为什么上传文件的表单需要 ...

  8. Java企业微信开发_05_消息推送之被动回复消息

    一.本节要点 1.消息的加解密 微信加解密包 下载地址:http://qydev.weixin.qq.com/java.zip      ,此包中封装好了AES加解密方法,直接调用方法即可. 其中,解 ...

  9. Java企业微信开发_04_消息推送之发送消息(主动)

    源码请见: Java企业微信开发_00_源码及资源汇总贴 一.本节要点 1.发送消息与被动回复消息 (1)流程不同:发送消息是第三方服务器主动通知微信服务器向用户发消息.而被动回复消息是 用户发送消息 ...

随机推荐

  1. C++ STL 栈和队列详解

    一.解释: 1.栈 栈是一种特殊的线性表.其特殊性在于限定插入和删除数据元素的操作只能在线性表的一端进行.如下所示: 结论:后进先出(Last In First Out),简称为LIFO线性表. 举个 ...

  2. 在html中使用javascript

    使用script元素,script6个元素 1.async:应该立即下载 2.charset:通过src属性指定代码的字符集 3.defer:表示脚本可以延迟到文档完全解析和显示后运行 4.langu ...

  3. jQuery全选、全不选、反选的简洁写法【实例】

    全选方面的功能几乎是每个需要列表展示的网站所必不可少的,当然此功能也有很多种写法,现在介绍一下,比较简洁易懂的写法: <input type="checkbox" name= ...

  4. COM编程_第一讲_深入COM框架以及实现简单的COM

    一丶我们要理解COM是什么(为什么理解) 现在很多人会用com(也就是ALT)但是不知道原理,如果改一点东西,那么整体的框架重来,因为你不懂改哪里,如果懂了,那么遇到问题,那么就会知道我要怎么做,是什 ...

  5. react入门之使用react-bootstrap当轮子造车(二)

    react入门之使用react-bootstrap当轮子造车(二) 上一篇我们谈了谈如何配置react的webpack环境 react入门之搭配环境(一) 可能很多人已经打开过官方文档学习了react ...

  6. 【NO.3-2】Jmeter - 在Linux配置HOSTS的方法

    在Linux配置HOSTS 咱平时在Windows系统做web测试的时候需要修改HOSTS文件.Linux也一样. /*在Linux执行性能测试的时候,很容易忘记配置HOSTS,那么你发送的请求的响应 ...

  7. NodeJS基本使用简介

    Node.js 1.Nvm Node的版本管理工具,用于切换node的版本 一.下载nvm,放在纯英文路径 二.用命令行打开nvm.exe 三.打开setting.txt,其中有四个配置. Root: ...

  8. PHP 常见工厂设计模式

    一.工厂模式 是一种类,它具有为您创建对象的某些方法.您可以使用工厂类创建对象,而不直接使用 new.这样,如果您想要更改所创建的对象类型,只需更改该工厂即可.使用该工厂的所有代码会自动更改. 下面代 ...

  9. Go 数据结构--二分查找树

    Go 数据结构--二分查找树 今天开始一个Go实现常见数据结构的系列吧.有时间会更新其他数据结构. 一些概念 二叉树:二叉树是每个节点最多有两个子树的树结构. 完全二叉树:若设二叉树的高度为h,除第 ...

  10. destoon源码分析一

    #0x01 先记录一些之前模糊的小知识点,补充一下 set_magic_quotes_runtime() -- 设置magic_quotes_runtime配置激活状态(php 5.3 被弃用,php ...