OIDC中JWK,JWKS的介绍与使用

JWKS全称为 JSON Web Key Set,是指多个JWK组合在一起的一种格式(详见 https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41#section-5 );

JWK全称是JSON Web Key,其标准协议链接:https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41

JWK在OIDC中的主要作用是为JWT(id_token)提供加密密钥,用于加密/解密或签名/验签,是JSON格式的数据。

综上所说,掌握了如何生成JWK,再按照JWKS协议的要求组合起来即可。根据使用不同的加密算法,各种算法的JWK展示内容会有些区别,一个使用RSA算法的JWK格式的数据如下,使用jose4j生成:

{
  "kty": "RSA",
  "kid": "myoidc-keyid",
  "use": "sig",
  "alg": "RS256",
  "n": "xMNL1jDatbHWGDH_H9GZbb2R4Ck2ga-lfE6wJnno_MfqJibSqkiLTZ7P06woJFxzB0WDu2PBe-ptRNoJhEnkzL8c1mgbGOdPVCsaaDm-POGZ3gnWGcyzyuJ9s9km41Rlan3TuuC3HIniY4bmyQAZ-SLwrFk7Bfa91ui4F9S5RvM",
  "e": "AQAB",
  "d": "q73XDM65p8vgll_VUn2NUPcdQ65VUB7y76xzHjJchX8tRj2utUj6pjaPKi_jHdG_6UHouIBa2oEE_QrfMaOZ2wA6LgYPXT_a296LKoOtKKRUlgob_leHxn0FJxLOXtEHgMqDgtMQ1-eFipsFgriip94dmcLEWW3YVK_y5JwJrnE",
  "p": "7OqN19bEbMb9cr9zA98QQq9LOgvQfq5-nOvzwMsubqMosHxViRnivFz2-3LpusK7u2XEG5EljqUxZrf4lgeVmw",
  "q": "1Jy9ksoLuQ5tuc4docgLaXQWqf3iaTm3iC07AA4v9jHzvnkewWFS_PUPPcra4475Dew-y7oIENaRBMijrrWViQ",
  "dp": "HBEdhLh4xLo-RF3R7l96uauyqcnEhX1-mnCJjAmGEq1ZoK7aI0fhyJbq25cdgXaYbvb7aJilbmS7mYOp-3wiiQ",
  "dq": "NRMLn-EgHAomCgQSEZjGgISiQ71FD8-AqVXhLJpeDq9ZkWra6ptJBCyIqXxCOBwwv_EBnAnMiaeGfBM6CRr7QQ",
  "qi": "VCdAeu4h25X_kM69Netc3SKPo5P-1VjeSHz0Ko0QuSMF0INc6H2yi7hZgZuYANVaTF1Ipf6lWfHBLpuRGh3lXw"
}

JWK数据的格式具体可参见 https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41#section-4

主要属性:kty(Key类型),use(两个可选值:sig or enc),alg(具体的算法),kid(Key的唯一标识id)

 

有了JWK数据,只需要按照JWKS的格式组装成对应的JSON数据即可,以上面的JWK组装的JWKS数据如下:

{
  "keys": [
    {
      "p": "7OqN19bEbMb9cr9zA98QQq9LOgvQfq5-nOvzwMsubqMosHxViRnivFz2-3LpusK7u2XEG5EljqUxZrf4lgeVmw",
      "kty": "RSA",
      "q": "1Jy9ksoLuQ5tuc4docgLaXQWqf3iaTm3iC07AA4v9jHzvnkewWFS_PUPPcra4475Dew-y7oIENaRBMijrrWViQ",
      "d": "q73XDM65p8vgll_VUn2NUPcdQ65VUB7y76xzHjJchX8tRj2utUj6pjaPKi_jHdG_6UHouIBa2oEE_QrfMaOZ2wA6LgYPXT_a296LKoOtKKRUlgob_leHxn0FJxLOXtEHgMqDgtMQ1-eFipsFgriip94dmcLEWW3YVK_y5JwJrnE",
      "e": "AQAB",
      "use": "sig",
      "kid": "myoidc-keyid",
      "qi": "VCdAeu4h25X_kM69Netc3SKPo5P-1VjeSHz0Ko0QuSMF0INc6H2yi7hZgZuYANVaTF1Ipf6lWfHBLpuRGh3lXw",
      "dp": "HBEdhLh4xLo-RF3R7l96uauyqcnEhX1-mnCJjAmGEq1ZoK7aI0fhyJbq25cdgXaYbvb7aJilbmS7mYOp-3wiiQ",
      "alg": "RS256",
      "dq": "NRMLn-EgHAomCgQSEZjGgISiQ71FD8-AqVXhLJpeDq9ZkWra6ptJBCyIqXxCOBwwv_EBnAnMiaeGfBM6CRr7QQ",
      "n": "xMNL1jDatbHWGDH_H9GZbb2R4Ck2ga-lfE6wJnno_MfqJibSqkiLTZ7P06woJFxzB0WDu2PBe-ptRNoJhEnkzL8c1mgbGOdPVCsaaDm-POGZ3gnWGcyzyuJ9s9km41Rlan3TuuC3HIniY4bmyQAZ-SLwrFk7Bfa91ui4F9S5RvM"
    }
  ]
}

以上的JWK,JWKS的示例的完整代码(JAVA实现)可在此查看 https://gitee.com/mkk/MyOIDC/blob/master/myoidc-server/src/test/java/myoidc/server/infrastructure/JWKSTest.java。代码中还包括除RSA外,ECC,OCT的示例实现。

 

JWK本身未定义一种新的加密算法或变种,只是定义一种JSON格式的数据展示(更符合互联网传输),其底层实现依旧依赖PKI体系的技术,如x509,公钥/私钥体系。在其协议介绍中能更好地体现出来。

20200920160628

 

如果是使用非JAVA语言,也可以使用对应的PKI技术生成JWK,如openssl;

一个使用JDK自带的 keytool 工具生成的示例如下:

keytool -genkeypair -alias oidc-demo -validity 180 -keyalg RSA -keypass oidc-demo -keystore oidc-demo.jks -storepass oidc-demo

命令输入后依次填写DN的各类信息,即可生成 oidc-demo.jks文件,密码为 oidc-demo,文件中有私钥公钥等信息。

若需要导出为 x509格式数据,使用keytool命令:

keytool -exportcert -keystore oidc-demo.jks -rfc -file oidc-demo.cer -alias oidc-demo

输入密码后生成的 oidc-demo.cer文件为x509格式。

 

OIDC中的术语介绍与示例

 

以下对OIDC主要的术语进行说明,并与OAuth2作相关对比。

  • EU End-User, 指用户(一般指有账号的个体,资源的拥有者)
  • RP Relying Party, 信任方(一般是一个软件应用), 理解为OAuth2中的客户端(client)即可, 需要在OP中注册
  • OP OpenID Provider, 能提供对EU进行认证的服务端(可理解为OAuth2中授权服务端),
    提供RP的注册(或管理)能力,EU管理,ID Token的签发与管理等。OIDC中的核心组件。
    OIDC-Server 就是一个OP的参考实现
  • ID Token 格式为JSON Web Token(JWT),
    包含EU认证授权信息等. 有关JWT请访问 https://jwt.io/除了JWT, 知道以下概念对掌握OIDC会很有帮助
    JSON Web Key(JWK),
    JSON Web Encryption(JWE),
    JSON Web Algorithm(JWA),
    JSON Web Signature(JWS)
  • UserInfo Endpoint EU信息接口(OAuth2保护), 在RP获取到access_token后,可调用此接口获取EU的详细信息.
    OIDC要求此接口必须使用https访问(更安全);一般在OP中提供的Discovery中获取
  • Claim EU信息的载体, 可在id_token中获取,主要包括认证信息与授权信息,可根据实际扩展
  • 查看完整术语定义请访问 https://openid.net/specs/openid-connect-core-1_0.html#Terminology

OIDC协议中抽象了一个主要的操作步骤与流程示意图,如下:

20200920150958

下面以AWS提供的OIDC服务来举例说明。 假设你现在有一个应用A想集成使用AWS的OIDC服务,在实现OIDC之前需要先做几件事

  1. 应用A(RP)先去AWS(OP)中注册一个应用并获取对应的client_id, client_secret,public key等信息( 一般这步骤是开发者去完成)。注册时一般需要提供应用名称,redirect_uri等信息。
  2. 根据AWS提供的实现文档进行应用A(RP)的集成(也是开发者完成)。

OIDC流程在此示例中的关系图如下:

20200920151748

结束。

 

详细完整的使用请访问 MyOIDC: https://gitee.com/mkk/MyOIDC

 

acme.sh申请Let’s Encrypt免费SSL证书注意

Let’s Encrypt免费SSL证书有效期3个月(本博客使用的SSL证书就是使用acme.sh实现的免费SSL证书)。

由于DNS解析使用了 dnspod,在签发时按正常流程会报以下异常

4FDDB5E8-035A-4894-A1D6-0D031910D2C7

参考文章 https://bugxia.com/33.html 中提到的注意点,需要先从dnspod获取API Token的 id与key。然后按以下步骤操作

export DP_Id=“xxx”
export DP_Key=“xxx”

./acme.sh –issue -d andaily.com -d *.andaily.com –dns dns_dp

结果可以成功,且此方法还省去了手动添加 TXT记录在DNS上的步骤。

成功的输出如下图所求:

截屏2020-09-20 下午2.34.50

结束。

 

 

spring-oauth-server 2.0.2 发布,扩展 OAuth2 Server

spring-oauth-server在2020-06-04更新发布了2.0.2版本,spring-oauth-server是Spring与Oauth2整合示例。

此版本更新内容如下:

1.Fix CVE-2019-3778, use spring-security-oauth 2.3.5.RELEASE

2.解决数据库多条 access_token问题,增加唯一约束

3.升级Spring-Boot版本为2.1.4.RELEASE

 

2.0.2版本release链接: https://gitee.com/shengzhao/spring-oauth-server/releases

 

MyOIDC v1.1.1 发布,基于 OIDC 协议的参考实现,根据各类库提供实现参考

MyOIDC  –基于OIDC协议的参考实现,根据各类库提供实现参考 。正式版本v1.1.1发布,主要更新内容:

  1. 修改启动方法为jar直接运行
  2. 更新How-To-Use.txt
  3. AccessToken使用时允许获取更详细用户信息
  4. 增加 JWKS test与使用说明示例
  5. jose4j升级使用0.7.1版本

openid

GitHub:   https://github.com/monkeyk/MyOIDC/tree/v1.1.1

Gitee:  https://gitee.com/mkk/MyOIDC/tree/v1.1.1

OAuth2中 access_token,refresh_token的各类配置与使用场景FAQ

过去几年的OAuth2经历与使用,总结一下,记录有关 access_token, refresh_token的各类配置与场景适应,到此以自问自答的形式把这些琐碎的点总结下来。

说明:以下问答中的截图或表等信息以 spring-oauth-server 中配置为参考。

 

> 问:可以设置永不过期的 refresh_token吗?

答:可以,需要按以下步骤操作:首先在client_details定义中不设置 refresh_token_validity 字段值(即默认null),其次在配置OAuth2的DefaultTokenServices时将refreshTokenValiditySeconds属性值配置为0或小于0(默认为30天,若想修改为其他默认值也在此配置),关键代码看下图

111

 

> 问:可以设置永不过期的 access_token吗?

答:可以,但不推荐使用(因为永不过期意味着除手动清除外无安全性可言);若需要按以下步骤操作:首先在client_details定义中不设置 access_token_validity字段值(即默认null),其次在配置OAuth2的DefaultTokenServices时将accessTokenValiditySeconds属性值配置为0或小于0(默认为12小时,若想修改为其他默认值也在此配置),关键代码看下图

8888

 

> 问:refresh_token功能可以禁用吗?

答:可以,首先要求client_details的grant_type不支持 refresh_token,即authorized_grant_types字段中无 refresh_token,其次在配置OAuth2的DefaultTokenServices时将supportRefreshToken配置为false即可,关键代码看下图

222

 

> 问:默认的 access_token有效时间是多少?默认的 refresh_token有效时间是多少?在哪看

答:默认access_token有效时间为12小时,默认refresh_token有效时间为30天,这两默认值在OAuth2的DefaultTokenServices中定义的,如下图:

3333

若想改变默认值,只需要在定义client_details是修改字段access_token_validity (access_token有效时间)与 refresh_token_validity (refresh_token有效时间),单位为:秒

 

> 问:可否在每次调用 refresh token操作时重新生成一个 refresh_token值?若可以如何做

答:可以支持此功能,具体为在配置OAuth2的DefaultTokenServices时将reuseRefreshToken属性配置为fasle即可(默认true),关键代码看下图

44

 

> 问:能否在生成access_token后加自己扩展的代码逻辑?若能如何做

答:可以加自己的扩展逻辑。首先需要写一个类实现接口 TokenEnhancer.java ,实现方法 enhance方法,一示例如下:

555

其次在配置 OAuth2的DefaultTokenServices时增加tokenEnhancer属性配置,关联扩展实现的类即可,关键代码如下

6666

实际上Spring Security OAuth2中实现JWT从而支持OIDC流程就是扩展TokenEnhancer.java来实现的(有一个实现类 JwtAccessTokenConverter.java)

 

> 问:生成access_token值是一UUID值吧?能否不使用UUID值,想扩展如何办?

答:确实生成的access_token是一UUID值,若要扩展不使用UUID,需要这样做:首先写一个子类继承DefaultTokenServices.java,其次将DefaultTokenServices.java的源代码复制到扩展的子类中,并修改createAccessToken方法与createRefreshToken方法中的代码(如此做是因为两方法定义的private的),关键代码如下

7777

另一种办法是通过扩展TokenEnhancer.java 来实现(详细见上一问答)

 

> 问:如何提高 access_token, refresh_token的性能,默认存储在数据库中在高并发大数据时数据库连接会成为性能瓶颈的

答:在大数据高并发环境时,建议使用Redis,将access_token, refresh_token存储在Redis中实现,具体在配置OAuth2的TokenStore时使用子类 RedisTokenStore(默认使用的子类为JdbcTokenStore),更多信息请查看 http://andaily.com/blog/?p=19776

 

 

 

 

MyOIDC v1.1.0 发布–基于OIDC协议的参考实现,根据各类库提供实现参考

MyOIDC  –基于OIDC协议的参考实现,根据各类库提供实现参考 。第1个正式版本v1.1.0发布,主要更新内容:

  1. 实现OIDC协议的主要流程,包括 DiscoveryEndpoint等各类Endpoint提供实现参考;实现OIDC中主要的认证授权流程;JWKS API实现等。
  2. 实现myoidc-server, myoidc-client 两模块的各类功能细节,能实际使用,形成操作闭环。
  3. openid

V1.1.0 代码库链接:

GitHub:  https://github.com/monkeyk/MyOIDC/tree/v1.1.0

Gitee: https://gitee.com/mkk/MyOIDC/tree/v1.1.0/

 

SpringMVC校验数据方式总结(含示例源码)

SpringMVC对于提交表单(form submit)的数据校验与异常信息回显,个人常用有两种方式

方式一:使用Validator接口,实现Validator接口并编写校验的代码,

1

Validator是一个单独类处理,示例源码链接:https://gitee.com/shengzhao/spring-oauth-server/tree/1.0/src/main/java/com/monkeyk/sos/web/controller

在使用时示例如下:

2

示例源码链接:https://gitee.com/shengzhao/spring-oauth-server/blob/1.0/src/main/java/com/monkeyk/sos/web/controller/UserController.java

此方式简单明了,validator与controller分开。

 

方式二:使用全注解,需要扩展实现一些业务需要的数据校验(此文重点)

不需要额外增加一个类来处理,注解加在Model对象属性中,

3

UserFormDto是一个Model对象,@Size, @NotBlank,@Length, @Email为 java validation API中定义的注解类型,

示例源码链接:https://gitee.com/mkk/MyOIDC/blob/1.1.0/myoidc-server/src/main/java/myoidc/server/service/dto/UserFormDto.java

@UsernameValidation 是扩展自定义实现的数据校验(主要增加业务逻辑,如此处username要求唯一),其具体实现为:

5

其实现的关键是 UsernameValidator.java的实现,如下:

6

UsernameValidator实现ConstraintValidatior接口并提供实现,在isValid()方法中实现具体业务逻辑,返回true表示校验成功

示例源码链接:https://gitee.com/mkk/MyOIDC/tree/1.1.0/myoidc-server/src/main/java/myoidc/server/service/validation

 

——————————–

对于表单校验异常后的展示,SpringMVC提供了一套页面标签实现,如果是JSP页面,示例如下:

8

示例源码链接:https://gitee.com/shengzhao/spring-oauth-server/blob/config/src/main/webapp/WEB-INF/jsp/user_form.jsp

如果是 SpringBoot中的 thymeleaf,示例如下:

7

示例源码链接:https://gitee.com/mkk/MyOIDC/blob/1.1.0/myoidc-server/src/main/webapp/WEB-INF/view/admin/user_form.html

(页面展示的标签方式有多种形式,可根据实际需要进行选择,如可以针对单个的字段进行异常显示)

 

浅层大脑皮层之思索

2.20  0:54  周四一大堆杂事工作,各类的不满意不经意不成长的人少些许是用心的,结束此日。

积极向前看,工作在多类事务并行而缺少过多的专注而言不衷己的心态,看书也是浅层大脑皮层在活动,且到了应该睡觉休息养身,一股冷意从空气中流传,下雨了的夜晚更多几分的愁意,思于无从坚持以每日写日记而想出记录每日睡觉时间保证能每日记录一行或几行在本子中,也如此坚持了最近几年的日记于今日,还继续写。

特殊年景的时日,疫情不会消失很快需跟上不断变化的光景且不断学习—-主要靠看书与工作中的接触。快了下周是有可能再次回来办公区办公楼,回归想要的传统生活节奏罢!

做菜三二支,爬上床被盖上被子,暖和地美美睡去而不便在9.00时起床方算美的早晨,可也只能静待周末的时光来临,每一日都一样都在小区里,不扩散不乱走,我不知,人不知病毒在隐藏中随时想发起的攻击,不堪一击的人类因转轮回没有主次,都于命中神中注定,无欲望有欲望控制不了的欲望,世间就是活不明白,都活不明白时也就打开潘多拉盒子推倒重来,轮转!

都是在没有疲倦地向前发展着,终点已经固定,无多的叫喊,思索,无望,离别等等一多多都是人的一番感情的展露罢。

2.21 0:40  周五,在家工作的类似最后一天也感受到上班那类感受:中午不睡,下午崩溃。去办健康证明(因为疫情),去洗车终于成功还免费消毒国内空间,干净的车子是好心情的一体会。

计划着的下周回到办公室开始2020继续的日子,虽说大可能继续等待着又一个14天的光景—-隔离时光;头发也理了,也洗澡了,做不完的工作压过头顶也得健康地顶着向前一直走—-此生活之工作真谛。

看了《人间失格》一书在晚上扒在床上,怕看得无法自拔而强迫自己睡觉,理智而合理,细水长流一般地关灯,上眼药水,然后真睡去。