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

查看更多

分享到

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

前言

今天来聊聊一个接口对接的场景,A 厂家有一套 HTTP 接口需要提供给 B 厂家使用,由于是外网环境,所以需要有一套安全机制保障,这个时候 oauth2 就可以作为一个方案。

关于 oauth2,其实是一个规范,本文重点讲解 spring 对他进行的实现,如果你还不清楚授权服务器,资源服务器,认证授权等基础概念,可以移步 理解 OAuth 2.0 - 阮一峰,这是一篇对于 oauth2 很好的科普文章。

需要对 spring security 有一定的配置使用经验,用户认证这一块,spring security oauth2 建立在 spring security 的基础之上。第一篇文章主要是讲解使用 springboot 搭建一个简易的授权,资源服务器,在文末会给出具体代码的 github 地址。后续文章会进行 spring security oauth2 的相关源码分析。java 中的安全框架如 shrio,已经有 跟我学 shiro - 开涛,非常成体系地,深入浅出地讲解了 apache 的这个开源安全框架,但是 spring security 包括 oauth2 一直没有成体系的文章,学习它们大多依赖于较少的官方文档,理解一下基本的使用配置;通过零散的博客,了解一下他人的使用经验;打断点,分析内部的工作流程;看源码中的接口设计,以及注释,了解设计者的用意。spring 的各个框架都运用了很多的设计模式,在学习源码的过程中,也大概了解了一些套路。spring 也在必要的地方添加了适当的注释,避免了源码阅读者对于一些细节设计的理解产生偏差,让我更加感叹,spring 不仅仅是一个工具框架,更像是一个艺术品。

查看更多

分享到

对于 Spring Cloud Feign 入门示例的一点思考

Spring Cloud Feign

Spring Cloud Feign 是一套基于 Netflix Feign 实现的声明式服务调用客户端。它使得编写 Web 服务客户端变得更加简单。我们只需要通过创建接口并用注解来配置它既可完成对 Web 服务接口的绑定。它具备可插拔的注解支持,包括 Feign 注解、JAX-RS 注解。它也支持可插拔的编码器和解码器。Spring Cloud Feign 还扩展了对 Spring MVC 注解的支持,同时还整合了 Ribbon 和 Eureka 来提供均衡负载的 HTTP 客户端实现。

分布式应用早在十几年前就开始出现,各自的应用运行在各自的 tomcat,jboss 一类的容器中,他们之间的相互调用变成了一种远程调用,而实现远程调用的方式很多。按照协议划分,可以有 RPC,Webservice,http。不同的框架也对他们有了各自的实现,如 dubbo(x),motan 就都是 RPC 框架,本文所要讲解的 Feign 便可以理解为一种 http 框架,用于分布式服务之间通过 Http 进行接口交互。说他是框架,有点过了,可以理解为一个 http 工具,只不过在 spring cloud 全家桶的体系中,它比 httpclient,okhttp,retrofit 这些 http 工具都要强大的多。

入门

先用一个简单的例子,看看如何在项目中使用 Feign。示例项目使用 maven 多 module 构建,采用 springcloud 的 Dalston.SR1 版本

查看更多

分享到

Re:从零开始的领域驱动设计

前言

领域驱动的火爆程度不用我赘述,但是即便其如此得耳熟能详,但大多数人对其的认识,还只是停留在知道它的缩写是 DDD,知道它是一种软件思想,或者知道它和微服务有千丝万缕的关系。Eric Evans 对 DDD 的诠释是那么地惜字如金,而我所认识的领域驱动设计的专家又都是行业中的资深前辈,他们擅长于对软件设计进行高屋建瓴的论述,如果没有丰富的互联网从业经验,是不能从他们的分享中获取太多的营养的,可以用曲高和寡来形容。1000 个互联网从业者,100 个懂微服务,10 个人懂领域驱动设计。

