OAuth2 与 JWT 那些事

  自从微博流行后,OAuth2 概念自然也在开发圈里活跃起来,各种微博客户端雨后春笋般出现。到后来的 Web 前后端分离流行起来后,JWT 又映入眼帘。
  15年花图相册上线后,自认为在PC上看图已经有很好的体验了,做到极致的响应式页面能适配从手机屏到4K屏。经过一年多的使用,并没达到之前的预料,这一Geek般操作在实际使用中还是有很多不足,这里就不列举了。
  为此何不针对不同平台,分别做一套程序,并调整不同的功能。例如微信小程序,仅提供基础浏览,分享某一个相册,隔离其他相册。基于这些点的考虑,有很多技术方案选型。本文简单聊聊 OAuth2 和 JWT。

  Oauth2有四个角色非常重要

  Resource Owner 资源拥有者
  Resource Server 资源服务器
  Authorization Server 授权服务器
  Client 客户端

  举个例子:

  用户拥有花图相册上的照片资源(PC端upload),花图服务器提供存放这些照片资源,同时提供授权服务。这时候,如果有一个【花图App】用户将其装在手机上,选择使用花图账号登录,跳转到一个花图相册登录界面,随后进行授权确认。再返回App中,App便可以与花图服务器进行交互,获取到用户照片资源,显示出来。这里面还有很多细节如:AppId,AppSecret,Grant,Access_Token,Refresh_Token,Expires等不具体描述了。

  JWT本质只是提供了一种 Client 与 Server 间交互数据的协议,提供编码,防篡改等一些特性,通常与一些权限框架结合使用,例如 Shiro、Security 等。通过自定义 Token 签发一定程度上取代 Web 里面的 Session。

  Spring Boot 对着两种技术均有支持,还挺方便的,后续用到再细写。

Spring Boot 2.0 goes GA

  经过 17 个月的酝酿,来自 215 个不同开发者,超过 6800 个 commits 之后,终于迎来了一个全新的 2.0 版本现在如期而至!
  可以通过 repo.spring.ioMaven Central 下载使用。
  而此次发布距离 1.0 版本已经过去了4个年头,这也是第一个开始全面支持 Spring Framework 5.0 。

  主要包括以下新特性:
1、支持 Java 9,最低支持 Java 8
2、支持 Reactive Web 编程
3、对 Reactive Spring Data 提供自动配置项,包括 Cassandra/MongoDB/Couchbase/Redis
4、内嵌 Netty 高性能网络框架
5、内嵌容器 Tomcat/Undertow/Jetty 开始支持 HTTP/2
6、提供 Kotlin 语言支持
7、提供 Jersey 和 WebFlux 支持
8、提供全新的 Micrometer 统计支持
9、提供 Quartz 定时任务支持
10、安全配置优化

  更多新特性了解,可以浏览这里:release notes,老版本迁移看这里:migration guide,还有熟悉的start bootstrap:start.spring.io

Solr 7.2.1配置smartcn中文分词器

  近期准备将歌词Style后端进行重构,首先将原来内嵌的Lucene 4.10.2升级到Solr 7.2.1。印象中上次使用Solr应该还是3的版本,变化非常大,整体看了下,其实简化了不少配置,通过admin管理界面能很方便的添加core和fieldType,也不用到处copy xml。这里简单记录下:

1、下载、解压:

wget http://mirrors.hust.edu.cn/apache/lucene/solr/7.2.1/solr-7.2.1.zip
unzip solr-7.2.1.zip
cd solr-7.2.1

2、加入smartcn分词器
  解压的contrib里面其实已经包含了smartcn分词器,默认没有启用,这里将其加入到默认的config中。

一、编辑server/solr/configsets/_default/conf/solrconfig.xml
节点最后增加:

<lib dir="${solr.install.dir:../../../..}/contrib/analysis-extras/lucene-libs/" regex="lucene-analyzers-smartcn-\d.*\.jar" />

二、编辑server/solr/configsets/_default/conf/managed-schema
节点里面增加

<fieldType name="text_cn_smart" class="solr.TextField" positionIncrementGap="100">
  <analyzer type="index">
    <tokenizer class="org.apache.lucene.analysis.cn.smart.HMMChineseTokenizerFactory"/>
  </analyzer>
  <analyzer type="query">
    <tokenizer class="org.apache.lucene.analysis.cn.smart.HMMChineseTokenizerFactory"/>
  </analyzer>
</fieldType>

3、启动、创建core:
  由于我这里是root用户安装,需要加-force,官方不建议root下安装。

./solr start -force
./solr create -c lyric -force

  通过命令创建的core,默认会读取_default下面的配置,所以在上一步需要加入所需要的组件。如无问题则可以通过浏览器打开管理界面进行下一步。

4、增加Field:
  在Core Admin可以看到刚才创建的lyric已经出现在了右边,在Core Selector中选择lyric,可以看到更详细的信息,下拉框中已经给我们建好了几个Field,其中有一个类型为string的id和_root_,这个建议保留使用,并两者配置保持一致,其余的Dynamic Field依据情况可以删除,具体使用方法可以参考_default中的配置文件,注释说的很详细。

  这里我们直接选择Scheme -> Add Field,将需要索引的中文字段field type设置为之前设置的text_cn_smart,若是没有这一选项,检查前面的步骤。Solr已经具备通过dataimport组件导入任意来源的数据,不过我建议还是在程序中来添加数据,这里就不细说dataimport了。

5、验证:
  通过程序导入测试数据后,可以通过Analysis界面检查分词器是否正常工作,Query界面可以模拟查询,检验结果。_default中的配置是一份比较齐全的参考,在生产环境建议移除不需要的选项,避免影响性能。顺便提一句:Solr热度已经明显低于Elasticsearch。

