@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe

在上一次我讲解了@EnableOauth2SSO的全过程,这次我将讲解spring security oauth2 搭建授权服务器的全过程。
首先,你得记录官方的文档网址:https://docs.spring.io/spring-security-oauth2-boot/docs/2.2.0.RELEASE/reference/html5/
在开始的地方会让你参阅这个文档,我们进去看看

@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
我们先看下Oauth2的介绍
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
简单来说Oauth2保护资源,建立Oauth2客户端 通过访问令牌去访问这些资源
对于Oauth2提供程序的实现(提供角色在授权服务和资源服务之前分配的),他们有时在同一服务器上 您也可以通过spring security Oauth 分在2个不同的服务器上,令牌的请求
由spring mvc控制端点处理,受保护的资源由spring seucirty 请求过滤器处理

在官网的原话中,为了实现OAuth 2.0授权服务器spring security过滤链中,需要下面的端点
AuthorizationEndpoint用于服务于授权请求。预设网址:/oauth/authorize
TokenEndpoint用于服务访问令牌的请求。预设网址:/oauth/token
要实现OAuth 2.0资源服务器,需要以下过滤器:
将OAuth2AuthenticationProcessingFilter用于加载的身份验证给定令牌的认证访问请求。

我们来看看@EnableAuthorizationServer会触发哪些配置
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
在官网中,很详细的说出了,当您使用这个注解的时候会触发3个重要的配置类
ClientDetailsServiceConfigurer 用于配置客户端信息
AuthorizationServerSecurityConfigurer 用于配置授权服务器端点的安全过滤拦截
AuthorizationServerEndpointsConfigurer 用于配置端点
我们看看客户端要配置的属性
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
对于JDBC的配置我们需要查看下官方给出的github的代码
https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql
但是他并不是我们平常使用的mysql,数据类型会有差别,这点我们自己改改即可
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
将改好的代码放到数据库执行下,就会得到几张表
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
表也创建好了,我们在来看看令牌的方式
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
简单翻译就是:在创建授权服务器的令牌服务实现的时候,默认是内存实现,其中还有数据库和JWT令牌实现
注意我画红的地方,我们本次就是使用JWT做令牌,因为他并不储存,也符合大多数场景。
在官方文档中,也详细说明了JWT令牌的使用
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
手写,您需要JWT令牌储存来在授权服务器中使用,他依赖于JWT访问令牌转换器做解析,
其次,TokenStore不同与其他的TokenStore实现,他只做转换,不做储存,这也源自于此
令牌的特性。由于他不做储存,有些信息会存在此令牌上,因此需要进行签名来加密。想要解密,需要密匙来解密。注意:授权服务器和资源服务器必须携带同一个密匙。 为了不必要的麻烦,我们将把授权服务器和资源服务器写在同一个服务器上。

关于密匙的生存和使用,这个官网也已经给出。
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
他说需要添加把这个密匙库添加到 密匙储存密匙工厂里面,关于密匙库怎么生成,您需要使用JDK 自带的keytool进行生成
使用方式如下:
https://blog.****.net/zglwy/article/details/8169948 参考其资料
我们直接使用这2个命令:

如果你电脑需要权限可能需要cmd,我这里不需要直接打开
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
生成密匙库为authorization.jks 其里面的密匙的秘密为zhanghe66. 密匙库的秘密为zhanghe66.
keytool -genkeypair -alias authorization.jwt -validity 3650 -keyalg RSA -dname “CN=jwt,OU=jtw,O=jtw,L=zurich,S=zurich,C=CH” -keypass zhanghe66. -keystore authorization.jks -storepass zhanghe66.

迁移到pkcs12标准格式
keytool -importkeystore -srckeystore authorization.jks -destkeystore authorization.jks -deststoretype pkcs12

我们就会多出一个密匙库来
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
接下来就是配置这个密匙库,不过还好,spring官方为我们已经配置了,在AuthorizationServerTokenServicesConfiguration 授权服务器的token服务配置
里面有个JwtKeyStoreConfiguration (jwt密匙库配置)

@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
这个是用来从配置文件中读取密匙库的位置信息和密匙库密码创建jwt访问令牌转换器,在构建jwtTokenStore 令牌储存 和默认的令牌服务
稍微介绍一下每个的作用
JWTAccessTokenConverter 是用来通过密匙签名数据成令牌 和 公匙解密
JwtTokenStore 就是调用JWTAccessTokenConverter读取访问令牌,解析访问令牌为Oauth2Authentication,因为JWT不做储存
还有一个TokenServices
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
上面有字打错了,但我已经不想修改了(任性)
我们看下条件配置
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
配置完成后。我们在看下Oauth2引导
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
首先您要关闭引导Oauth2引导程序的自动配置(说白了就是想自定义配置),您需要基础
授权服务器配置适配器
您需要一个认证管理器,这个认证管理器不是用来认证客户端的,而是认证用户的。
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
为了客户端能够支持JDBC,请参考我给出的依赖


