前言

《Spring Microservices in Action》

《Spring Cloud Alibaba 微服务原理与实战》

《B站 尚硅谷 SpringCloud 框架开发教程 周阳》

JWT 为 OAuth2 令牌提供规范标准,并且可以自定义 JWT 令牌;


1. JWT 令牌存储基础知识

1.1 JSON Web Token

  • OAuth2 是一个基于令牌的验证框架,但它并没有为如何定义其规范中的令牌提供任何标准;
  • 由此出现了 JSON Web Token ( JWT )新标准;
  • JWT是因特网工程任务组( Internet Engineering Task Force, IETF )提出的开放标准(RFC-7519 ),旨在为 0Auth2令牌提供标准结构
    • JWT 令牌的特点:小巧、密码签名、自包含、可扩展;
  • Spring Cloud Security 为 JWT 提供了开箱即用的支持;

2. 构建使用 JWT 令牌存储的 OAuth2 服务器

2.1 引入 pom.xml 依赖文件

<!-- JWT OAuth2 库 -->
<dependency>
<groupid>org.springframework.security</groupid>
<artifactid>spring-security-jwt</artifactid>
</dependency> <!--security 通用安全库-->
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-security</artifactid>
</dependency>
<!--oauth2.0-->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>

2.2 创建 JWT 令牌存储

在 security 包或 config 包下;

@Configration
public class JWTTokenStoreConfig{
@Autowired
private ServiceConfig serviceConfig; @Bean
public TokenStore tokenStore(){
return new JwtTokenStore(jwtAccessTokenConverter());
} //用于从出示给服务的令牌中读取数据
@Bean
@Primary //用于告诉 Spring,如果有多个特定类型的 bean(本例中为 DefaultTokenService),那么使用 @Primary 标注的 Bean 类型自动注入
public DefaultTikenServices tokenServices(){
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
} @Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
//在 JWT 和 OAuth2 服务器之间充当翻译
JwtAccessTokenConverter conver = new JwtAccessTokenConverter();
//定义将用于签署令牌的签名密钥
conver.setSigningKey(serviceConfig.getJwtSigningKey());
} @Bean
public TokenEnhancer jwtTokenEnhancer(){
return new JWTTokenEnhancer();
} }
  • 该类用于定义 Spring 将如何管理 JWT 令牌的创建、签名和翻译;

2.3 将 JWT 挂载到验证服务中

@Configuration
public class JWTOAuth2Config extends AuthorizationServerConfigurerAdapter { @Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private TokenStore tokenStore;
@Autowired
private DefaultTokenServices tokenServices;
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Autowired
private TokenEnhancer jwtTokenEnhancer; @Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtTokenEnhancer, jwtAccessTokenConverter)); endpoints.tokenStore(tokenStore) //JWT,5.2中定义的命令存储将在这里注入
.accessTokenConverter(jwtAccessTokenConverter) //JWT,钩子,用于告诉 Spring Security OAuth2 代码使用 JWT
.tokenEnhancer(tokenEnhancerChain) //JWT
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
} @Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory()
.withClient("eagleeye")
.secret("thisissecret")
.authorizedGrantTypes("refresh_token", "password", "client_credentials")
.scopes("webclient", "mobileclient");
}
}

3. 在受保护服务中使用 JWT

3.1 引入 pom.xml 依赖文件

  • 同 OAuth2 服务器依赖;
<!-- JWT OAuth2 库 -->
<dependency>
<groupid>org.springframework.security</groupid>
<artifactid>spring-security-jwt</artifactid>
</dependency> <!--security 通用安全库-->
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-security</artifactid>
</dependency>
<!--oauth2.0-->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>

3.2 在受保护服务中创建 JWTTokenStoreConfig 类

  • 同本篇《5.2 创建 JWT 令牌存储》;
@Configuration
public class JWTTokenStoreConfig { @Autowired
private ServiceConfig serviceConfig;
//JWT
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
} //JWT
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
} //JWT
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(serviceConfig.getJwtSigningKey());
return converter;
} }

3.3 创建自定义的 RestTemplate 类以注入 JWT 令牌

可以在主程序类里,也可以在主程序类所在包及其子包里;

@Primary
@Bean
public RestTemplate getCustomRestTemplate() {
RestTemplate template = new RestTemplate();
List interceptors = template.getInterceptors();
if (interceptors == null) {
//UserContextInterceptor 会将 Authorization 首部注入每个 REST 调用
template.setInterceptors(Collections.singletonList(new UserContextInterceptor()));
} else {
interceptors.add(new UserContextInterceptor());
template.setInterceptors(interceptors);
}
return template;
}