Spring MVC之工作流程

  一直以来都是用Spring MVC做业务开发,却很少去了解内部的一些细节,虽然网上与天盖地的源码解读,然而却无多大印象,加上版本变化之快,不妨重读一次。这里以spring-webmvc 4.3.4为例,也正是花图所使用的版本,下面详细来看看。

  DispatcherServlet继承自FrameworkServlet,而FrameworkServlet又继承自HttpServletBean并实现了ApplicationContextAware,通过这样的一个分层结构,可以更清晰的知道处理流程。

  第一部分:初始化

  初始化分两个部分:一个是监听器部分:负责Root WebApplicationContext的初始化,包含通过XmlBeanDefinitionReader获取配置文件,进行加载,通过RequestMappingHandlerMapping扫描出带有RequestMapping注解的Controller,通过RequestMappingHandlerAdapter注册Controller,等等。

  另一部分是FrameworkServlet的初始化:抽象类HttpServletBean继承自HttpServlet,实现了父类的init()方法,获取init-param中的配置,对DispatcherServlet进行数据绑定。然后调用FrameworkServlet.initServletBean(),初始化webApplicationContext,并调用DispatcherServlet.onRefresh(),来完成初始化。

initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);

  从Bean中取出相应的Resolver,若没配置则取默认,默认的设置在DispatcherServlet.properties。

  第二部分:接收请求

  对于Java Web而言,都是通过web.xml中的Servlet来分配请求,一般的都会将”/”根请求配置为DispatcherServlet,这样所有的前端请求都会路由到Spring MVC。

  抽象类FrameworkServlet对Servlet的几个基本Http method方法进行了代理,统一通过processRequest()进行处理,并最终到达DispatcherServlet中的doDispatch()。

  doDispatch()中先检查request是否是multipart request,如果是,则用一个multipartResolver来包装request,判断依据是contentType是否是”multipart/”开头。

  找到HandlerExecutionChain用来处理当前的请求,方法是从已注册的mappingRegistry中找到最匹配请求URL路径的handlerMethod,这个即我们最熟悉的Controller,放在HandlerExecutionChain中。若HandlerExecutionChain为空,则没有匹配到任何处理流程,发送404响应,并报非常熟悉的一个日志:”No mapping found for HTTP request with URI XXX”。

  找到HandlerAdapter用来处理请求,从已加载的handlerAdapters中遍历出一个支持该Handler的HandlerAdapter,本例中的Handler即Controller。

  检查Http method是否为GET或者HEAD,如果是,进行last-modified验证,符合条件的发送304响应,并更新Last-Modified相关参数值。

  检查拦截器配置,遍历执行applyPreHandle(),并触发triggerAfterCompletion(),若有返回false的,则本次请求结束。

  一路顺利到此可以调用Controller中的方法了。

  对于Controller返回时ModelAndView没设置view的,给一个默认view。

  接着倒着遍历执行过滤器applyPostHandle(),执行完一个完整的拦截器链。

  接下来是进行ModelAndView的渲染,将ModelMap转化到request.setAttribute()中,并RequestDispatcher.include() or forward()到JSP中。

  最终完成本次请求。

在MacOS中使用AB测试工具

  一直以来都是用JMeter来做压力测试,GUI界面功能虽然强大,报表齐全,但有时候只是想简单测试下,启动JMeter过于繁琐,于是想到用ab来测试,一条命令搞定。MacOS自带了ab,却因版本问题无法正常使用,需要升级到最新版,本文简单记录下过程。

1,先下载httpd、apr、apr-util、pcre,若有已安装的可以忽略
http://httpd.apache.org/download.cgi
http://apr.apache.org/download.cgi
https://ftp.pcre.org/pub/pcre/

2,由于httpd依赖apr、pcre,所以先安装apr、apr-util、pcre

$ cd apr-1.5.2/
$ ./configure
$ make
$ make test
$ sudo make install

$ cd apr-util-1.5.4/
$ ./configure --with-apr=../apr-1.5.2/
$ make
$ sudo make install

$ cd pcre-8.40/
$ ./configure
$ make
$ sudo make install

$ cd ../httpd-2.4.25/
$ make
$ sudo make install

3,在恢复模式下替换系统ab,关闭MacOS Rootless,开机按住Cmd+R键,进入恢复模式,打开终端执行:

$ csrutil disable
Successfully disabled System Integrity Protection. Please restart the machine for the changes to take effect.

重启
$ reboot

4,备份并替换,以下路径是httpd默认安装位置

$ cd /usr/local/apache2/bin/
$ sudo mv /usr/sbin/ab /usr/sbin/ab.bak
$ sudo cp ab /usr/sbin/

5,重新进入恢复模式,开启Rootless

$ csrutil enable
Successfully enabled System Integrity Protection. Please restart the machine for the changes to take effect.

重启
$ reboot

  到此,工具算是准备好了,但MacOS对文件打开有限制,无法满足ab需求,通过以下命令临时调整:(重启后需重新执行)

$ sysctl kern.maxfiles
kern.maxfiles: 12288

$ sysctl kern.maxfilesperproc
kern.maxfilesperproc: 10240

$ sudo sysctl -w kern.maxfiles=1048600
kern.maxfiles: 12288 -> 1048600

$ sudo sysctl -w kern.maxfilesperproc=1048576
kern.maxfilesperproc: 10240 -> 1048576

$ ulimit -n
256

$ ulimit -n 1048576

$ ulimit -n
1048576

  搞定!