Re:从零开始的 Spring Session(二)

上一篇文章介绍了一些 Session 和 Cookie 的基础知识,这篇文章开始正式介绍 Spring Session 是如何对传统的 Session 进行改造的。官网这么介绍 Spring Session:

Spring Session provides an API and implementations for managing a user’s session information. It also provides transparent integration with:

  • HttpSession - allows replacing the HttpSession in an application container (i.e. Tomcat) neutral way. Additional features include:
    • Clustered Sessions - Spring Session makes it trivial to support clustered sessions without being tied to an application container specific solution.
    • Multiple Browser Sessions - Spring Session supports managing multiple users’ sessions in a single browser instance (i.e. multiple authenticated accounts similar to Google).
    • RESTful APIs - Spring Session allows providing session ids in headers to work with RESTful APIs
  • WebSocket - provides the ability to keep the HttpSession alive when receiving WebSocket messages

其具体的特性非常之多,具体的内容可以从文档中了解到,笔者做一点自己的总结,Spring Session 的特性包括但不限于以下:

  • 使用 GemFire 来构建 C/S 架构的 httpSession(不关注)
  • 使用第三方仓储来实现集群 session 管理,也就是常说的分布式 session 容器,替换应用容器(如 tomcat 的 session 容器)。仓储的实现,Spring Session 提供了三个实现(redis,mongodb,jdbc),其中 redis 使我们最常用的。程序的实现,使用 AOP 技术,几乎可以做到透明化地替换。(核心)
  • 可以非常方便的扩展 Cookie 和自定义 Session 相关的 Listener,Filter。
  • 可以很方便的与 Spring Security 集成,增加诸如 findSessionsByUserName,rememberMe,限制同一个账号可以同时在线的 Session 数(如设置成 1,即可达到把前一次登录顶掉的效果)等等

介绍完特性,下面开始一步步集成 Spring Session


Re:从零开始的 Spring Session(一)

Session 和 Cookie 这两个概念,在学习 java web 开发之初,大多数人就已经接触过了。最近在研究跨域单点登录的实现时,发现对于 Session 和 Cookie 的了解,并不是很深入,所以打算写两篇文章记录一下自己的理解。在我们的应用集成 Spring Session 之前,先补充一点 Session 和 Cookie 的关键知识。

由于 http 协议是无状态的协议,为了能够记住请求的状态,于是引入了 Session 和 Cookie 的机制。我们应该有一个很明确的概念,那就是 Session 是存在于服务器端的,在单体式应用中,他是由 tomcat 管理的,存在于 tomcat 的内存中,当我们为了解决分布式场景中的 session 共享问题时,引入了 redis,其共享内存,以及支持 key 自动过期的特性,非常契合 session 的特性,我们在企业开发中最常用的也就是这种模式。但是只要你愿意,也可以选择存储在 JDBC,Mongo 中,这些,spring 都提供了默认的实现,在大多数情况下,我们只需要引入配置即可。而 Cookie 则是存在于客户端,更方便理解的说法,可以说存在于浏览器。Cookie 并不常用,至少在我不长的 web 开发生涯中,并没有什么场景需要我过多的关注 Cookie。http 协议允许从服务器返回 Response 时携带一些 Cookie,并且同一个域下对 Cookie 的数量有所限制,之前说过 Session 的持久化依赖于服务端的策略,而 Cookie 的持久化则是依赖于本地文件。虽然说 Cookie 并不常用,但是有一类特殊的 Cookie 却是我们需要额外关注的,那便是与 Session 相关的 sessionId,他是真正维系客户端和服务端的桥梁。


解析 Spring 中的 ResponseBody 和 RequestBody

spring,restful,前后端分离这些关键词都是大家耳熟能详的关键词了,一般 spring 常常需要与前端、第三方使用 JSON,XML 等形式进行交互,你也一定不会对 @RequestBody 和 @ResponseBody 这两个注解感到陌生。


XML 与 javabean 的转换

XML 可以说是一种被时代淘汰的数据传输格式,毕竟相比较 JSON,其语法,表现形式,以及第三方类库的支持,都要略逊一筹,但最近在对接一些老接口时,主要还是以 XML 为主,而翻阅相关的文档以及博客,没看到很好的文章介绍如何使用 xml 进行数据传输,所以简单写下此文,做一下记录。内心多多少少还是会抵制对接如此老旧的接口,不过生活还是要继续。