3.4 UserContextInterceptor 将注入 JWT 令牌到 REST 调用

public class UserContextInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(
HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException { HttpHeaders headers = request.getHeaders();
headers.add(UserContext.CORRELATION_ID, UserContextHolder.getContext().getCorrelationId());
//将授权令牌添加到 HTTP 首部
headers.add(UserContext.AUTH_TOKEN, UserContextHolder.getContext().getAuthToken()); return execution.execute(request, body);
}
}

3.5 扩展 JWT 令牌(也叫自定义、增强)

  • 通过向被保护服务添加一个 Spring OAuth2 令牌增强类,扩展 JWT 令牌;
//需要扩展 TokenEnhancer 类
public class JWTTokenEnhancer implements TokenEnhancer {
@Autowired
private OrgUserRepository orgUserRepo; //基于用户名查找用户的组织 ID
private String getOrgId(String userName){
UserOrganization orgUser = orgUserRepo.findByUserName( userName );
return orgUser.getOrganizationId();
} //进行这种增强,需要覆盖 enhance() 方法
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
Map<String, Object> additionalInfo = new HashMap<>();
String orgId = getOrgId(authentication.getName()); additionalInfo.put("organizationId", orgId);
//所有附加属性放在 HashMap 中,并设置在传入该方法的 accessToken 变量上
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}

3.6 告诉被保护服务使用 JWTTokenEnhancer 类

  • 首先需要公开 TokenEnhancer 类;
@Bean
public TokenEnhancer jwtTokenEnhancer() {
return new JWTTokenEnhancer();
}
  • TokenEnhancer 类挂钩在验证服务中。类似《2.3 将 JWT 挂载到验证服务中》;
@Configuration
public class JWTOAuth2Config extends AuthorizationServerConfigurerAdapter { @Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private TokenStore tokenStore;
@Autowired
private DefaultTokenServices tokenServices;
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
//自动装配 TokenEnhancer 类
@Autowired
private TokenEnhancer jwtTokenEnhancer; @Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//允许开发人员挂钩多个令牌增强器
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtTokenEnhancer, jwtAccessTokenConverter)); endpoints.tokenStore(tokenStore) //JWT
.accessTokenConverter(jwtAccessTokenConverter) //JWT
.tokenEnhancer(tokenEnhancerChain) //JWT,将令牌增强器链挂钩到传入 configure() 方法的 endpoints 参数
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
} @Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("eagleeye")
.secret("thisissecret")
.authorizedGrantTypes("refresh_token", "password", "client_credentials")
.scopes("webclient", "mobileclient");
}
}

3.7 使用 JJWT 库在 Zuul 网关里解析 JWT 令牌中解析自定义字段

以下配置均在在 Zuul 网关服务里进行;

3.7.1 在 Zuul 网关里添加 pom.xml 依赖文件

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>

3.7.2 添加一个新方法,用来解析 serviceId

private String getOrganizationId(){
String result="";
if (filterUtils.getAuthToken()!=null){
//从 HTTP 首部 Authorization 解析出令牌
String authToken = filterUtils.getAuthToken().replace("Bearer ","");
try {
//传入用于签署令牌的签名密钥,使用 JWTS 类解析令牌
Claims claims = Jwts.parser()
.setSigningKey(serviceConfig.getJwtSigningKey().getBytes("UTF-8"))
.parseClaimsJws(authToken).getBody();
//从令牌中提取 xxxId
result = (String) claims.get("organizationId");
}
catch (Exception e){
e.printStackTrace();
}
}
return result;
}

最后

新人制作,如有错误,欢迎指出,感激不尽!
欢迎关注公众号,会分享一些更日常的东西!
如需转载,请标注出处!