可能有很多和我一样的读者,在得知 DDD 如此火爆之后,尝试去读了开山之作《领域驱动设计——软件核心复杂性应对之道》,翻看了几张之后,晦涩的语句,不明所以的专业术语,加上翻译导致的语句流畅性,可以说观看体验并不是很好,特别是对于开发经验不是很多的读者。我总结了一下,为何这本书难以理解:

  1. 没有阅读软件设计丛书的习惯,更多人偏向于阅读偏应用层面的书籍,“talk is cheap,show me the code”往往更符合大多数人的习惯。
    1. 没有太多的开发经验支撑。没有踩过坑,就不会意识到设计的重要性,无法产生共情。
    2. 年代有些久远,这本书写于 2004 年,书中很多软件设计的反例,在当时是非常流行的,但是在现在已经基本绝迹了。大师之所以为大师,是因为其能跨越时代的限制,预见未来的问题,这也是为什么 DDD 在十几年前就被提出,却在微服务逐渐流行的现阶段才被大家重视。

诚然如标题所示,本文是领域驱动设计的一个入门文章,或者更多的是一个个人理解的笔记,笔者也正在学习 DDD 的路上,可能会有很多的疏漏。如有理解有偏颇的地方,还望各位指摘。

查看更多

分享到

spring 中的懒加载与事务 -- 排坑记录

案例描述

本文主要描述了开发中常见的几个与 spring 懒加载和事务相关的案例,描述常见的使用场景,以及如何规避他们,给出具体的代码。

  1. 在新的线程中,访问某个持久化对象的懒加载属性。
  2. 在 quartz 定时任务中,访问某个持久化对象的懒加载属性。
  3. 在 dubbo,motan 一类 rpc 框架中,远程调用时服务端 session 关闭的问题。

上面三个案例,其实核心都是一个问题,就是牵扯到 spring 对事务的管理,而懒加载这个技术,只是比较容易体现出事务出错的一个实践,主要用它来引发问题,进而对问题进行思考。

查看更多

分享到

使用 zipkin 做分布式链路监控

介绍

快速入门

  • 安装方式一:使用 zipkin 官方提供的 jar 启动服务
    zipkin 官方提供了一个现成的使用 springboot 写的 zipkin 服务端,客户端的链路监控报告可以通过多种方式(下文会讲解具体的方式)向服务端发送报告。

    配置详解
    存储方式
    查看源码可知其有 4 种持久化方式,本文选择使用最熟悉的 mysql 持久化链路调用信息。

首先建立数据库:
默认情况下 zipkin 运行时数据保存在内存中,重启数据会丢失
数据库脚本下载

查看与 mysql storage 相关的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
@ConfigurationProperties("zipkin.storage.mysql")
public class ZipkinMySQLStorageProperties implements Serializable { // for Spark jobs
private static final long serialVersionUID = 0L;

private String host = "localhost";
private int port = 3306;
private String username;
private String password;
private String db = "zipkin";
private int maxActive = 10;
private boolean useSsl;
...
}

所以,我们使用 mysql 作为持久化策略,启动服务端的脚本也就有了

1
java -server -jar zipkin-server-1.26.0-exec.jar --zipkin.storage.type=mysql --zipkin.storage.mysql.host=localhost --zipkin.storage.mysql.port=3306 --zipkin.storage.mysql.username=root --zipkin.storage.mysql.password=root --zipkin.storage.mysql.db=zipkin

  • 安装方式二
    springcloud 官方按照传输方式分成了三种启动服务端的方式:Sleuth with Zipkin via HTTP,Sleuth with Zipkin via Spring Cloud Stream,Spring Cloud Sleuth Stream Zipkin Collector。只需要添加相应的依赖,之后配置相应的注解,如 @EnableZipkinStreamServer 即可。具体配置参考 Spring Cloud 官方文档

项目中,我们使用第一种作为服务端的启动方式,使用 mysql 作为持久化方案

查看更多

分享到

