• 第一步:进入企业微信,微信扫一扫,创建一个临时测试公司,然后“创建应用”,如下图“述职&转正” 第二步:点击应用,设置可信域名 第三步:设置主页,也就是在企业微信客户端工作台里点击自己...

    第一步:进入企业微信,微信扫一扫,创建一个临时测试公司,然后“创建应用”,如下图“述职&转正”


    第二步:点击应用,设置可信域名



    第三步:设置主页,也就是在企业微信客户端工作台里点击自己应用时的主页,这个流程是:1、企业微信API得到请求 2、企业微信跳转你设置的重定向url并写到code给你 3、进入自身应用的Controller对应方法,获取code+生成的access_token获取用户信息,4、自身应用 return "redirect:/portal/home.html"; 重定向主页即可



    第四步:设置支持二维码,企业微信扫一扫功能



    第五步,完善自身应用

    展开全文
  • 拥有一个企业微信 按照上两篇文章,设定了可信域名 自建应用可信域名 阿里云完成域名解析 使用的是springMVC架构 注意事项; 其他架构可能需要根据架构自身,对本实现环节进行调整 本篇文章,意在指导用户建立完整...

    前提

    1. 拥有一个企业微信
    2. 按照上两篇文章,设定了可信域名
      自建应用可信域名
      阿里云完成域名解析
    3. 使用的是springMVC架构

    注意事项;

    1. 其他架构可能需要根据架构自身,对本实现环节进行调整
    2. 本篇文章,意在指导用户建立完整的企业微信自建应用的调用流程
    3. 所有的细节部分,如果有报错,可能是由于大家对于springMVC的配置不同,单个细节部分完全可以百度实现。

    实现步骤

    微信资源请求流转

    在这里插入图片描述

    设定拦截器

    在配置文件中,设定拦截器拦截内容,需要区分微信和平台端,配置方法可以参考下面的图片
    在这里插入图片描述
    配置的代码段:

    <!-- 配置拦截器 -->
    	<mvc:interceptors>
    		<mvc:interceptor>
    			<mvc:mapping path="/**" />
    			<mvc:exclude-mapping path="/www/**" />
    			<mvc:exclude-mapping path="/viewer/**" />
    			<mvc:exclude-mapping path="/css/**" />
    			<mvc:exclude-mapping path="/error/**" />
    			<mvc:exclude-mapping path="/index/**" />
    			<mvc:exclude-mapping path="/image/**" />
    			<mvc:exclude-mapping path="/initlib/**" />
    			<mvc:exclude-mapping path="/js/**" />
    			<mvc:exclude-mapping path="/weChat/**" />
    			<!-- 配置权限拦截器 如果部定义mappingUrl,则默认拦截所有对controller的请求 可以使用正则表达式对url进行匹配,从而更细粒度的进行拦截(.*/entryOrJsonController\.do\?action=reg.*) -->
    			<bean class="com.pms.system.interceptor.SecurityAndLogInterceptor">
    			   <property name="mappingURL" value="edit|del|add|save|lock|assign|batchAssign"/>
    			</bean> 
    		</mvc:interceptor>
    	</mvc:interceptors>
    	<!-- OAuth2拦截器 -->
    	<mvc:interceptors>
    		<mvc:interceptor>
    			<!--  对所有的请求拦截使用/** ,对某个模块下的请求拦截使用:/myPath/*-->
    			<mvc:mapping path="/weChat/**" />
    			<ref bean="oauth2Interceptor" />
    		</mvc:interceptor>
    	</mvc:interceptors>
    	<bean id="oauth2Interceptor" class="com.hhisp.wechat.interceptor.OAuth2Interceptor">
    	</bean>
    

    编写拦截器代码

    拦截器主要是用于在企业应用内部中,对资源请求进行拦截,防止未登录直接跳转到某一资源下。

    在本示例中,微信的拦截器oauth2Interceptor主要完成微信的token获取,code获取等功能,并且实现页面的跳转。此处相当于一个归口作用,将所有关于微信的请求,都汇总到一个方法中处理登录认证等通用流程,完成认证流程后,再跳转到具体的业务页面进行业务办理。

    package com.hhisp.wechat.interceptor;
    
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.UnsupportedEncodingException;
    import java.lang.reflect.Method;
    
    public class OAuth2Interceptor implements HandlerInterceptor {
    
    	/**
    	 * 在DispatcherServlet完全处理完请求后被调用
    	 * 当有拦截器抛出异常时,会从当前拦截器往回执行所有的拦截器的afterCompletion()
    	 */
    	@Override
    	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
    	}
    
    	/**
    	 * 在业务处理器处理请求执行完成后,生成视图之前执行的动作
    	 */
    	@Override
    	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object arg2, ModelAndView modelAndView) throws Exception {
    
    	}
    
    	/**
    	 * 在业务处理器处理请求之前被调用 如果返回false 从当前的拦截器往回执行所有拦截器的afterCompletion(),再退出拦截器链
    	 * 如果返回true 执行下一个拦截器,直到所有的拦截器都执行完毕 再执行被拦截的Controller 然后进入拦截器链,
    	 * 从最后一个拦截器往回执行所有的postHandle() 接着再从最后一个拦截器往回执行所有的afterCompletion()
    	 */
    	@Override
    	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    		//String url = request.getRequestURL().toString();
    		HttpSession session = request.getSession();
    		
    		// 先判断是否有注解
    		HandlerMethod handlerMethod = (HandlerMethod) handler;
    		Method method = handlerMethod.getMethod();
    		OAuthRequired annotation = method.getAnnotation(OAuthRequired.class);
    		if (annotation != null) {
    			System.out.println("session:"+session.toString());
    			Object objUid = session.getAttribute("weChatId");
    			if (objUid == null) {
    				String resultUrl = request.getRequestURL().toString();
    				String param=request.getQueryString();
    				if(param!=null){
    					resultUrl+= "?" + param;
    				}
    				try {
    					resultUrl = java.net.URLEncoder.encode(resultUrl, "utf-8");
    				} catch (UnsupportedEncodingException e) {
    					e.printStackTrace();
    				}
    				//请求的路径
    		        String contextPath=request.getContextPath();
    				System.out.println("contextPath !"+contextPath);
    				System.out.println("resultUrl !"+resultUrl);
    				System.out.println("all!"+contextPath + "/weChat/oauth2/oauth2.do?resultUrl=" + resultUrl);
    				response.sendRedirect(contextPath + "/weChat/oauth2/oauth2.do?resultUrl=" + resultUrl);
    				return false;
    			}
    
    		}
    		return true;
    	}
    
    }
    
    

    代码详解:
    整篇代码中,要实现的功能就是最后一句话,相当于将所有wechat请求的信息,都重定向到微信认证接口,进行微信认证。
    在这里插入图片描述

    添加微信认证的相关类和函数

    1. 刚才我们在拦截器中,重定向了一个url指向后台的oauth2.do方法
    2. 在该方法中,我们需要向微信官方的链接,发送获取用户微信Code的请求,并且将获取code后重定向的url提交给微信官方链接。
      具体代码如下:
    import com.hhisp.wechat.pojo.AccessToken;
    import com.hhisp.wechat.utils.Constants;
    import com.hhisp.wechat.utils.QiYeUtil;
    import com.hhisp.wechat.utils.Result;
    import org.apache.commons.lang3.StringUtils;
    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;
    
    @Controller
    @RequestMapping("/weChat/oauth2")
    public class OAuth2Controller {
    	
    	/**
    	 * 构造参数并将请求重定向到微信API获取登录信息
    	 * 
    	 * @param resultUrl
    	 * @return
    	 */
    	@RequestMapping(value = { "/oauth2.do", "/oauth2" })
    	public String Oauth2API(HttpServletRequest request, @RequestParam String resultUrl) {
    		// 此处可以添加获取持久化的数据,如企业号id等相关信息
    		String CropId = 自有企业微信ID;
    		String redirectUrl = "";
    		if (resultUrl != null) {
    			String backUrl = Constants.BEFORE_URL + "/weChat/oauth2/oauth2url.do?oauth2url=" + resultUrl;
    			redirectUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + corpid + "&redirect_uri=" + redirect_uri
    				+ "&response_type=code&scope=snsapi_base&state=sunlight#wechat_redirect";;
    		}
    		return "redirect:" + redirectUrl;
    	}
    }
    

    获取微信账号

    上面,我们向微信官方提交了一个请求code的链接,并且告诉微信官方,我们获取了code后,需要定向的目的url是什么

    接下来,在这个url的处理函数中,需要获取微信官方返回的code,并且根据code,处理相关的业务逻辑,从而跳转到具体的业务页面

    	/**
    	 * 根据code获取Userid后跳转到需要带用户信息的最终页面
    	 * 
    	 * @param request
    	 * @param code
    	 *            获取微信重定向到自己设置的URL中code参数
    	 * @param oauth2url
    	 *            跳转到最终页面的地址
    	 * @return
    	 */
    	@RequestMapping(value = { "/oauth2url.do" })
    	public String Oauth2MeUrl(HttpServletRequest request, @RequestParam String code, @RequestParam String oauth2url,String activityId) {
    		AccessToken accessToken = QiYeUtil.getAccessToken(Constants.CORPID,Constants.SECRET);
    		HttpSession session = request.getSession();
    		if (accessToken != null && accessToken.getToken() != null) {
    			String weChatId = getMemberGuidByCode(accessToken.getToken(), code, Constants.AGENTID);
    			if (weChatId != null) {
    				session.setAttribute("weChatId", weChatId);
    				System.out.println("weChatId"+weChatId);
    				System.out.println("session after set wid"+session.toString());
    			}
    		}
    		if(StringUtils.isNotEmpty(activityId)){
    			return "redirect:" + oauth2url+ "&activityId=" + activityId;
    		}
    		// 这里简单处理,存储到session中
    		return "redirect:" + oauth2url;
    	}
    		/**
    	 * 调用接口获取用户信息
    	 * 
    	 * @param token
    	 * @param code
    	 * @param agentId
    	 * @return
    	 */
    	public String getMemberGuidByCode(String token, String code, int agentId) {
    		Result<String> result = QiYeUtil.oAuth2GetUserByCode(token, code, agentId);
    		if (result.getErrcode() == "0") {
    			if (result.getObj() != null) {
    				// 此处可以通过微信授权用code获取的Userid查询自己本地服务器中的数据
    				return result.getObj();
    			}
    		}
    		return "";
    	}
    

    获取code后的步骤解析:

    1. 带code跳转中,code在request参数中可以获取,但是这个code不是用户编号,是分配的一个临时编号;
    2. 在方法中,getMemberGuidByCode函数,主要就是通过token以及code,获取实际的用户信息的过程。
    3. 关于具体的获取token,以及通过code获取userId的实现方式,网上一搜一大把,临时代码封装的不太好,我就不嫌丑了。
    4. 截止到此,代码端的工作就完成了。

    企业微信的后台配置以及实现效果

    1. 企业微信添加自建应用
      在这里插入图片描述
    2. 自建应用中增加自定义菜单
      在这里插入图片描述
    3. 自定义菜单中配置按钮的url
      在这里插入图片描述
    4. 在企业微信中,点击自建应用下的按钮
      在这里插入图片描述
    5. 查看结果
      在这里插入图片描述
    展开全文
  • 企业微信开发

    2018-05-10 15:19:32
    企业微信的认识企业微信概念:企业微信2016年4月18日,腾讯正式发布全平台企业办公工具“企业微信”。与微信一致的沟通体验,为企业员工提供最基础和最实用的办公服务,并加入贴合办公场景的特色功能、轻OA工具,提供...

    企业微信的认识

    1. 企业微信概念:
      企业微信2016年4月18日,腾讯正式发布全平台企业办公工具“企业微信”。与微信一致的沟通体验,为企业员工提供最基础和最实用的办公服务,并加入贴合办公场景的特色功能、轻OA工具,提供了通讯录管理、应用管理、消息推送、身份验证、移动端SDK、素材、OA数据接口、企业支付、电子发票等API,管理员可以使用这些API,为企业接入更多个性化的办公应用。
    2. 企业微信与微信企业号区别:

      其实两个产品最大的其别就是微信企业号是基于微信,而企业微信是一个独立app。企业微信,倾向于将工作和生活完全分开,以独立app的形式去使用,更有着丰富的办公应用,如预设打卡、审批、日报、公告等OA应用,如果你对这些应用不满足,还可以通过API接入和第三方应用满足更多个性需求。
      区别官方说明:http://kf.qq.com/faq/170104mEjqUV1701043mAR32.html
      有一种说法: 微信企业号要合并到企业微信,然后慢慢淡化微信企业号的概念。

    企业微信的功能

    企业微信有什么用,介绍企业微信6个强大的功能:https://blog.csdn.net/towtotow/article/details/78920572

    1. 三大产品功能:

      预设轻量OA应用:包括打卡、审批、日报、公告、同事吧等,还有丰富的API接口方便接入更多企业应用。
      企微云OA : http://wbg.do1.com.cn/oa/?utm_campaign=wbg&utm_medium=&utm_source=index
      集成多种通讯方式:有由公司统一付费的公费电话、高清稳定的视频会议、和会话紧密结合的企业邮箱,同步提高沟通效率。
      企微与CRM:http://wbg.do1.com.cn/crm/?utm_campaign=wbg&utm_medium=&utm_source=index
      微信云HR功能:通过人事管理、培训、考试、考勤、工资条等应用,帮助企业HR实现全员工全任职周期的一站式人事管理,有效的提升HR管理效能、降低人事成本。
      企微云HR:http://wbg.do1.com.cn/hr/?utm_campaign=wbg&utm_medium=&utm_source=index
    2. 齐全应用功能:

      功能全面集成至微信及企业微信,30多款内嵌应用,快速搭建办公平台。
      应用介绍:http://wbg.do1.com.cn/apply/superform/?utm_campaign=wbg&utm_medium=&utm_source=index

    企业微信的开发

    1. 官方API文档:

    企业微信 API文档:https://work.weixin.qq.com/api/doc

    2. 企业微信开发思路:

    企业微信的开发大体可分为以下几步:
    (1)封装实体类
    参考官方文档给出的请求包、回包(即响应包),封装对应的java实体类。
    (2)java对象的序列化
    将java对象序列化为json格式的字符串
    (3)获取AccessToken,拼接请求接口url
    凭证的获取方式有两种(此处暂时存疑,以待勘误):
    通讯录AccessToken:CorpId+通讯录密钥
    其他AccessToken:CorpId+应用密钥
    (4)调用接口发送http请求
    封装好http请求方法:httpRequest(请求url, 请求方法POST/GET, 请求包);

    3.开始开发:

    开发时请留意企业微信与企业号的接口差异:https://work.weixin.qq.com/api/doc#12060
    (1) Quick Start
    https://github.com/Wechat-Group/weixin-java-tools/wiki/CP_Quick-Start
    (2) 微信消息路由器
    https://github.com/Wechat-Group/weixin-java-tools/wiki/CP_微信消息路由器
    (3)WxCpConfigStorage
    https://github.com/Wechat-Group/weixin-java-tools/wiki/CP_WxCpConfigStorage
    (4)同步回复消息
    https://github.com/Wechat-Group/weixin-java-tools/wiki/CP_同步回复消息
    (5)刷新access_token
    https://github.com/Wechat-Group/weixin-java-tools/wiki/CP_刷新access_token
    (6)用户身份二次验证
    https://github.com/Wechat-Group/weixin-java-tools/wiki/CP_用户身份二次验证
    (7)主动发送消息
    https://github.com/Wechat-Group/weixin-java-tools/wiki/CP_主动发送消息
    (8)临时素材(多媒体文件)管理
    https://github.com/Wechat-Group/weixin-java-tools/wiki/CP_多媒体文件管理
    (9) 用户管理
    https://github.com/Wechat-Group/weixin-java-tools/wiki/CP_用户管理
    (10)部门管理
    https://github.com/Wechat-Group/weixin-java-tools/wiki/CP_部门管理
    (11)标签管理
    https://github.com/Wechat-Group/weixin-java-tools/wiki/CP_标签管理
    (12)自定义菜单管理
    https://github.com/Wechat-Group/weixin-java-tools/wiki/CP_自定义菜单管理
    (13)OAuth2网页授权
    https://github.com/Wechat-Group/weixin-java-tools/wiki/CP_OAuth2网页授权
    (14)http代理支持
    https://github.com/Wechat-Group/weixin-java-tools/wiki/CP_http代理支持
    (15)如何调用未支持的接口
    https://github.com/Wechat-Group/weixin-java-tools/wiki/CP_如何调用未支持的接口
    (16)如何执行本项目单元测试
    https://github.com/Wechat-Group/weixin-java-tools/wiki/CP_如何执行本项目单元测试

    可以运行 demo-1 的代码来对weixin-java-tools的有一个更好的了解。
    项目demo-1: https://github.com/Wechat-Group/weixin-java-tools
    启动方式: https://github.com/Wechat-Group/weixin-java-tools/wiki/CP_demo代码

    4. 可能遇到的异常:

    异常整理引用博客园作者整理
    企业微信开发整理:http://www.cnblogs.com/shirui/category/1053578.html
    参考demo-2:https://github.com/shirayner/WeiXin_QiYe_Demo/tree/master

    (1)异常:“errcode”:60011,“errmsg”:"no privilege to access/modify contact/party/agent "处理;

    组装json数据时,有些字段的值是不能包含双引号的

           {
               "userid": "zhangsan",
                "name": "张三",
               "english_name": "jackzhang"
               "department": [1, 2],
            }
    

    比如department。所以就没有用JSONObject.fromObject方法了,直接用字符串按照格式自己拼装了。
    链接:http://www.cnblogs.com/shirui/p/7367902.html

    (2)异常:"java.security.InvalidKeyException: Illegal key size "处理;

    不合法秘钥长度异常,原因是jdk安装时自带的JCE策略文件时受限的, 需要将其替换为 JCE无限制权限策略文件。

    java.security.InvalidKeyException: Illegal key size
    

    链接:http://www.cnblogs.com/shirui/p/7411735.html

    (3)异常:未验证域名归属,JS-SDK功能受限的处理;

    https://images2017.cnblogs.com/blog/748623/201711/748623-20171108230927559-1276532792.png

    处理方案:将下载的文件放到域名根目录下,勾选“已上传域名归属校验文件”,点击确认即可。
    https://images2017.cnblogs.com/blog/748623/201711/748623-20171108231022153-231240638.png
    链接:http://www.cnblogs.com/shirui/p/7806888.html

    (4)异常: "java.net.UnknownHostException: qyapi.weixin.qq.com“处理;

    网络原因导致 dns解析失败。 网络原因导致 dns解析失败。

    解决方案链接:http://www.cnblogs.com/shirui/p/8023005.html

    (5)异常:"java.lang.OutOfMemoryError: Java heap space“处理;

    java.lang.OutOfMemoryError: Java heap space
    

    解决方案链接:http://www.cnblogs.com/shirui/p/8399947.html

    (6)异常: "com.qq.weixin.mp.aes.AesException“处理;

    解密后得到的buffer非法

    方法:POST@ echostr是否存在 :false
    java.lang.IllegalArgumentException: 20 > -367029533
        at java.util.Arrays.copyOfRange(Arrays.java:3519)
        at com.qq.weixin.mp.aes.WXBizMsgCrypt.decrypt(WXBizMsgCrypt.java:181)
        at com.qq.weixin.mp.aes.WXBizMsgCrypt.DecryptMsg(WXBizMsgCrypt.java:263)
        .......
    

    解决方案链接:http://www.cnblogs.com/shirui/p/8513684.html

    Dedo整理

    1.可能是目前最好最全的微信Java开发工具包(SDK)

    包括微信支付、开放平台、公众号、企业微信、企业号、小程序等
    Github链接:demo-1:https://github.com/Wechat-Group/weixin-java-tools

    2.企业微信开发Dome:纯java+servlet实现,以后会尝试用ssm框架来实现。

    Github链接:demo-2:https://github.com/shirayner/WeiXin_QiYe_Demo/tree/master

    3.企业微信QuickStart扩展Dome:基于Spring Boot构建,实现微信企业号开发功能。

    Github链接:https://github.com/binarywang/weixin-java-cp-demo

    展开全文
  • 手把手教你springboot企业微信开发(二)1、企业微信开发第一步2、weixin-java-cp-demo-master1)、引入thymeleaf改造项目2)增加Configuration3)回到企业号4)穿透内外网5)完善 从这一篇开始,开始从实际项目探索...


    从这一篇开始,开始从实际项目探索企业微信开发。

    1、企业微信开发第一步

    看一下企业微信的开发者文档: 企业微信开发者文档.

    在开发文档的首页中,我们看到:企业&定制化服务商开发流程

    1. 获取企业号CorpID&Secret: 企业管理员建立管理组,获取CorpID&Secret
    2. 开发对接相关接口: 开发测试应用,对接企业号接口,接口文档qydev.weixin.qq.com

    企业号的CorpID和Secret,在上一篇已经讲解了,这里不赘述。对接接口的内容,在后面也会使用,暂时按下不表。

    看【主动调用】,文档中有个醒目的获取AccessToken而且备注是:你应该审慎配置管理组的权限,够用即好,权限过大会增加误操作可能性及信息安全隐患。对于我们开发者而言:做好缓存、注意有时间限制7200s。下面还说频率的问题等等,这个也做到心中有数吧。

    我们先把关注点集中在获取access_token上。在项目中,使用get请求获取access_token,请求地址为:https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=id&corpsecret=secrect

    【回调模式】在开发中用的也很多,使用的时候按照文档来吧。

    我在一个项目中,写了一个微信开发的WeixinUtil,java写的未必那么完美。它的作用就是基于主动调用的,来看一下,这里一个推送的实现:

    /**
    	 * 企业号推送文章
    	 */
    sentPushMsg(List<String> userIds , PushMsgEntity pushMsg) {
    		String appid = sysConfigService.getWeixinAppid();
        	String secret = sysConfigService.getWeixinSecret();
    		String agentId = sysConfigService.getParamValue("agentId");
    		
    		String qytoken = apiRedis.getWeAccessToken(appid); //从缓存中获取token
    		if(qytoken == null) {
    			TokenResponse tokenResponse = WeixinUtil.token(appid,secret);
    			try {
    				qytoken = tokenResponse.getAccessToken();
    				apiRedis.setWeAccessToken(appid , qytoken);
    			}catch(Exception e) {
    				return R.error(ErrorCode.WEIXIN_API_GET_TOKEN_ERROR, "微信获取AccessToken失败,请重新获取!");
    			}
    		}
    		MessageCustomArticle article = new MessageCustomArticle();
    		
    		article.setTitle(pushMsg.getTitle());
    		article.setDescription(pushMsg.getMessage());
    		article.setUrl(pushMsg.getUrl());
    		article.setPicurl(pushMsg.getPicurl());
    		
    		MessageCustomSendRequest  messageCustomSendRequest  = WeixinUtil.getMessageCustomSendRequest(userIds , article);
    		if(!StringUtil.isBlank(agentId)) {
    			messageCustomSendRequest.setAgentid(new Integer(agentId));
    		}
    		
    		Response res = WeixinUtil.messageCustomSend(qytoken , messageCustomSendRequest);
    		if(res == null) {
    			//....略去了业务代码
    			return R.error( ErrorCode.SENT_TEMPLATE_MSG_ERROR, "消息发送失败");
    		}else {
    			//....略去了业务代码
    			return R.ok(res.getErrMsg()).put("code", res.getErrCode());
    		}
    		
    	}
    

    大家在自己的项目中,也一定会有自己封装的方式。

    接着上一篇,这里用的是什么呢?我在上一篇里说到,引入weixin-java-cp的Jar包了,这个jar包有非常强悍的作用。

    2、weixin-java-cp-demo-master

    1)、引入thymeleaf改造项目

    在pom.xml中引入spring-boot-starter-thymeleaf

    		<dependency>
    		    <groupId>org.springframework.boot</groupId>
    		    <artifactId>spring-boot-starter-thymeleaf</artifactId>
    		</dependency>
    

    引入之后,application.yml中配置解析html:

    spring:
      thymeleaf:
        prefix: classpath:/templates/
        suffix: .html
        mode: HTML5
        encoding: UTF-8
        servlet:
            content-type: text/html
        cache: false
    

    稍微解释一下,前缀prefix是地址,工程的相对路径,这边是404错误的根据地了吧,哈哈哈,得注意编译和请求得路径啊;后缀suffix需要spring解析的模板后缀,这里是html。mode、encoding、wevlet、cache就不说了哦。

    在thymeleaf目录下,新建一个文件夹,thymeleaf,在文件夹中,新建一个first.html

    <!DOCTYPE HTML>
    <html xmlns:th="http://www.thymeleaf.org">
    <head>
        <title>first</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    </head>
    <body>
    <p th:text="'Url:' + ${Url}"/>
    <p th:text="'Error:' + ${Error}"/>
    <p th:text="'Message:' + ${Message}"/>
    <p th:text="'Exception:' + ${Exception}"/>
    <p th:text="'Path:' + ${Path}"/>
    <p th:text="'Status:' + ${Status}"/>
    <p th:text="'Timestamp:' + ${Timestamp}"/>
    </body>
    </html>
    

    看一下文件目录结构:

    在这里插入图片描述
    新建一个ThemleafController.java,内容是:

    package com.github.binarywang.demo.wx.cp.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    @RequestMapping("/thymeleaf")
    public class ThemleafController {
    
    	  @GetMapping(value = "/first")
    	  public String first(Model model) {
    		model.addAttribute("Url", "thymeleaf/first");
    		model.addAttribute("Error", "0");
    		model.addAttribute("Path", "");
    		model.addAttribute("Message", "");
    		model.addAttribute("Exception", "");
    		model.addAttribute("Status", "");
    		model.addAttribute("Timestamp", System.currentTimeMillis());
    	    return "/thymeleaf/first";
    	  }
    
    }
    

    改一手application.yml

    server:
      port: 8080
    

    debug模式启动WxCpDemoApplication.java
    在浏览器中输入http://localhost:8080/thymeleaf/first

    在这里插入图片描述
    出来它就可以了。

    2)增加Configuration

    springboot在很多配置项简洁得让人摸不着头脑,这里是基于springboot不得不说的就是这个,先上代码,然后解释:

    package com.github.binarywang.demo.wx.cp.config;
    
    import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    public class WebMvcConfig implements WebMvcConfigurer{
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
        }
    }
    

    这个Java文件的意思是以静态资源的形式访问/static/文件夹下的所有文件。
    /static/**使用了**作为通配符。但是,并没有static文件夹,对吗?建立一个static文件夹,看一下项目结构:
    在这里插入图片描述

    在里面放个txt文件文件名叫who_love_who.txt,文件里面写上自己喜欢的人的名字,文件名的话用我们的代码来试试看TA对你是否有意,哈哈哈。我的文件里面写的是:天赐我爱-love-天赐我爱。写好了测试文件,现在的项目结构是:
    在这里插入图片描述

    重新debug
    WxCpDemoApplication.java
    启动好了之后,在浏览器输入:
    http://localhost:8080/who_love_who.txt
    我这边的是:
    在这里插入图片描述
    看到是乱码,我就放心了,她是爱谁谁,与我这单身技术狗无关!差不多了哦!配置起作用了。

    3)回到企业号

    回到企业号。找到上篇手把手教你springboot企业微信开发(一)中的应用配置页:
    在这里插入图片描述
    拉到下面:
    【开发者接口】–》点开【网页授权及JS-SDK】
    在这里插入图片描述
    点,【 下载文件】的链接,下载了一个txt文件吧?好!这个文件,放到我之前说的那个static文件夹!现在的static文件夹:
    在这里插入图片描述
    是否疑问,我在大费周章在搞thymeleaf、txt静态资源是为了什么呢?下面细细说这个。

    4)穿透内外网

    访问链接: https://natapp.cn/login.
    注册~~~购买,不不不申请免费隧道:
    在这里插入图片描述
    天底下最好的东西,一定的免费的,阳光是免费的、空气是免费的,爱也是免费的。点开刚刚申请的隧道:
    在这里插入图片描述
    端口用个8080;authtoken复制备用。下载客户端:
    在这里插入图片描述
    自己选择吧。
    下载完了,自己创建一个文件夹,就叫natapp得了,在里面放个文件,文件名config.ini在里面的配置:

    #将本文件放置于natapp同级目录 程序将读取 [default] 段
    #在命令行参数模式如 natapp -authtoken=xxx 等相同参数将会覆盖掉此配置
    #命令行参数 -config= 可以指定任意config.ini文件
    [default]
    authtoken=myauthtoken                     #对应一条隧道的authtoken
    clienttoken=                    #对应客户端的clienttoken,将会忽略authtoken,若无请留空,
    log=none                        #log 日志文件,可指定本地文件, none=不做记录,stdout=直接屏幕输出 ,默认为none
    loglevel=ERROR                  #日志等级 DEBUG, INFO, WARNING, ERROR 默认为 DEBUG
    http_proxy=                     #代理设置 如 http://10.123.10.10:3128 非代理上网用户请务必留空
    

    刚才我说复制的authtoken,粘贴在这里。打开natapp.exe有木有什么?
    在这里插入图片描述
    有类似这个吗?如果是那行的意思是:现在这个域名,已经穿透到了你本地的8080端口。
    好,把我画线的部分复制到浏览器:
    是不是有这样的画面:
    在这里插入图片描述
    有的话,说明穿透成功了。把http://域名/thymeleaf/first复制下来,编码:
    链接: url编码.把编码后的这段复制备用。把域名(不带http://的)复制下来备用。

    5)完善

    在pom.xml中引入jar包:

    		<dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.45</version>
            </dependency>
    

    引入这个fastjson是为了解析返回的字符串。

    自动注入:

    @Autowired
    WxCpProperties wxCpProperties;
    

    修改first方法:

     @GetMapping(value = "/first")
    	  public String first(ServletRequest request, ServletResponse response, Model model) {
    		  
    		  HttpServletRequest servletRequest = ((HttpServletRequest) request);
    		  String userAgent = servletRequest.getHeader("user-agent");
    		  
    		  final WxCpService wxCpService = WxCpConfiguration
    					.getCpService(wxCpProperties.getAppConfigs().get(0).getAgentId());
    
    			
    			String queryString = servletRequest.getQueryString();
    			String backString = "";
    			try {
    				backString = HttpRequest.sendGet("https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token="
    						+ wxCpService.getAccessToken() + "&" + queryString, null);
    			} catch (WxErrorException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			JSONObject json_test = JSONObject.parseObject(backString);  
    		  
    		model.addAttribute("Url", "thymeleaf/first");
    		model.addAttribute("Error", "0");
    		model.addAttribute("Path", "");
    		model.addAttribute("Message", "");
    		model.addAttribute("Exception", "");
    		model.addAttribute("Status", "");
    		model.addAttribute("Timestamp", System.currentTimeMillis());
    	    return "/thymeleaf/first";
    	  }
    

    这里用的HttpRequest.java是我自己写的:

    package com.github.binarywang.demo.wx.cp.utils;
    
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.net.URL;
    import java.net.URLConnection;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    import javax.net.ssl.HostnameVerifier;
    import javax.net.ssl.HttpsURLConnection;
    import javax.net.ssl.SSLSession;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class HttpRequest {
    	
    	public static Logger logger = LoggerFactory.getLogger(HttpRequest.class);
    	
    	/**
    	 * 向指定URL发送GET方法的请求
    	 * 
    	 * @param url
    	 *            发送请求的URL
    	 * @param param
    	 *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
    	 * @return URL 所代表远程资源的响应结果
    	 */
    	public static String sendGet(String url, Map params) {
    		return sendGet(url,"", params);
    	}
    	/**
    	 * 向指定URL发送GET方法的请求
    	 * 
    	 * @param url
    	 *            发送请求的URL
    	 * @param param
    	 *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
    	 * @return URL 所代表远程资源的响应结果
    	 */
    	public static String sendGet(String url,String interfaceName, Map params) {
    		String result = "";
    		BufferedReader in = null;
    		StringBuffer strBufParamsSet = new StringBuffer();
    		
    		if(params!=null) {
    			Set<String> paramsSet = params.keySet();
    			
    			for(String parma:paramsSet) {
    				if(strBufParamsSet.length()>0) {
    					strBufParamsSet.append("&").append(parma).append("=").append(params.get(parma));
    				}else {
    					strBufParamsSet.append("?").append(parma).append("=").append(params.get(parma));
    				}
    			}
    		}else {
    			strBufParamsSet.append("");
    		}
    		
    		
    		try {
    			String urlNameString = "";
    			if(interfaceName==null || "".contains(interfaceName.trim())) {
    				urlNameString = url + strBufParamsSet.toString();
    			}else {
    				urlNameString = url +"/"+ interfaceName + strBufParamsSet.toString();
    			}
    			urlNameString = urlNameString.replaceAll("\r\n", "");
    			trustAllHttpsCertificates();
    			logger.info("请求路径:" + urlNameString);
    			URL realUrl = new URL(urlNameString);
    			// 打开和URL之间的连接
    			URLConnection connection = realUrl.openConnection();
    			// 设置通用的请求属性
    			connection.setRequestProperty("accept", "*/*");
    			connection.setRequestProperty("connection", "Keep-Alive");
    			connection.setRequestProperty("Accept-Charset", "UTF-8");
    			connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
    			// 建立实际的连接
    			connection.connect();
    			// 获取所有响应头字段
    			Map<String, List<String>> map = connection.getHeaderFields();
    //			// 遍历所有的响应头字段
    //			for (String key : map.keySet()) {
    //				System.out.println(key + "--->" + map.get(key));
    //			}
    			// 定义 BufferedReader输入流来读取URL的响应
    			in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
    			String line;
    			while ((line = in.readLine()) != null) {
    				result += line;
    			}
    		} catch (Exception e) {
    			logger.debug("发送 get请求出现异常!获取文件流失败:" + e);
    			
    		}
    		// 使用finally块来关闭输入流
    		finally {
    			try {
    				if (in != null) {
    					in.close();
    				}
    			} catch (Exception e2) {
    				logger.debug("发送 get请求出现异常!获取文件流失败:" + e2);
    			}
    		}
    		return result;
    	}
    
    	
    
    	private static void trustAllHttpsCertificates() throws Exception {  
            javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];  
            javax.net.ssl.TrustManager tm = new miTM();  
            trustAllCerts[0] = tm;  
            javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext  
                    .getInstance("SSL");  
            sc.init(null, trustAllCerts, null);  
            javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc  
    				                .getSocketFactory());
    
    		HostnameVerifier hv = new HostnameVerifier() {
    			public boolean verify(String urlHostName, SSLSession session) {
    				System.out.println("Warning: URL Host: " + urlHostName + " vs. "
    								   + session.getPeerHost());
    				return true;
    			}
        	};
    		HttpsURLConnection.setDefaultHostnameVerifier(hv);
        }  
    			  
        static class miTM implements javax.net.ssl.TrustManager,
    			            javax.net.ssl.X509TrustManager {  
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {  
                return null;  
            }  
      
            public boolean isServerTrusted(java.security.cert.X509Certificate[] certs) {
                return true;  
            }  
      
            public boolean isClientTrusted(java.security.cert.X509Certificate[] certs) {
                return true;  
            }  
      
            public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
                    throws java.security.cert.CertificateException {  
                return;  
            }  
      
            public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
                    throws java.security.cert.CertificateException {  
                return;  
            }  
        }  
    }
    

    到了这一步,把
    再回企业微信应用配置的页面,复制这个:

    https://open.weixin.qq.com/connect/oauth2/authorize?appid=appIdf&redirect_uri=编码后的域名&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
    复制到:【工作台应用主页】里面去。
    复制域名到【网页授权及JS-SDK】:
    在这里插入图片描述
    勾选【已上传域名归属校验文件】,【确定】!
    点:【企业微信授权登录】已启用–》Web网页–》
    在这里插入图片描述
    编辑填入域名
    好了之后,debug重启:WxCpDemoApplication.java并且在这行打断点:
    在这里插入图片描述

    在企业微信中打开:
    在这里插入图片描述
    点进去自己的应用,如果进断点,看backString,是不是与我类似:
    在这里插入图片描述
    如果看到返回值与我类似的,恭喜,已经完成了!!!如果有疑问或者需要讨论,或者报错的不好解决的话,给我留言即可。
    附上我这边最终的画面:
    在这里插入图片描述
    另外,稍微看一下natapp的情况,那里有请求的情况:
    在这里插入图片描述
    好了,这一篇也结束了。

    展开全文
  • 企业微信开发Demo

    2020-06-26 08:55:38
    企业微信 开发 实例 公众平台开发 Demo
  • 手把手教你springboot企业微信开发(一)1、 配置企业号2、Springboot配置企业号 1、 配置企业号 企业号登录、设置,网址是: https://work.weixin.qq.com/wework_admin/loginpage_wx 微信扫码登录: 选一个,进入...

    手把手教你springboot企业微信开发(一)

    1、 配置企业号

    企业号登录、设置,网址是:
    https://work.weixin.qq.com/wework_admin/loginpage_wx
    微信扫码登录:
    企业号登录
    选一个,进入企业号,点【我的企业】进入:
    在这里插入图片描述
    点左上角我的企业:

    设置里,有企业ID复制,到一边备用,这是cropId,在后面会有用的。
    下一步点【应用管理】
    在这里插入图片描述
    在这里插入图片描述
    拉到下面,看【自建】

    点【创建应用】,填写:

    在这里插入图片描述
    在这里插入图片描述
    可见范围选好了哈,没选自己的话在企业号里看不到应用。
    配置好了之后,点【创建应用】,看到下面的页面:
    在这里插入图片描述
    复制AgentId,和secret至一边,后面备用。现在我们的备用有哪些啦?crop_id,agentId,secret对吧?

    到此,企业号配置,暂时告一段落,我们先转到springboot。

    2、Springboot配置企业号

    Springboot概述…………略
    这里就不细说springboot了,以后看看有没有时间写吧。

    先看一个开源的demo吧:https://github.com/binarywang/weixin-java-cp-demo
    微信企业号中,有很多细节问题,这里不一一赘述,如果有疑问,可以在评论区,我看到之后一起讨论。

    把这个项目拉下来。这里先说一下,要使用lombok插件,下载地址:
    https://projectlombok.org/download打开安装到eclipse。要安装一下哦,要不然会报错的。

    言归正传,从github上拉下来weixin-java-cp-demo之后,打开eclipse,import:
    在这里插入图片描述

    要用maven导入。对maven有疑问?

    导入~

    导入成功之后目录结构应该类似这个(红框中的两个yml文件除外):

    在这里插入图片描述

    将application.yml.template的.template去掉,打开yml文件:
    在这里插入图片描述

    这里,还记得我前面让复制下来备用的吗?
    Token、aeskey暂且分别填个111;corpId、agentId、secret粘贴到这里。

    我这边多添加了个yml文件是application-dev.yml,这里不赘述配置文件的细节。

    打开pom.xml文件,引入jar包:

      <dependency>
                <groupId>com.github.binarywang</groupId>
                <artifactId>weixin-java-cp</artifactId>
                <version>3.7.0</version>
            </dependency>
    

    新建一个java文件试试配置有没有效果,名称是:WxPushMessageController,内容是:

    package com.github.binarywang.demo.wx.cp.controller;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.github.binarywang.demo.wx.cp.config.WxCpConfiguration;
    import com.github.binarywang.demo.wx.cp.config.WxCpProperties;
    
    import me.chanjar.weixin.common.error.WxErrorException;
    import me.chanjar.weixin.cp.api.WxCpService;
    import me.chanjar.weixin.cp.bean.WxCpMessage;
    import me.chanjar.weixin.cp.bean.WxCpMessageSendResult;
    @RestController
    @RequestMapping("/wx/cp/push")
    public class WxPushMessageController {
    
    	private final Logger logger = LoggerFactory.getLogger(this.getClass());
    	
    	@Autowired
    	WxCpProperties wxCpProperties;
    	
    	@GetMapping("/touser")
    	public Map pushToUsers(@RequestParam(value="user") String user , @RequestParam(value="content") String content , @RequestParam(value="index") int index) {
    		
    		Map returnMap = new HashMap();
    		returnMap.put("code", "100");
    		
    		int agentId = wxCpProperties.getAppConfigs().get(index).getAgentId();
    		final WxCpService wxCpService = WxCpConfiguration.getCpService(agentId);
    		
    		WxCpMessage message = WxCpMessage
    		  .TEXT()
    		  .agentId(agentId) // 企业号应用ID
    		  .toUser(user)
    //		  .toParty("非必填,PartyID列表,多个接受者用‘|’分隔。当touser为@all时忽略本参数")
    //		  .toTag("非必填,TagID列表,多个接受者用‘|’分隔。当touser为@all时忽略本参数")
    		  .content(content)
    		  .build();
    		
    		this.logger.info("\n接收到来自微信服务器的认证消息:echostr = [{}]" , message.getContent());
    
    		WxCpMessageSendResult wxCpMessageSendResult = new WxCpMessageSendResult();
    		try {
    			wxCpMessageSendResult = wxCpService.messageSend(message);
    		} catch (WxErrorException e) {
    			
    			e.printStackTrace();
    			this.logger.info("\n---------------------------------> 报错cause: [{}]" , e.getCause());
    			this.logger.info("\n---------------------------------> 报错message: [{}]" , e.getMessage());
    			returnMap.put("code", "400");
    			returnMap.put("msg", "推送失败,请联系管理员。");
    			return returnMap;
    		}
    		
    		if(wxCpMessageSendResult.getErrCode()==0) {
    			returnMap.put("code", "0");
    			returnMap.put("msg", "推送成功。");
    			return returnMap;
    		}else {
    			returnMap.put("code", "500");
    			returnMap.put("msg", "推送失败,请联系管理员。");
    			return returnMap;
    		}
    		
    	}
    	
    }
    
    

    这个是实现能发个推送消息。
    简单解释一下:

    @Autowired
    WxCpProperties wxCpProperties;
    

    注入的是配置信息,wxCpService.messageSend(message);是调用发送消息。完成之后,可以自己调试一下,先体会体会。
    调试启动:WxCpDemoApplication.java
    看到下面的日志,说明已经成功了:
    在这里插入图片描述

    在浏览器里输入:
    http://localhost:8000/wx/cp/push/touser?user=XXXXX&content=%E6%B5%8B%E8%AF%95%E6%B6%88%E6%81%AF%E8%BF%99%E6%98%AF&index=0

    这里的user留意一下,是企业号的userId。在浏览器中出现:
    在这里插入图片描述
    恭喜你,已经成功的迈出了第一步了。转载的小伙伴,注一下出处,谢谢你们。有任何问题,在评论区留言,我们一起讨论。下一篇博文我与大家一道再接再厉!

    展开全文
  • SpringCloud集成微信公众号开发 步骤: 1.引入依赖 错误方式: <dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-mp</artifactId> <...
  • 微信开发实例,实现了大多数接口。可以直接用,也可以学习和研究,与君共勉!
  • 手把手教你springboot企业微信开发(四)js-sdk结语 js-sdk 按照企业微信官方给的做法来链接是...
  • 公司在开发企业微信的时候,发现企业微信的api的调用可以做成轮子,这里贡献出来, 配置好企业微信账号,可以直接用,方便开发。 主要是做了几个简单处理, 统一处理每个请求的accesstoken携带 统一处理accesstoken...
  • spring boot 微信网页授权的实现 按照官方文档步骤,网页授权分为四步。这里不再累述,官方文档传送门:微信网页授权 1 - 获取用户授权,获取code @RequestMapping(value = "/login", method = RequestMethod.GET)...
  • 上一章讲解了如何用QQ实现登录,这一回讲解的是用微信实现登录。 实现功能 实现微信登录。 开发步骤 引入jar包 jar包的引入和QQ引入的jar包一致。 微信实体信息 /** * @author lvhaibao * @description * @date ...
  • springboot开发微信环境搭建 需要用到IntelliJ IDEA、微信公号测试平台、springboot、内网穿透(本地开发比较方便,这里我用的是小米球内网穿透) 请自行建立springboot项目 首先我们需要登录到微信测试开发平台,...
  • 第1章 课程介绍 1-1 课程导学 ...3-1 开发环境搭建 3-2 日志的使用 第4章 买家端类目 4-1 买家类目-dao(上) 4-2 买家类目-dao(下) 4-3 买家类目-service 第5章 买家端商品 5-1 买家商品-dao 5-...
  • SpringSocial之微信登录

    2020-02-06 19:08:18
    编写顺序同SpringSecurity之QQ登录 创建用户信息类WeiXinUserInfo: package com.cong.security.core.social.weixin.api; import lombok.Data; /** * 微信用户信息 */ @Data public class WeixinUserInfo { //...
  • 源码请见:Java企业微信开发_00_源码及资源汇总贴 一、本节要点 1.发送消息与被动回复消息 (1)流程不同:发送消息是第三方服务器主动通知微信服务器向用户发消息。而被动回复消息是 用户发送消息之后,微信...
  • 学习微信公众号的开发,必须先阅读微信开发文档,通过阅读文档,了解有关微信公众开发的步骤及相关限制,了解其工作原理等。 微信开发之入门指引 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...
  • 通过热门框架spring boot来学习微信公众号开发,帮助同学们快速入门微信公众号开发
  • 慕课网2019版 微服务时代Spring Boot企业微信点餐系统https://coding.imooc.com/class/117.html#Anchor Spring Boot点餐系统实战(1)–准备 Spring Boot点餐系统实战(2)–商品类目的Dao和Service层开发和测试 ...
  • 最近通过慕课网学习了《基于springboot企业微信点餐系统项目》,干货满满,既有springboot知识的学习,又有业务逻辑的处理体会。更多的是接触了企业级开发过程中各种编程心得和技巧,能够很大程度的帮助我们规范开发...
1 2 3 4 5 ... 20
收藏数 8,223
精华内容 3,289