SpringCloud Alibaba微服务实战十七

Easter79
• 阅读 472

SpringCloud Alibaba微服务实战十七

概述

在 OAuth2 体系中认证通过后返回的令牌信息分为两大类:不透明令牌(opaque tokens)透明令牌(not opaque tokens)。

不透明令牌 就是一种无可读性的令牌,一般来说就是一段普通的 UUID 字符串。使用不透明令牌会降低系统性能和可用性,并且增加延迟,因为资源服务不知道这个令牌是什么,代表谁,需要调用认证服务器获取用户信息接口才知道这个令牌是谁。
如下就是我们在资源服务器中的配置,需要指明认证服务器的接口地址。

security: oauth2: resource: user-info-uri: http://localhost:5000/user/current/get id: account-service

透明令牌的典型代表就是 JWT ,用户信息保存在 JWT 字符串中,资源服务器自己可以解析令牌不再需要去认证服务器校验令牌。

之前的章节中我们是使用了不透明令牌access_token,但考虑到在微服务体系中这种中心化的授权服务会成为瓶颈,本章我们就使用jwt来替换之前的access_token,专(zhuang)业(bi)点就叫去中心化。

SpringCloud Alibaba微服务实战十七

jwt 是什么

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519)。该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。 ”

简单点说就是一种固定格式的字符串,通常是加密的;

它由三部分组成,头部、载荷与签名,这三个部分都是json格式。

  • Header 头部:JSON方式描述JWT基本信息,如类型和签名算法。使用Base64编码为字符串

  • Payload 载荷: JSON方式描述JWT信息,除了标准定义的,还可以添加自定义的信息。同样使用Base64编码为字符串。

  1. iss: 签发者

  2. sub: 用户

  3. aud: 接收方

  4. exp(expires): unix时间戳描述的过期时间

  5. iat(issued at): unix时间戳描述的签发时间

  • Signature 签名: 将前两个字符串用 . 连接后,使用头部定义的加密算法,利用密钥进行签名,并将签名信息附在最后。

JWT可以使用对称的加密密钥,但更安全的是使用非对称的密钥,本篇文章使用的是对称加密。

代码修改

数据库

原来使用access_token的时候我们建立了7张oauth2相关的数据表

SpringCloud Alibaba微服务实战十七

oauth2相关数据表

使用jwt的话只需要在数据库存储一下client信息即可,所以我们只需要保留数据表oauth_client_details。SpringCloud Alibaba微服务实战十七

其他数据表已不再需要,大家可以删除。

认证服务 AuthorizationServerConfig

  1. 修改 AuthorizationServerConfig 中TokenStore的相关配置

`@Bean
public TokenStore tokenStore() {
    //return new JdbcTokenStore(dataSource);
        return new JwtTokenStore(jwtTokenEnhancer());
}

/**
 * JwtAccessTokenConverter
 * TokenEnhancer的子类,帮助程序在JWT编码的令牌值和OAuth身份验证信息之间进行转换。
 */
 @Bean
 public JwtAccessTokenConverter jwtTokenEnhancer(){
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        // 设置对称签名
        converter.setSigningKey("javadaily");
        return converter;
}
`

之前我们是将access_token存入数据库,使用jwt后不再需要存入数据库,所以我们需要修改存储方式。

jwt需要使用加密算法对信息签名,这里我们先使用 对称秘钥 (javadaily)来签署我们的令牌,对称秘钥当然这也以为着资源服务器也需要使用相同的秘钥。

  1. 修改 configure(AuthorizationServerEndpointsConfigurer endpoints) 方法,配置jwt

@Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {         //如果需要使用refresh_token模式则需要注入userDetailService         endpoints.authenticationManager(this.authenticationManager)                         .userDetailsService(userDetailService)                         .tokenStore(tokenStore())                         .accessTokenConverter(jwtTokenEnhancer()); }

这里主要是注入accessTokenConverter,即上面配置的token转换器。

经过上面的配置,认证服务器已经可以帮我们生成jwt token了,这里我们先使用Postman调用一下,看看生成的jwt token。

SpringCloud Alibaba微服务实战十七

image.png

从上图看出已经正常生成jwt token,我们可以将生成的jwt token拿到https://jwt.io/网站上进行解析。

如果大家对生成jwt token的逻辑不是很了解,可以在 DefaultTokenServices#createAccessToken(OAuth2Authentication authentication)JwtAccessTokenConverter#enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication)上打个断点,观察代码执行的效果。

  1. 删除认证服务器提供给资源服务器获取用户信息的接口

/**  * 获取授权的用户信息  * @param principal 当前用户  * @return 授权信息  */@GetMapping("current/get") public Principal user(Principal principal){         return principal; }

用了透明令牌jwt token后资源服务器可以直接解析验证token,不再需要调用认证服务器接口,所以此处可以直接删除。

  1. 修改jwt token有效期(可选)

    jwt token的默认有效期为12小时,refresh token的有效期为30天,如果要修改默认时间可以注入 DefaultTokenServices 并修改有效时间。

@Primary @Bean public DefaultTokenServices tokenServices(){         DefaultTokenServices tokenServices = new DefaultTokenServices();         tokenServices.setTokenEnhancer(jwtTokenEnhancer());         tokenServices.setTokenStore(tokenStore());         tokenServices.setSupportRefreshToken(true);         //设置token有效期,默认12小时,此处修改为6小时   21600         tokenServices.setAccessTokenValiditySeconds(60 * 60 * 6);         //设置refresh_token的有效期,默认30天,此处修改为7天         tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 7);         return tokenServices; }

然后在 configure() 方法中添加tokenServices

@Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {                 //如果需要使用refresh_token模式则需要注入userDetailService         endpoints.authenticationManager(this.authenticationManager)                         .userDetailsService(userDetailService)             //注入自定义的tokenservice,如果不使用自定义的tokenService那么就需要将tokenServce里的配置移到这里                         .tokenServices(tokenServices()); }

资源服务器 ResourceServerConfig

  1. 删除资源服务器中配置认证服务器的接口属性 user-info-uri

security:   oauth2:     resource:       id: account-service

  1. 注入TokenStore 和 JwtAccessTokenConverter

`@Bean
public TokenStore tokenStore() {
        return new JwtTokenStore(jwtTokenEnhancer());
}

@Bean
public JwtAccessTokenConverter jwtTokenEnhancer(){
        JwtAccessTokenConverter jwtTokenEnhancer = new JwtAccessTokenConverter();
        jwtTokenEnhancer.setSigningKey("javadaily");
        return jwtTokenEnhancer;
}`

注意:对称加密算法需要跟认证服务器秘钥保持一致,当然这里可以提取到配置文件中。 ”

  1. 添加 configure(ResourceServerSecurityConfigurer resources) 方法,加入token相关配置

@Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception {         resources.resourceId(resourceId)                         .tokenStore(tokenStore()); }

思考题:资源服务器使用jwt后从哪校验token呢?

给应用添加 @EnableResourceServer 注解后会给Spring Security的FilterChan添加一个 OAuth2AuthenticationProcessingFilterOAuth2AuthenticationProcessingFilter 会使用 OAuth2AuthenticationManager 来验证token。

校验逻辑主体代码执行顺序如下:

SpringCloud Alibaba微服务实战十七

校验代码执行顺序图

建议大家在 OAuth2AuthenticationProcessingFilter#doFilter() 处打个断点体会一下校验过程。

网关 SecurityConfig

  1. 创建 ReactiveJwtAuthenticationManager 从tokenStore加载 OAuth2AccessToken

由于原来的access_token是存储在数据库中,所以我们编写了 ReactiveJdbcAuthenticationManager 来从数据库获取access_token,现在使用jwt我们也需要定义一个jwt的相关类 ReactiveJwtAuthenticationManager,代码跟 ReactiveJdbcAuthenticationManager 一样,这里就不再贴出。

  1. 注入TokenStore 和 JwtAccessTokenConverter

`@Bean  
public TokenStore tokenStore() {
        return new JwtTokenStore(jwtTokenEnhancer());
}

@Bean  
public JwtAccessTokenConverter jwtTokenEnhancer(){
        JwtAccessTokenConverter jwtTokenEnhancer = new JwtAccessTokenConverter();
        jwtTokenEnhancer.setSigningKey("javadaily");
        return jwtTokenEnhancer;
}
`

注意:对称加密算法需要跟认证服务器秘钥保持一致,当然这里可以提取到配置文件中。

  1. 修改 SecurityConfig#SecurityWebFilterChain() 方法,替换 ReactiveJdbcAuthenticationManager

ReactiveAuthenticationManager tokenAuthenticationManager  = new ReactiveJwtAuthenticationManager(tokenStore());

只需要将tokenStore传入构造器即可。

测试

大家自行测试。

小结

使用jwt token 和 access_token 最大的区别就是资源服务器不再需要去认证服务器校验token,提升了系统整体性能,使用jwt后项目的流程架构如下:

SpringCloud Alibaba微服务实战十七

流程架构

本系列文章目前是第19篇,如果大家对之前的文章感兴趣可以移步至 http://javadaily.cn/tags/SpringCloud 查看

使用了jwt我们不仅要看到jwt的优点,也要看到它的缺点,这样我们才能根据实际场景自由选择,下面是jwt最大的两个缺点:

  • jwt是一次性的,一旦token被签发,那么在到期时间之前都是有效的,无法废弃。如果你中途修改了用户权限需要更新信息那就只能重新签发一个jwt,但是旧的jwt还是可以正常使用,使用旧的jwt拿到的信息也就是过时的。

  • jwt 包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了防止盗用,jwt的有效时间不应该设置过长。

如果本文对你有帮助,

别忘记给我个三连:

点赞,转发,评论

咱们下期见!

收藏 等于白嫖点赞 才是真情!

End

干货分享

这里为大家准备了一份小小的礼物,关注公众号,输入如下代码,即可获得百度网盘地址,无套路领取!

001:《程序员必读书籍》
002:《从无到有搭建中小型互联网公司后台服务架构与运维架构》
003:《互联网企业高并发解决方案》
004:《互联网架构教学视频》
006:《SpringBoot实现点餐系统》
007:《SpringSecurity实战视频》
008:《Hadoop实战教学视频》
009:《腾讯2019Techo开发者大会PPT》

010: 微信交流群

近期热文top

1、还不了解ETL?看看这篇文章吧!

2、数据库优化 - 实例参数优化

3、数据库优化技巧 - SQL语句优化

4、架构师之路-微服务技术选型

5、RocketMQ进阶 - 事务消息

SpringCloud Alibaba微服务实战十七

我就知道你“在看”

SpringCloud Alibaba微服务实战十七

本文分享自微信公众号 - JAVA日知录(javadaily)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k