JAVA 程序员分级,你属于哪一种?

  • 初级 — 初

    掌握 java 基础,熟悉常用类库。理解 java web 中的 servlet,jsp,并了解常用的框架对 java web 的封装原理,能够借助框架完成增删改查功能。理解数据库在 web 开发中的地位。

  • 初级 — 中

    理解 java 中较为高级的特性,如反射,动态代理,JVM,内存模型,多线程等等。熟练使用框架,对框架中遇到的 bug,能够借助日志和搜索引擎分析出问题的原因。在团队中,能够独立完成普通后台业务功能的开发。了解数据库的高级特性,如索引,存储引擎等等。

  • 初级 — 高

    理解 java 分布式架构,微服务架构,了解其与集中式架构的区别,并能保证分布式代码质量。熟练使用各个中间件如 redis,mq,zookeeper 等等,并了解其工作原理和使用场景。能够在中级或高级程序员的带领之下,完成非核心功能的研发。能够关注开源,并且具有阅读源码的能力。

  • 中级

    具备一定的项目开发经验(3 年之上一线互联网产品研发经验),拥有线上 bug 的处理能力,JVM 调优能力,以及完成核心业务功能的开发。并且带领团队的新人,能够按能力分配任务。

  • 高级

    团队的核心人物,把控整个项目的质量,包括代码漏洞和规范问题。具有 5 年以上项目开发经验,2 年以上架构搭建的经验,能够根据业务选择不同的架构类型;根据团队组成,分配不同的任务。具有将自己的知识分享出去的能力,带领初级程序员走向中级,中级程序员走向高级的能力。

分享到

drools 用户指南 ----Cross Products

Cross Products

之前提到“Cross Products”一词,其实就是一个 join 操作(译者注:可以理解为笛卡尔积)。想象一下,火灾报警示例的数据与以下规则结合使用,其中没有字段约束:

1
2
3
4
5
6
7
rule "Show Sprinklers" when
$room : Room()
$sprinkler : Sprinkler()
then
System.out.println("room:" + $room.getName() +
"sprinkler:" + $sprinkler.getRoom().getName() );
end

在 SQL 术语中,这就像是执行了 select * from Room, Sprinkler,Sprinkler 表中的每一行将与 Room 表中的每一行相连接,从而产生以下输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
room:office sprinkler:office
room:office sprinkler:kitchen
room:office sprinkler:livingroom
room:office sprinkler:bedroom
room:kitchen sprinkler:office
room:kitchen sprinkler:kitchen
room:kitchen sprinkler:livingroom
room:kitchen sprinkler:bedroom
room:livingroom sprinkler:office
room:livingroom sprinkler:kitchen
room:livingroom sprinkler:livingroom
room:livingroom sprinkler:bedroom
room:bedroom sprinkler:office
room:bedroom sprinkler:kitchen
room:bedroom sprinkler:livingroom
room:bedroom sprinkler:bedroom

这些连接结果显然会变得巨大,它们必然包含冗余数据。 cross products 的大小通常是新规则引擎产品性能问题的根源。 从这可以看出,我们希望约束 cross products,这便是用可变约束(the variable constraint)完成的。

1
2
3
4
5
6
7
8
rule
when
$room : Room()
$sprinkler : Sprinkler(room == $room)
then
System.out.println("room:" + $room.getName() +
"sprinkler:" + $sprinkler.getRoom().getName() );
end

这就使得筛选结果只有寥寥几行, 这就为每一个 Room 筛选出了正确的 Sprinkler. 在 sql 中 (实际上是 HQL) 这样的查询约等于 select * from Room, Sprinkler where Room == Sprinkler.room.

分享到

drools 用户指南 ----Methods vs Rules

Methods vs Rules

人们经常混淆方法和规则,初学者经常会问:“我如何理解规则的含义?“ 在最后一节之后,你会对规则的使用得心应手,答案也变得显而易见的,但在这之前,先让我们总结一下方法判断和规则的差异。

1
2
3
4
5
public void helloWorld(Person person) {
if (person.getName().equals("Chuck") ) {
System.out.println("Hello Chuck");
}
}
  1. 方法是被直接调用的
  2. 需要传递具体的实例
  3. 一个调用导致一次执行(One call results in a single execution)。
1
2
3
4
5
rule "Hello World" when
Person(name == "Chuck")
then
System.out.println("Hello Chuck");
end
  1. 只要将其插入引擎,就可以通过匹配任何数据执行规则。
  2. 规则永远无法被直接调用,而只能触发
  3. 无法将特定的实例传递给规则
  4. 根据匹配,一个规则可能会触发一次或多次,或根本不被触发。
分享到