Code First

先上一段代码,展示一下如何封装,讲解放到后面

一个典型的对接方提供的 XML 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ORDER>
<ORDER_NO>10086</ORDER_NO>
<TOTAL_PRICE>3.14</TOTAL_PRICE>
<CREATE_TIME>2017-08-26 03:39:30</CREATE_TIME>
<ORDER_ITEMS>
<ORDER_ITEM>
<GOODS_NAME> 德芙 </GOODS_NAME>
<NUM>3</NUM>
</ORDER_ITEM>
<ORDER_ITEM>
<GOODS_NAME> 旺仔 </GOODS_NAME>
<NUM>10</NUM>
</ORDER_ITEM>
</ORDER_ITEMS>
</ORDER>

而我们要对应的实体类,则应当如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@XmlRootElement(name = "ORDER")// <1>
@XmlAccessorType(XmlAccessType.FIELD)// <1>
public class Order {

@XmlElement(name = "ORDER_NO")// <1>
private String orderNo;

@XmlElement(name = "TOTAL_PRICE")
private BigDecimal totalPrice;

@XmlElement(name = "CREATE_TIME")
@XmlJavaTypeAdapter(DateAdapter.class) // <2>
private Date createTime;

@XmlElementWrapper(name = "ORDER_ITEMS") // <3>
@XmlElement(name = "ORDER_ITEM")
private List<OrderItem> orderItems;

}
1
2
3
4
5
6
7
8
9
10
@XmlAccessorType(XmlAccessType.FIELD)
public class OrderItem {

@XmlElement(name = "GOODS_NAME")
private String goodsName;

@XmlElement(name = "NUM")
private Integer num;

}

我举的这个示例基本包含一般情况下所有可能出现的需求

<1> 常用注解 XmlRootElement,XmlAccessorType,XmlElement

<2> 日期转换的适配器注解

<3> 如何在 XML 中设置集合

在介绍这三点之前,先给出转换的工具类


sinosoft 代码规范

介绍

本文档主要针对我们项目内部正在使用的框架,以及代码审查发现的一些共性问题提出一些开发规范。

JavaBean 规范

1 驼峰命名法【强制】

2 布尔类型规范【强制】
【说明】所有的布尔类型不允许以 is 开头,否则会导致部分序列化,hibernate 框架出现解析异常。
【反例】
原来项目的 BaseDomain 中标记逻辑删除的字段, 在部分场景下会出现问题


博客搬家

陆陆续续,写博客已经写了有 4 年多了,之前一直在 CSDN 维护博客(博客旧址),最近有了点空余时间,使用 hexo 搭了这个博客,的确比 CSDN 清爽多了,首先感谢 @程序猿 DD 推荐的 icarus 模板,国人开发的一个 hexo 模板,插件支持可能不是很完善,但是样式非常让人喜欢。

作为一个前端弱渣,搭建博客的过程还是遇到了不少的困难。原先是打算直接使用 github 个人主页作为博客地址,hexo 对 git 有很好的支持,源代码和博客静态页面都托管在了 github,master 分支放静态页面,hexo 分支放源文件。可惜的是国内坑爹的网速,github.io 的访问速度不尽如人意(github.com 倒还好),于是在宇泽学妹 @ntzyz 的帮助下,搞了 github 的 hook,本地提交到 github 时,代理服务器自动向 master 分支拉取页面,同时设置反向代理和 https。由于 hexo 是静态文件搭建的博客,这种方式可以说是非常合适的。所以,国内的朋友浏览本博客可以直接访问 https://www.cnkirito.moe,如果有国外代理的朋友可以直接访问我的 github 个人主页 https://lexburner.github.io

目前博客功能还不算完善,缺少评论,分享,和一些小插件,以后逐渐完善,不过不影响主要功能。以后这儿就作为我主要更新博客的地方了!


一个 DDD 指导下的实体类设计案例

1 引子

