设计模式之策略模式
定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
在微信开发过程中,一些例如jsapi_token 和jsapi_ticket这些信息都有固定的生存时间,过了生存时间即失效,需要重新获取,然而微信方面对于这些信息的获取次数也加以限制。比如jsapi_token的有效时间为7200秒,日最大刷新次数为2000次。如果在每个业务周期内都获取一次的话,很有可能把接口刷爆掉,影响该公众号其他业务。
在传统开发中,上述那些敏感信息主要有以下几种存储形式:外部文件、内存、数据库、高速缓存等。介于一些信息都限制了有效时间,像是文件、内存、数据库这种形式存储的话每次获取前都需要判断一下token距上次获取的时间是否超过keytoken有效时间,若超过则调用token的刷新方法重新获取token。这个逻辑不难,但每次都要判断时间,这个很头疼。
当然,还有最后一种 高速缓存,比如redis,他可以很简单的在插入key的时候设置该key的生存时间,这样一来我们在读key的时候只需要判断有没有就行了,有就用没有就获取,是不是so easy。
好了,逻辑捋清楚了,再来看一下weixin4j给我们提供了哪些缓存的实现。
在此之前,我对该项目的缓存没有任何一点了解,完全出于我个人判断,因为微信开发中必须要涉及到关键性token的缓存问题,那么该框架一定会提供缓存功能,这个方向是对的,那么具体在哪里?怎么用?怎么实现的?如何扩展?就需要翻文档或者查作者源码了。很遗憾,项目官网并没有提供缓存使用方面的文档或介绍,但我在该项目的github主页中找到了一篇介绍,实际上只是说了提供了和默认提供哪种缓存而已,并没有提供别的信息。开启自悟模式。
在基础组件的cache包中,我找到了一组关于缓存的API。
很巧,我的思路和作者一样,这里有五种缓存方式的具体实现,与我上面相比,多了一个redis集群配置和Memcache缓存配置,这个就不多说了。他们分别是
默认情况下,weixin4j使用文件方式,即使用FileCacheStorager来管理token缓存,默认缓存路径:java.io.tmpdir。
五种方式都可以尝试,这里因为项目背景的原因,我使用单个Redis方式配置。
直接看RedisCacheStorager这个类的源码
1 | public class RedisCacheStorager<T extends Cacheable> implements |
在基于单个Redis的token缓存实现中,默认使用本地无密码认证的6379端口的Redis作为缓存介质,但也提供了四个构造方法去创建自己的token缓存。
第一种,直接使用默认的方式,不多说
第二种,使用主机地址、端口、超时时间配置
第三种,直接使用jedis连接池配置对象,但主机地址、端口和超时时间均使用默认
第四种,全部自定义
第五种,直接把jedis连接池对象传入
在Weixin4jConfig对象中,加入RedisCacheStorager对象Bean配置。这里我的项目基于SpringBoot,配置如下
1 | @Autowired |
直接将Jedis的连接工厂拿到,配置RedisCacheStorager的时候注入四个信息即可。
在WeixinProxy对象中可以找到这样一个构造函数
1 | public WeixinProxy(CacheStorager<Token> cacheStorager) { |
在构造该对象的时候传入一个Token对象的缓存存储管理器。这里直接在昨天的配置类中Weixin4jProxy的Bean配置处使用该构造函数初始化,并传入一个RedisCacheStorager对象。
1 | @Bean |
调用分享接口,进入redis查看缓存信息
使用weixin4j第二天,今天搞一下JSSDK,也就是分享到朋友圈,分享到QQ之类的接口,官方称JS接口。废话不说,开干。
在使用weixin4j进行微信JSSDK开发之前,先熟悉一下sdk的开发逻辑,这样在使用weixin4j的时候如果遇到问题,解决起来比较方便,逻辑也比较清楚。下面捋一下步骤
签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。
逻辑都知道了吧,建议用原生去写一下,一步步按照开发逻辑捋一遍再看这篇文章,就会更清晰一点。这里不再写原生了,直接使用weixin4j快速开发后端接口
上一篇文章在获取用户信息的时候单独创建了一个配置类Weixin4jConfig,其中配置了开发代理类WeixinProxy,通过看这个Bean的源码,我们发现了如下的方法:
1 | /** |
看到这个方法是关于JSSDK的,而且直接操作的是Ticket,我就乐坏了,哈哈。
不难发现该方法可以获取Ticket票据管理对象,需要传如一个参数TicketType,用我多年体育老师教的英语经验来看,这个参数名叫票据类型,让我们再看一下这个票据类型到底是啥
1 | public enum TicketType { |
这是一个枚举类,发现jsapi,思路清晰了,需要根据票据类型获取票据管理对象。查阅票据管理对象的源代码发现并没有生成签名的算法。
接着沿着我们的思路,我们需要找到一个可以将四个参数生成签名的函数,或者如果weixin4j封装的更完善一点直接传入一个url得到组配置结果信息。
翻源码翻了好一会,发现在weixin4j-base组件com.foxinmy.weixin4j.jssdk包中有两个类,分别是JSSDKAPI和JSSDKConfigurator,真是踏破草鞋无觅处,得来全不费功夫呀
JSSDKConfigurator是真正的JSSDK配置信息的操作对象,而JSSDKAPI对象则是API信息的对象,后续才会用到它,先不着急。来看一下这个对象的结构:
根据思路我们知道,使用weixin4j开发JSSDK需要三个组件,一个是TokenManager另一个是JSSDKConfigurator,还有上一篇的WeixinProxy代理类
1 | @Bean |
到此我们的配置工作就结束了, 接着来搞一下api接口
首先注入刚刚配置的两个Bean
1 | @Autowired |
接着 编写web接口
1 | @GetMapping(value = "jssdk_jsonconfig") |
后端的接口代码就是这样。关键代码就两行,是不是特别一贼
根据我们的原生开发逻辑,直接上代码,不啰嗦
1 | <!-- js --> |
因为最近的项目需要做微信方面的开发,之前也有过微信开发的经验,但每逢项目中遇到跟微信沾边的东西就得从头写起,一直也没单独把微信开发方面的代码单独独立出来。
首先到微信开放平台申请一个测试号,绑定安全域名
这是一个封装了相当完善的Java微信开发工具,项目主页:weixin4j
建议先了解微信传统Oauth2.0开发流程再使用该工具
1 | //这里引入weixin4j公众号开发Api,还有企业号和服务器的这里不引入 |
weixin4j相关依赖还有fastjson和HttpClient
将com.foxinmy.weixin4j.mp.WeixinProxy类注入Spring容器管理
1 | @Bean |
获取用户信息需要使用的几个对象
在classpath下创建weixin4j.properties配置文件,配置Appid和secret
1 | weixin4j.account={"id":appid,"secret":secret} |
贴上获取用户信息的代码,按照微信逻辑走即可
1 | @GetMapping(value = "/user_authenticator") |
1 | @GetMapping(value = "/user_authenticator") |
weixin4j提供了构造授权连接的Api,传入回调地址、state、scope即可
com.foxinmy.weixin4j.mp.api.OauthApi#getUserAuthorizationURL(redirectUri, state, scope)
scope分为两种snsapi_base和snsapi_userinfo,具体请查阅微信开发文档
通过微信访问授权连接,应该会得到用户信息的输出。
使用weixin4j大大简化了java微信开发的时间,而且weixin4j还提供了一套非常灵活的token缓存机制。这篇先到这里,下一篇会分享通过weixin4j开发分享到朋友圈的功能。
在调用php程序员的接口时,我们约定对参数进行字典排序并encode后按照约定的逻辑加密生成签名,加到请求头一同传输,php端对提交的参数执行同样的逻辑并且与请求头中的签名对比,起到防止数据在传输过程中被篡改。当然了,使用ssl的自然不用这么麻烦。我的问题出现在java方面给php接口方面post时中文乱码的问题。
谈到中文乱码问题,我首先想到肯定是字符集在某个环节出现不匹配的情况。在使用HttpClient进行Http请求操作时,涉及到编码的无非在请求头处,但这里我们按照接口定义,对参数生成签名时使用了encode(UTF-8)编码,经过与接口方面的校验签名生成正确,也就说明是请求头出了问题。看一下post代码
1 | public static StringBuilder post(String url, Map<String, String> headers, Map<String, String> params) throws IOException { |
第九行位置,可以看到我在发起post请求时使用的是默认UrlEncodedFormEntity,该对象会对参数进行自动encode操作,但问题来了,编码呢?
看一下这个对象的构造函数
1 | /** |
可以看到在单参数构造函数中还使用了一个第二个参数是字符编码的构造函数,看注释可以看到,在不指定字符编码的时候默认使用的是DEFAULT_CONTENT_CHARSET字符编码(ISO-8859-1)
好了,现在已经定位到问题了。问题出现在这里,现在可以使用第二个参数为字符编码的构造函数构造该对象。使用utf-8构造该对象即可解决问题。
先说说什么是Google Authenticator(以下简称GA),你可以把它想成开源的QQ手机令牌,基于时间和一定的算法生成6位动态口令。
在macbook系统上安装phpMyadmin工具管理mysql数据库。
目录地址:/Library/WebServer/Documents/ 为apache 的www目录
使用 终端su命令输入密码后切换管理员账号,并输入一下命令
1 | nano /etc/apache2/httpd.conf |
找到以下信息并修改
1 | #LoadModule php5_module libexec/apache2/libphp5.so |
复制配置文件
1 | cp config.sample.inc.php config.inc.php |
修改权限
1 | chmod -R 777 /Library/WebServer/Documents/phpmyadmin/ |
启动apache测试环境
1 | /usr/sbin/apachectl start |
访问 http://localhost/phpmyadmin 输入帐密登录
从字面意义上来讲,泛型,即广泛的类型,这样就不难理解了。泛型是java程序设计的一个手段,可以想想为使用泛型编写的代码即是一个模板,使用泛型机制编写java程序在安全性和可读性上都会更有帮助。
开发时最痛恨的是灯下黑的bug,bug就在眼皮子地下被自己的“自作聪明”忽略掉。就在刚刚,我花了近十分钟去解决一个Mybatis报的异常: Result Maps collection already contains value for #¥%。先解释一下该异常信息的意思:Result Maps集合已经存在于#¥%。总之通过异常信息可以确定的是 该文件中有相同的某某某,即有同名的东西存在于一个文件中。着手解决一下吧
今天手贱升级max最新系统后,打开Idea提示git环境异常,提示如下信息:
xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun
完美解决方法:xcode-select –install