SpringCloud Alibaba微服务实战十七 - JWT认证

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: SpringCloud Alibaba微服务实战十七 - JWT认证

概述


在 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)点就叫去中心化。


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相关的数据表

oauth2相关数据表

使用jwt的话只需要在数据库存储一下client信息即可,所以我们只需要保留数据表oauth_client_details。

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


认证服务 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。

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。

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

校验代码执行顺序图

建议大家在 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后项目的流程架构如下:

流程架构

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

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

  • jwt是一次性的,一旦token被签发,那么在到期时间之前都是有效的,无法废弃。如果你中途修改了用户权限需要更新信息那就只能重新签发一个jwt,但是旧的jwt还是可以正常使用,使用旧的jwt拿到的信息也就是过时的。
  • jwt 包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了防止盗用,jwt的有效时间不应该设置过长。


如果本文对你有帮助,别忘记给我个三连:点赞,转发,评论咱们下期见!

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

目录
相关文章
|
11天前
|
Dubbo Java 应用服务中间件
Spring Cloud Dubbo:微服务通信的高效解决方案
【10月更文挑战第15天】随着信息技术的发展,微服务架构成为企业应用开发的主流。Spring Cloud Dubbo结合了Dubbo的高性能RPC和Spring Cloud的生态系统,提供高效、稳定的微服务通信解决方案。它支持多种通信协议,具备服务注册与发现、负载均衡及容错机制,简化了服务调用的复杂性,使开发者能更专注于业务逻辑的实现。
30 2
|
14天前
|
Dubbo Java 应用服务中间件
Dubbo学习圣经:从入门到精通 Dubbo3.0 + SpringCloud Alibaba 微服务基础框架
尼恩团队的15大技术圣经,旨在帮助开发者系统化、体系化地掌握核心技术,提升技术实力,从而在面试和工作中脱颖而出。本文介绍了如何使用Dubbo3.0与Spring Cloud Gateway进行整合,解决传统Dubbo架构缺乏HTTP入口的问题,实现高性能的微服务网关。
|
11天前
|
JSON Java 数据格式
【微服务】SpringCloud之Feign远程调用
本文介绍了使用Feign作为HTTP客户端替代RestTemplate进行远程调用的优势及具体使用方法。Feign通过声明式接口简化了HTTP请求的发送,提高了代码的可读性和维护性。文章详细描述了Feign的搭建步骤,包括引入依赖、添加注解、编写FeignClient接口和调用代码,并提供了自定义配置的示例,如修改日志级别等。
30 1
|
14天前
|
人工智能 文字识别 Java
SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?
尼恩,一位拥有20年架构经验的老架构师,通过其深厚的架构功力,成功指导了一位9年经验的网易工程师转型为大模型架构师,薪资逆涨50%,年薪近80W。尼恩的指导不仅帮助这位工程师在一年内成为大模型架构师,还让他管理起了10人团队,产品成功应用于多家大中型企业。尼恩因此决定编写《LLM大模型学习圣经》系列,帮助更多人掌握大模型架构,实现职业跃迁。该系列包括《从0到1吃透Transformer技术底座》、《从0到1精通RAG架构》等,旨在系统化、体系化地讲解大模型技术,助力读者实现“offer直提”。此外,尼恩还分享了多个技术圣经,如《NIO圣经》、《Docker圣经》等,帮助读者深入理解核心技术。
SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?
|
28天前
|
监控 Java 对象存储
监控与追踪:如何利用Spring Cloud Sleuth和Netflix OSS工具进行微服务调试
监控与追踪:如何利用Spring Cloud Sleuth和Netflix OSS工具进行微服务调试
37 1
|
21天前
|
负载均衡 算法 Nacos
SpringCloud 微服务nacos和eureka
SpringCloud 微服务nacos和eureka
49 0
|
2月前
|
SpringCloudAlibaba API 开发者
新版-SpringCloud+SpringCloud Alibaba
新版-SpringCloud+SpringCloud Alibaba
|
3月前
|
资源调度 Java 调度
Spring Cloud Alibaba 集成分布式定时任务调度功能
定时任务在企业应用中至关重要,常用于异步数据处理、自动化运维等场景。在单体应用中,利用Java的`java.util.Timer`或Spring的`@Scheduled`即可轻松实现。然而,进入微服务架构后,任务可能因多节点并发执行而重复。Spring Cloud Alibaba为此发布了Scheduling模块,提供轻量级、高可用的分布式定时任务解决方案,支持防重复执行、分片运行等功能,并可通过`spring-cloud-starter-alibaba-schedulerx`快速集成。用户可选择基于阿里云SchedulerX托管服务或采用本地开源方案(如ShedLock)
105 1
|
19天前
|
JSON SpringCloudAlibaba Java
Springcloud Alibaba + jdk17+nacos 项目实践
本文基于 `Springcloud Alibaba + JDK17 + Nacos2.x` 介绍了一个微服务项目的搭建过程,包括项目依赖、配置文件、开发实践中的新特性(如文本块、NPE增强、模式匹配)以及常见的问题和解决方案。通过本文,读者可以了解如何高效地搭建和开发微服务项目,并解决一些常见的开发难题。项目代码已上传至 Gitee,欢迎交流学习。
Springcloud Alibaba + jdk17+nacos 项目实践
|
6天前
|
消息中间件 自然语言处理 Java
知识科普:Spring Cloud Alibaba基本介绍
知识科普:Spring Cloud Alibaba基本介绍
24 2

热门文章

最新文章