微服务架构 | 7.2 构建使用 JWT 令牌存储的 OAuth2 安全认证的更多相关文章

  1. 微服务架构 - 基于Harbor构建本地镜像仓库

    之前写过<搭建docker本地镜像仓库并提供权限校验及UI界面>文章,然后有同仁评论道这样做太复杂了,如果Harbor来搭建会更简单同时功能也更强大.于是抽时间研究了基于Harbor构建本 ...

  2. 微服务架构 | 7.1 基于 OAuth2 的安全认证

    目录 前言 1. OAuth2 基础知识 1.1 安全性的 4 个组成部分 1.2 OAuth2 的工作原理 1.3 OAuth2 规范的 4 种类型的授权 1.4 OAuth2 的优势 1.5 OA ...

  3. Net分布式系统之五:微服务架构

    因工作较忙,抽时间将框架遇到的问题和框架升级设计进行记录. 一.背景&问题 之前框架是一个基于SOA思想设计的分布式框架.各应用通过服务方式提供使用,服务之间通信是RPC方式调用,具体实现基于 ...

  4. 使用微服务架构思想,设计部署OAuth2.0授权认证框架

    1,授权认证与微服务架构 1.1,由不同团队合作引发的授权认证问题 去年的时候,公司开发一款新产品,但人手不够,将B/S系统的Web开发外包,外包团队使用Vue.js框架,调用我们的WebAPI,但是 ...

  5. 【转】「Chris Richardson 微服务系列」微服务架构的优势与不足

    Posted on 2016年5月4日 编者的话|本文来自 Nginx 官方博客,是微服务系列文章的第一篇,主要探讨了传统的单体式应用的不足,以及微服务架构的优势与挑战. 作者介绍:Chris Ric ...

  6. 微服务实战(一):微服务架构的优势与不足 - DockOne.io

    原文:微服务实战(一):微服务架构的优势与不足 - DockOne.io [编者的话]本文来自Nginx官方博客,是微服务系列文章的第一篇,主要探讨了传统的单体式应用的不足,以及微服务架构的优势与挑战 ...

  7. 微服务架构 技能图谱skill-map

    # 微服务架构 技能图谱 ## 理论基础### 概念#### 多微合适 - 非代码函数 - 非重写时间 - 适合团队最重要 - 独立业务属性 - 全功能团队 #### 进程隔离 - 服务运行在独立的进 ...

  8. 基于spring boot2.0+spring security +oauth2.0+ jwt微服务架构

    github地址:https://github.com/hankuikuide/microservice-spring-security-oauth2 项目介绍 该项目是一个演示项目,主要演示了,基于 ...

  9. Spring Cloud构建微服务架构(一)服务注册与发现

    Spring Cloud简介 Spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中的配置管理.服务发现.断路器.智能路由.微代理.控制总线.全局锁 ...

随机推荐

  1. B. Petya and Exam

    B. Petya and Exam 题目链接 题意 给你一串字符,在这个串中所有出现的字符都是\(good\)字符,未出现的都是\(bad\)字符, 然后给你另一串字符,这个字符串中有两个特殊的字符, ...

  2. 1122 机器人走方格 V4

    1122 机器人走方格 V4 基准时间限制:1 秒 空间限制:131072 KB  四个机器人a b c d,在2 * 2的方格里,一开始四个机器人分别站在4个格子上,每一步机器人可以往临近的一个格子 ...

  3. 【Netty技术专题】「原理分析系列」Netty强大特性之ByteBuf零拷贝技术原理分析

    零拷贝Zero-Copy 我们先来看下它的定义: "Zero-copy" describes computer operations in which the CPU does n ...

  4. 亲测:三个值得练手的Java实战项目

    测试奇谭,BUG不见. 大家好,我是谭叔. 一提到编码,很多小伙伴便感到头疼,特别是半路转行的小伙伴或者没有系统学习过计算机基础的小伙伴. 对于想学而不知道怎么学的小伙伴,我可以分享下我的策略: 刷一 ...

  5. CS5265替代LT8711设计TYPEC转HDMI 4K高清投屏方案|LT8711龙迅替代方案

    龙迅LT8711是一款Type-C/DP1.2 to HDMI2.0方案芯片.LT8711HE是一款高性能Type-C/DP1.2至HDMI2.0转换器,设计用于将USB typec或DP1.2源连接 ...

  6. springboot配置health接口

    springboot配置health接口 spring-boot-starter-actuator 健康监控配置及使用 这样是可以看到一些结果的 如果在配置文件中用了下面这个,也是可以生效的 # 不进 ...

  7. java -jar 指定logback.xml、application.yaml

    java -jar 指定logback.xml -Dlogging.config="C:\logbacs\logback.xml" 示例:java -jar   -Dlogging ...

  8. 【Linux】Linux安装JDK

    1.下载linux版本的JDK1.8 链接:JDK下载链接 提取码:fxn4 链接:Xshell下载链接 提取码:439l 2.使用Xshell连接虚拟机,上传文件 在 /usr目录下创建一个新文件夹 ...

  9. Python_类型转换

    列表与字符串互相转换 join方法将list转换为string _list = ["a", "b", "c"] # 以".&quo ...

  10. Python3.7 发送邮件 报‘[WinError 10061] 由于目标计算机积极拒绝,无法连接’错误的解决方法

    背景: 最近在练习Python 的邮件发送功能 照着教程写了一个简单的demo 结果运行时报如下错误:[WinError 10061] 由于目标计算机积极拒绝,无法连接. 如图: 解决路径如下: St ...