org.springframework.cloud
spring-cloud-starter-oauth2


org.springframework.cloud
spring-cloud-starter-netflix-eureka-client


org.springframework.boot
spring-boot-starter-web


org.springframework.boot
spring-boot-starter-data-jdbc


org.springframework.cloud
spring-cloud-starter-openfeign


org.springframework.boot
spring-boot-starter-data-redis


org.springframework.session
spring-session-data-redis


mysql
mysql-connector-java

Application.yml
server:
port: 9404
servlet:
session:
cookie:
path: /
security:
oauth2:
authorization:
jwt:
key-store: classpath:authorization.jks
eureka:
client:
service-url:
defaultZone: http://${eureka.instance.ip-address}:9000/eureka
instance:
ip-address: 127.0.0.1
prefer-ip-address: true
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: zhanghe66.
url: jdbc:mysql://192.168.200.128/tensquare_auth2?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Hongkong
application:
name: auth2
redis:
host: 192.168.200.128

我们先配置客户端,先添加一些数据
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
执行完毕后
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
看下数据:
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
我填写的数据
BaseClientDetails clientDetails = new BaseClientDetails(
“client1”
,“resources”
,""
,“authorization_code,refresh_token,password”
,""
,“http://localhost:9014/login,http://127.0.0.1:9014/login”);
clientDetails.setClientSecret(passwordEncoder.encode(“client1”));
大家赋值一下就可以了,我也不做过多的解释,这个只是对象的赋值罢了。
客户端有了,就差

终端的用户

@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe

在官网给出的例子中,他用的是内存存储,用做方便测试,我们就不用了,直接数据库了。
在我这里有个用户数据表
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
还有个用户服务
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
有一个用户API,我这里没有用swagger
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
使用了mybatisPlus 进行查询
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
为了能够使用此API
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
导入接口
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
继承使用

@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
我们只需要简短的加如下配置即可
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
注意,一定要加上@Bean
因为在AuthenticationConfiguration配置里面会加载InitializeUserDetailsBeanManagerConfigurer
初始化用户详情bean管理配置
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
在这里面会自动化的拾取Bean并构建Dao认证提供者
我们访问:http://localhost:9404/,他就会让你登录,您就正常登录即可。
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
登录后的页面,登录完成后,我们来获取下授权码
http://localhost:9404/oauth/authorize?grant_type=authorization_code&response_type=code&client_id=client1&state=1234&scope=all&redirect_uri=http://localhost:9014/login

http://localhost:9404/oauth/authorize?grant_type=授予的类型&response_type=响应类型&client_id=客户端id&state=随机的状态&scope=您给予的授权范围&redirect_uri=您注册的重定向地址

其中 授权类型 和 响应类型 客户端ID 授权访问(数据库scope为空为任意) 和重定向地址需要和数据库一致 状态码任意
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
请求后,跳转到批准授权的页面,当然您也可以自定义这个界面,为了不麻烦我不做过多的演示。
我们在客户端详情报表中
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
给予true,让他可以自动授权,不用手动批准。
此时我们在直接刷新页面,他就会直接重定向到我们注册的页面上来。
http://localhost:9014/login?code=EltKvE&state=1234
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
记住这个授权码,我们去申请令牌,注意一定要快,不然授权码会过期
我们填写客户端凭证,还有body参数,授权类型 重定向地址 授权码 授予的访问
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
虽然获取到了令牌,但是您也已经注意到了,这不像jwt令牌,没错,我们配置的他并没有触动
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
在Oauth2授权服务配置,由于我们自定义了配置,关闭了他的自动引导配置,我们只能复制一下了
我们把他的配置复制到我们这里来
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
并且在加下端点配置
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
在重新启动,重复之前的操作
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
没问题了,接下来我们尝试能否解析
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
没有权限。。。
我们配置一下,允许所有
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
好了,我们成功访问了。
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
看看公匙
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
在控制台看看是否一致
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
完全一样。
我们在看看password模式,怎么访问。
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
哦,没有这个授权,我们又得在配置一下
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
因为password模式需要用户的认证管理器,有的话他会自动给复合token授权添加一个ResourceOwnerPasswordTokenGranter 资源所有者密码token授权
好了,重启一下
@EnableAuthorizationServer 源码讲解(上半部分) 全分析 BY:ZhangHe
至此,我们的简单配置使用到此结束,接下来我将讲解源码