项目开发中的工具类代码总是随着项目发展逐渐变大,在公司诸多的公用代码中,笔者发现了一个简单的,也是经常被使用的类:BaseDomain,引起了我的思考。
在我们公司的开发习惯中,数据库实体类通常会继承一个叫做 BaseDomain 的类,这个类很简单,主要用来填充一些数据库实体公用的属性,它的设计如下:


使用 spring validation 完成数据后端校验

前言

数据的校验是交互式网站一个不可或缺的功能,前端的 js 校验可以涵盖大部分的校验职责,如用户名唯一性,生日格式,邮箱格式校验等等常用的校验。但是为了避免用户绕过浏览器,使用 http 工具直接向后端请求一些违法数据,服务端的数据校验也是必要的,可以防止脏数据落到数据库中,如果数据库中出现一个非法的邮箱格式,也会让运维人员头疼不已。我在之前保险产品研发过程中,系统对数据校验要求比较严格且追求可变性及效率,曾使用 drools 作为规则引擎,兼任了校验的功能。而在一般的应用,可以使用本文将要介绍的 validation 来对数据进行校验。

简述 JSR303/JSR-349,hibernate validation,spring validation 之间的关系。JSR303 是一项标准,JSR-349 是其的升级版本,添加了一些新特性,他们规定一些校验规范即校验注解,如 @Null,@NotNull,@Pattern,他们位于 javax.validation.constraints 包下,只提供规范不提供实现。而 hibernate validation 是对这个规范的实践(不要将 hibernate 和数据库 orm 框架联系在一起),他提供了相应的实现,并增加了一些其他校验注解,如 @Email,@Length,@Range 等等,他们位于 org.hibernate.validator.constraints 包下。而万能的 spring 为了给开发者提供便捷,对 hibernate validation 进行了二次封装,显示校验 validated bean 时,你可以使用 spring validation 或者 hibernate validation,而 spring validation 另一个特性,便是其在 springmvc 模块中添加了自动校验,并将校验信息封装进了特定的类中。这无疑便捷了我们的 web 开发。本文主要介绍在 springmvc 中自动校验的机制。


Re:从零开始的 Spring Security OAuth2(三)

上一篇文章中我们介绍了获取 token 的流程,这一篇重点分析一下,携带 token 访问受限资源时,内部的工作流程。

@EnableResourceServer 与 @EnableAuthorizationServer

还记得我们在第一节中就介绍过了 OAuth2 的两个核心概念,资源服务器与身份认证服务器。我们对两个注解进行配置的同时,到底触发了内部的什么相关配置呢?

上一篇文章重点介绍的其实是与身份认证相关的流程,即如果获取 token,而本节要分析的携带 token 访问受限资源,自然便是与 @EnableResourceServer 相关的资源服务器配置了。

我们注意到其相关配置类是 ResourceServerConfigurer,内部关联了 ResourceServerSecurityConfigurer 和 HttpSecurity。前者与资源安全配置相关,后者与 http 安全配置相关。(类名比较类似,注意区分,以 Adapter 结尾的是适配器,以 Configurer 结尾的是配置器,以 Builder 结尾的是建造器,他们分别代表不同的设计模式,对设计模式有所了解可以更加方便理解其设计思路)

1
2
3
4
5
6
7
8
9
10
11
public class ResourceServerConfigurerAdapter implements ResourceServerConfigurer {
@Override
public void configure(ResourceServerSecurityConfigurer resources <1>) throws Exception {
}

@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}

}

<1> ResourceServerSecurityConfigurer 显然便是我们分析的重点了。


Re:从零开始的 Spring Security OAuth2(二)

本文开始从源码的层面,讲解一些 Spring Security Oauth2 的认证流程。本文较长,适合在空余时间段观看。且涉及了较多的源码,非关键性代码以… 代替。

准备工作

首先开启 debug 信息:

1
2
3
logging:
level:
org.springframework: DEBUG

可以完整的看到内部的运转流程。

client 模式稍微简单一些,使用 client 模式获取 token
http://localhost:8080/oauth/token?client_id=client_1&client_secret=123456&scope=select&grant_type=client_credentials

由于 debug 信息太多了,我简单按照顺序列了一下关键的几个类:

1
2
3
4
ClientCredentialsTokenEndpointFilter
DaoAuthenticationProvider
TokenEndpoint
TokenGranter

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×