当前位置: 首页 > news >正文

【分布式微服务专题】SpringSecurity OAuth2快速入门

目录

  • 前言
  • 阅读对象
  • 阅读导航
  • 前置知识
  • 笔记正文
    • 一、OAuth2 介绍
      • 1.1 使用场景
      • *1.2 基本概念(角色)
      • 1.3 优缺点
    • 二、OAuth2的设计思路
      • 2.1 客户端授权模式
        • 2.1.0 基本参数说明
        • 2.1.1 授权码模式
        • 2.1.2 简化(隐式)模式
        • 2.1.3 密码模式
        • 2.1.4 客户端模式
      • 2.2 令牌的使用
      • 2.3 令牌更新
    • 三、Spring Security OAuth2快速开始
      • 3.1 授权服务器的几个节点
      • 3.2 整体架构(授权码模式)
      • 3.3 代码整合(授权码模式)
      • 3.4 更新令牌
      • 3.5 基于redis存储Token
    • 四、Spring Security Oauth2整合JWT
      • 4.1 整合JWT
      • 4.2 扩展JWT中的存储内容
      • 4.3 解析JWT
  • 学习总结
  • 感谢

前言

这里面的笔记都是我这里抄抄那里抄抄得来的,然后也经过了一些简单的测试,主要是用来拓宽知识面用的,毕竟到了项目开发中,不会裸用这些技术框架,通常都是采用第三方集成框架。
另外需要说明的是,这里要介绍的Spring Security OAuth2框架应该有点过时了,在Spring官网已经没有了项目介绍,现在项目似乎已经升级,并且迁移为Spring Authorization Server项目。所以,大家看自己需要是否要继续学习Spring Authorization Server项目了。

阅读对象

  1. 了解、熟悉Spring Security
  2. 有过OAuth2使用经验

阅读导航

系列上一篇文章:《【分布式微服务专题】SpringSecurity快速入门》

前置知识

笔记正文

一、OAuth2 介绍

权威文档地址:OAuth2.0协议介绍

OAuth(Open Authorization,开放授权)是一个关于授权(authorization)的开放网络标准协议,允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容。OAuth在全世界得到广泛应用,目前的版本是2.0版。

注意:OAuth2只是一种标准协议,或者叫作:规范! 其实在我们计算机学习中,你会发现很多各种各样的规范,这些规范往往指的是一种约定,并不特指某一具体的项目,SpringSecurityOAuth2才是一个具体的项目。可别被带偏了呀

标准协议特点:

  • 简单:不管是OAuth服务提供者还是应用开发者,都很易于理解与使用
  • 安全:没有涉及到用户密钥等信息,更安全更灵活
  • 开放:任何服务提供商都可以实现OAuth,任何软件开发商都可以使用OAuth

1.1 使用场景

  • 原生app授权:app登录请求后台接口,为了安全认证,所有请求都带token信息,如果登录验证、请求后台数据。
  • 前后端分离单页面应用:前后端分离框架,前端请求后台数据,需要进行oauth2安全认证,比如使用vue、react或者h5开发的app
  • 第三方应用授权登录,比如QQ,微博,微信的授权登录

案例1:云快印
有一个【云快印】的网站,可以将用户存储在的百度网盘上的照片打印出来。用户为了使用该服务,必须让【云快印】读取自己储存在百度网盘上的照片。只有得到用户的授权,百度网盘才会同意【云快印】取这些照片。那么,【云快印】怎样获得用户的授权呢?
传统方法是,用户将自己百度网盘的用户名和密码,告诉【云快印】,后者就可以读取用户的照片了。这样的做法有以下几个严重的缺点:

  1. 【云快印】为了后续的服务,会保存用户的密码,这样很不安全
  2. 百度网盘不得不部署密码登录,而我们知道,单纯的密码登录并不安全
  3. 【云快印】拥有了获取用户储存在百度网盘服务所有资料的权力,用户没法限制【云快印】获得授权的范围和有效期
  4. 用户只有修改密码,才能收回赋予【云快印】的权力。但是这样做,会使得其他所有获得用户授权的第三方应用程序全部失效
  5. 只要有一个第三方应用程序被破解,就会导致用户密码泄漏,以及所有被密码保护的数据泄漏。

综上缺陷,这显然不是我们期待的方案。

案例2:CSDN登录
附上一张常见的OAuth2场景:CSDN接入微信QQ开放平台,用户可以通过微信QQ登录CSDN
在这里插入图片描述

*1.2 基本概念(角色)

角色:

  1. Third-party application:第三方应用程序,又称【客户端client / 三方应用】,即例子中的【云快印】
  2. HTTP service:HTTP服务提供商,简称【服务提供商】,即例子中的百度网盘
  3. Resource Owne:资源所有者,又称【用户user】
  4. User Agent:用户代理,比如浏览器
  5. Authorization server:授权服务器,即服务提供商专门用来处理认证授权的服务器
  6. Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与授权服务器,可以是同一台服务器,也可以是不同的服务器。当然,想要访问资源,需要通过认证服务器由资源所有者授权才可以访问

概念:

  1. 授权许可 (Authorization Grant):授权许可是资源所有者授权给客户端访问受保护资源的凭证。OAuth2定义了多种授权许可类型,如授权码、简化授权、密码授权和客户端凭证等
  2. 访问令牌 (Access Token):访问令牌是由授权服务器颁发给客户端的凭证,表示客户端被授权访问受保护资源的权限。客户端使用访问令牌来请求资源服务器获取受保护资源
  3. 刷新令牌 (Refresh Token):刷新令牌是可选的,用于在访问令牌过期后获取新的访问令牌。客户端可以使用刷新令牌向授权服务器请求刷新访问令牌,以延长访问权限的有效期

1.3 优缺点

优点:

  • 更安全,客户端不接触用户密码,服务器端更易集中保护
  • 广泛传播并被持续采用
  • 短寿命和封装的token
  • 资源服务器和授权服务器解耦
  • 集中式授权,简化客户端
  • HTTP/JSON友好,易于请求和传递token
  • 考虑多种客户端架构场景
  • 客户可以具有不同的信任级别

缺点:

  • 协议框架太宽泛,造成各种实现的兼容性和互操作性差
  • 不是一个认证协议,本身并不能告诉你任何用户信息

二、OAuth2的设计思路

OAuth在【客户端】与【服务提供商】之间,设置了一个授权层(authorization layer)。【客户端】不能直接登录【服务提供商】,只能登录授权层,以此将用户与客户端区分开来。【客户端】登录授权层所用的令牌(token),与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期,【客户端】登录授权层以后,【服务提供商】根据令牌的权限范围和有效期,向【客户端】开放用户储存的资料。
在这里插入图片描述
(1)用户打开客户端以后,客户端要求用户给予授权
(2)用户同意给予客户端授权
(3)客户端使用上一步获得的授权,向授权服务器申请令牌。
(4)授权服务器对客户端进行认证以后,确认无误,同意发放令牌
(5)客户端使用令牌,向资源服务器申请获取资源
(6)资源服务器确认令牌无误,同意向客户端开放资源

令牌(token)与密码(password)的作用是一样的,都可以进入系统,但是有三点差异
(1)令牌是短期的,到期会自动失效,用户自己无法修改。密码一般长期有效,用户不修改,就不会发生变化
(2)令牌可以被数据所有者撤销,会立即失效。密码一般不允许被他人撤销
(3)令牌有权限范围(scope)。对于网络服务来说,只读令牌就比读写令牌更安全。密码一般是完整权限
上面这些设计,保证了令牌既可以让第三方应用获得权限,同时又随时可控,不会危及系统安全。这就是 OAuth 2.0 的优点

2.1 客户端授权模式

客户端必须得到用户的授权(authorization grant),才能获得令牌(access token)。OAuth 2.0 对于如何颁发令牌的细节,规定得非常详细。具体来说,一共分成四种授权类型(authorization grant),即四种颁发令牌的方式,适用于不同的互联网场景。它们分别是:

  • 授权码模式(authorization code)
  • 密码模式(resource owner password credentials)
  • 简化(隐式)模式(implicit)
  • 客户端模式(client credentials)

不过不管哪一种授权方式,它们的本质流程都是:三方客户端到服务提供商系统中备案,说明自己身份,然后拿到身份识别码【客户端id + 客户端密钥】。这是为了防止令牌被滥用,没有备案过的第三方应用,是不会拿到令牌的。

2.1.0 基本参数说明

在客户端授权中,我们通常需要用到以下参数:

  • client_id:客户端应用的ID
  • client_secret:客户端秘钥
  • response_type:响应类型,可选的值有:codetoken
  • grant_type:授权模式,可选的值有authorization_codepasswordclient_credentials等,对应的是不同的授权模式
  • scope:授权范围
  • redirect_uri:回调地址
  • username:用户名称,通常是在密码模式下使用
  • password:用户密码,通常是在密码模式下使用
2.1.1 授权码模式

目前主流的三方授权模式

授权码(authorization code)方式,指的是第三方应用先申请一个授权码,然后再用该码获取令牌。

特点:

  • 安全性最高
  • 适用于有后端的Web应用(前后分离)
  • 授权码通过前端传送
  • 令牌储存在后端(避免令牌泄露)
  • 由后端与资源服务器通信

一般性流程描述:
(1)用户访问客户端,后者将前者导向授权服务器
(2)用户选择是否给予客户端授权
(3)假设用户给予授权,授权服务器将用户导向客户端事先指定的【重定向URI(redirection URI)】,同时附上一个授权码
(4)客户端收到授权码,附上早先的【重定向URI】,向授权服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见
(5)授权服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)

案例:
1) A网站提供一个链接,用户点击后就会跳转到 B 网站,授权用户数据给 A 网站使用。下面就是 A 网站跳转 B 网站的一个示意链接

https://b.com/oauth/authorize?response_type=code&            # 表示授权类型,此处固定为codeclient_id=CLIENT_ID&           # 表示客户端的id,通常为必填redirect_uri=CALLBACK_URL&     # 表示重定向的uri。接受或拒绝请求后的跳转地址scope=read					  # 表示申请的权限范围(这里是只读)state=xxx					  # 示客户端的当前状态,可以指定任意值,授权服务器会原封不动地返回这个值

2)用户跳转后,B 网站会要求用户登录,然后询问是否同意给予 A 网站授权。用户表示同意,这时 B 网站就会跳回redirect_uri参数指定的网址。跳转时,会传回一个授权码,就像下面这样

https://a.com/callback?code=AUTHORIZATION_CODE    #code参数就是授权码

3)A 网站拿到授权码以后,就可以在后端,向 B 网站请求令牌。 用户不可见,服务端行为

https://b.com/oauth/token?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&      # client_id和client_secret用来让 B 确认 A 的身份,client_secret参数是保密的,因此只能在后端发请求grant_type=authorization_code&    # 采用的授权方式是授权码code=AUTHORIZATION_CODE&          # 上一步拿到的授权码redirect_uri=CALLBACK_URL			# 令牌颁发后的回调网址

4)B 网站收到请求以后,就会颁发令牌。具体做法是向redirect_uri指定的网址,发送一段JSON数据

{    "access_token":"ACCESS_TOKEN",     # 令牌"token_type":"bearer","expires_in":2592000,"refresh_token":"REFRESH_TOKEN","scope":"read","uid":100101,"info":{...}
}
2.1.2 简化(隐式)模式

有些 Web 应用是纯前端应用,没有后端。这时就不能用上面的方式了,必须将令牌储存在前端。RFC 6749 就规定了第二种方式,允许直接向前端颁发令牌,这种方式没有授权码这个中间步骤,所以称为【(授权码)隐式(implicit)】

特点:

  • 三方应用没有服务端
  • 不需要授权码,直接颁发令牌给三方
  • 不安全,适用于安全要求不高的场景
  • 令牌时效短,通常是会话期内有效

一般性流程描述:
(1)用户访问客户端,后者将前者导向授权服务器
(2)用户选择是否给予客户端授权
(3)假设用户给予授权,授权服务器将用户导向客户端事先指定的【重定向URI(redirection URI)】,并在URI的Hash部分包含了访问令牌

案例:
1)A 网站提供一个链接,要求用户跳转到 B 网站,授权用户数据给 A 网站使用

 https://b.com/oauth/authorize?response_type=token&          # response_type参数为token,表示要求直接返回令牌client_id=CLIENT_ID&redirect_uri=CALLBACK_URL&scope=read

2)用户跳转到 B 网站,登录后同意给予 A 网站授权。这时,B 网站就会跳回redirect_uri参数指定的跳转网址,并且把令牌作为 URL 参数,传给 A 网站

https://a.com/callback#token=ACCESS_TOKEN     #token参数就是令牌,A 网站直接在前端拿到令牌
2.1.3 密码模式

如果你高度信任某个应用,RFC 6749 也允许用户把用户名和密码,直接告诉该应用。该应用就使用你的密码,申请令牌,这种方式称为【密码式(password)】。
在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而授权服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。

特点:

  • 用户必须把自己的密码给客户端,但是客户端不得储存密码

一般性流程描述:
(1)用户向客户端提供用户名和密码
(2)客户端将用户名和密码发给授权服务器,向后者请求令牌
(3)授权服务器确认无误后,向客户端提供访问令牌

案例:
1)A 网站要求用户提供 B 网站的用户名和密码,拿到以后,A 就直接向 B 请求令牌。整个过程中,客户端不得保存用户的密码

 https://oauth.b.com/token?grant_type=password&       # 授权方式是"密码式"username=USERNAME&password=PASSWORD&client_id=CLIENT_ID

2)B 网站验证身份通过后,直接给出令牌。注意,这时不需要跳转,而是把令牌放在 JSON 数据里面,作为 HTTP 回应,A 因此拿到令牌

2.1.4 客户端模式

客户端模式(Client Credentials Grant)指客户端以自己的名义,而不是以用户的名义,向【服务提供商】请求授权。
适用于没有前端的命令行应用,即在命令行下请求令牌。一般用来提供给我们完全信任的服务器端服务。

特点:
一般性流程描述:
(1)客户端向授权服务器进行身份认证,并要求一个访问令牌
(2)授权服务器确认无误后,向客户端提供访问令牌

案例:
1)A 应用在命令行向 B 发出请求

 https://oauth.b.com/token?grant_type=client_credentials&client_id=CLIENT_ID&client_secret=CLIENT_SECRET

2)B 网站验证通过以后,直接返回令牌

2.2 令牌的使用

A 网站拿到令牌以后,就可以向 B 网站的 API 请求数据了。这个时候,每次发送的请求,都会带上令牌,具体做法是,在头信息header中,加上一个Authorization字段,然后把令牌值设置在该字段中

2.3 令牌更新

令牌到期之后,正常来说是需要有自动刷新的逻辑的,不然每次到期让用户重新申请授权,未免显得太麻烦,而且也没必要。于是OAuth 2.0 允许用户自动更新令牌。
具体方法是,B 网站颁发令牌的时候,一次性颁发两个令牌,一个用于获取数据,另一个用于获取新的令牌(refresh token 字段)。令牌到期前,用户使用 refresh token 发一个请求,去更新令牌。

 https://b.com/oauth/token?grant_type=refresh_token&    # grant_type参数为refresh_token表示要求更新令牌client_id=CLIENT_ID&client_secret=CLIENT_SECRET&refresh_token=REFRESH_TOKEN    # 用于更新令牌的令牌

三、Spring Security OAuth2快速开始

首先,为了帮助更好的理解当前正在做什么,先再次声明以下内容:

  • Spring Security是Spring提供的一个身份验证和访问控制框架,是一个具体的项目
  • OAuth是一个标准协议,是一种约定,不特指某一个具体的项目
  • Spring Security OAuth2是实现了OAuth2标准协议的Spring Security框架,是一个具体的项目

我在前面大概介绍过SpringSecurity了,这边就不再赘述了。但首先我们得再次声明一点:SpringSecurity主要实现了【认证】和【访问控制】。
正常在我们的企业应用中,需要结合SpringSecurity与OAuth2,这样才算是得到一套完整的解决方案。我们可以通过Spring Security + OAuth2构建一个授权服务器来验证用户身份以提供access_token,并使用这个access_token来从资源服务器请求数据。

3.1 授权服务器的几个节点

在这里插入图片描述
上面是一个典型授权服务器的节点图。它们分别有如下作用:

  • Authorize Endpoint :授权端点,进行授权
  • Token Endpoint :令牌端点,经过授权拿到对应的Token
  • Introspection Endpoint :校验端点,校验Token的合法性
  • Revocation Endpoint :撤销端点,撤销授权

3.2 整体架构(授权码模式)

在这里插入图片描述
上图的流程:

  1. 用户访问资源,此时没有Token。Oauth2RestTemplate会报错,这个报错信息会被Oauth2ClientContextFilter捕获并重定向到授权服务器
  2. 授权服务器通过Authorization Endpoint进行授权,并通过AuthorizationServerTokenServices生成【授权码】并返回给客户端
  3. 客户端拿到授权码去授权服务器通过Token Endpoint调用AuthorizationServerTokenServices生成Token并返回给客户端
  4. 客户端拿到Token去资源服务器访问资源,一般会通过Oauth2AuthenticationManager调用ResourceServerTokenServices进行校验。校验通过可以获取资源

3.3 代码整合(授权码模式)

1)引入依赖

    <!-- 接入spring security--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!-- https://mvnrepository.com/artifact/org.springframework.security.oauth/spring-security-oauth2 --><dependency><groupId>org.springframework.security.oauth</groupId><artifactId>spring-security-oauth2</artifactId><version>2.5.2.RELEASE</version></dependency>

2)配置 spring security

    @Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin().permitAll().and().authorizeRequests().antMatchers("/oauth/**").permitAll().anyRequest().authenticated().and().logout().permitAll().and().csrf().disable();}
}

3)配置授权服务器

public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate PasswordEncoder passwordEncoder;@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.inMemory()// 配置client_id.withClient("client")// 配置client-secret.secret(passwordEncoder.encode("123456"))// 配置访问token的有效期.accessTokenValiditySeconds(3600)// 配置刷新token的有效期.refreshTokenValiditySeconds(864000)// 配置redirect_uri,用于授权成功后跳转.redirectUris("http://www.baidu.com")// 配置申请的权限范围.scopes("all")// 配置grant_type,表示授权类型.authorizedGrantTypes("authorization_code");}
}

4)配置资源服务器

@Configuration
@EnableResourceServer
public class ResourceServiceConfig extends ResourceServerConfigurerAdapter {@Overridepublic void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().authenticated().and().requestMatchers().antMatchers("/user/**");}
}

5)测试,获取授权码
输入:http://localhost:8081/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://www.baidu.com&scope=all
接着会进入这个页面:
在这里插入图片描述
登录之后选择Approve
在这里插入图片描述
接着就会在地址栏显示如下:
在这里插入图片描述
上面的code即为我们的【授权码】

6)根据授权码获取令牌
下面需要使用Apifox来构建一个post请求,去授权服务器中获取令牌了
在这里插入图片描述
在这里插入图片描述
7)请求令牌,得到如下数据:
在这里插入图片描述

3.4 更新令牌

使用oauth2时,如果令牌失效了,可以使用刷新令牌通过refresh_token的授权模式再次获取access_token。只需修改认证服务器的配置,添加refresh_token的授权模式即可。
1)修改授权服务器AuthorizationServerConfig配置,增加refresh_token配置

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate PasswordEncoder passwordEncoder;@Autowiredprivate ShenUserService userService;@Autowiredprivate AuthenticationManager authenticationManagerBean;@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints.authenticationManager(authenticationManagerBean) //使用密码模式需要配置// .tokenStore(tokenStore)  //指定token存储到redis.reuseRefreshTokens(false)  //refresh_token是否重复使用.userDetailsService(userService) //刷新令牌授权包含对用户信息的检查.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST); //支持GET,POST请求}@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.inMemory()// 配置client_id.withClient("client")// 配置client-secret.secret(passwordEncoder.encode("123456"))// 配置访问token的有效期.accessTokenValiditySeconds(3600)// 配置刷新token的有效期.refreshTokenValiditySeconds(864000)// 配置redirect_uri,用于授权成功后跳转.redirectUris("http://www.baidu.com")// 配置申请的权限范围.scopes("all")// 配置grant_type,表示授权类型.authorizedGrantTypes("authorization_code", "implicit", "refresh_token");}
}

测试获取token,得到如下结果:
在这里插入图片描述
相比之前的,多了一个refresh_token

3.5 基于redis存储Token

我们生成的token可以存储在很多地方,比较常用的一种方式是存储到redis中。只需要3步即可

1)引入依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId>
</dependency>

2)修改application.yml

spring:redis:host: 127.0.0.1database: 0

3)编写redis配置类,声明TokenStore Bean

@Configuration
public class RedisConfig {@Autowiredprivate RedisConnectionFactory redisConnectionFactory;@Beanpublic TokenStore tokenStore(){return new RedisTokenStore(redisConnectionFactory);}
}

4)在授权服务器AuthorizationServerConfig配置中指定令牌的存储策略为Redis

   @Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints.authenticationManager(authenticationManagerBean) //使用密码模式需要配置.tokenStore(tokenStore)  //指定token存储到redis.reuseRefreshTokens(false)  //refresh_token是否重复使用.userDetailsService(userService) //刷新令牌授权包含对用户信息的检查.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST); //支持GET,POST请求}

四、Spring Security Oauth2整合JWT

我在前一篇笔记里面已经简单介绍过JWT了,感兴趣的朋友可以看看我上一篇文章,链接在最前面的【阅读导航】中有介绍。下面开始介绍整合步骤

4.1 整合JWT

1)引入依赖

<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-jwt</artifactId><version>1.0.9.RELEASE</version>
</dependency>

2)添加配置文件JwtTokenStoreConfig.java

@Configuration
public class JwtTokenStoreConfig {@Beanpublic TokenStore jwtTokenStore(){return new JwtTokenStore(jwtAccessTokenConverter());}@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter(){JwtAccessTokenConverter accessTokenConverter = newJwtAccessTokenConverter();// 配置JWT使用的秘钥accessTokenConverter.setSigningKey("123456");return accessTokenConverter;}
}

3)在授权服务器AuthorizationServerConfig配置中指定令牌的存储策略为JWT

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate PasswordEncoder passwordEncoder;@Autowiredprivate ShenUserService userService;@Autowiredprivate AuthenticationManager authenticationManagerBean;@Autowiredprivate TokenStore tokenStore;@Autowiredprivate JwtAccessTokenConverter jwtAccessTokenConverter;@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints.authenticationManager(authenticationManagerBean) // 使用密码模式需要配置.tokenStore(tokenStore)  // 指定token存储到redis.accessTokenConverter(jwtAccessTokenConverter).reuseRefreshTokens(false)  // refresh_token是否重复使用.userDetailsService(userService) // 刷新令牌授权包含对用户信息的检查.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST); // 支持GET,POST请求}@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.inMemory()// 配置client_id.withClient("client")// 配置client-secret.secret(passwordEncoder.encode("123456"))// 配置访问token的有效期.accessTokenValiditySeconds(3600)// 配置刷新token的有效期.refreshTokenValiditySeconds(864000)// 配置redirect_uri,用于授权成功后跳转.redirectUris("http://www.baidu.com")// 配置申请的权限范围.scopes("all")// 配置grant_type,表示授权类型.authorizedGrantTypes("authorization_code", "implicit", "refresh_token");}
}

4)接着还是按照上面的测试步骤走一遍,得到以下结果,令牌变成了JWT格式
在这里插入图片描述

4.2 扩展JWT中的存储内容

有时候我们需要扩展JWT中存储的内容,这里我们在JWT中扩展一个 key为enhance,value为enhance info 的数据。
继承TokenEnhancer实现一个JWT内容增强器

1)新增JwtTokenEnhancer ,增强内容:("enhance", "enhance info")

@Component
public class JwtTokenEnhancer implements TokenEnhancer {@Overridepublic OAuth2AccessToken enhance(OAuth2AccessToken accessToken,OAuth2Authentication authentication) {Map<String, Object> info = new HashMap<>();info.put("enhance", "enhance info");((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);return accessToken;}
}

2)在授权服务器AuthorizationServerConfig配置中配置JWT的内容增强器

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate PasswordEncoder passwordEncoder;@Autowiredprivate ShenUserService userService;@Autowiredprivate AuthenticationManager authenticationManagerBean;@Autowiredprivate TokenStore tokenStore;@Autowiredprivate JwtAccessTokenConverter jwtAccessTokenConverter;@Autowiredprivate JwtTokenEnhancer jwtTokenEnhancer;@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {//配置JWT的内容增强器TokenEnhancerChain enhancerChain = new TokenEnhancerChain();List<TokenEnhancer> delegates = new ArrayList<>();delegates.add(jwtTokenEnhancer);delegates.add(jwtAccessTokenConverter);enhancerChain.setTokenEnhancers(delegates);endpoints.authenticationManager(authenticationManagerBean) // 使用密码模式需要配置.tokenStore(tokenStore)  // 指定token存储到redis.accessTokenConverter(jwtAccessTokenConverter).tokenEnhancer(enhancerChain) //配置tokenEnhancer.reuseRefreshTokens(false)  // refresh_token是否重复使用.userDetailsService(userService) // 刷新令牌授权包含对用户信息的检查.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST); // 支持GET,POST请求}@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.inMemory()// 配置client_id.withClient("client")// 配置client-secret.secret(passwordEncoder.encode("123456"))// 配置访问token的有效期.accessTokenValiditySeconds(3600)// 配置刷新token的有效期.refreshTokenValiditySeconds(864000)// 配置redirect_uri,用于授权成功后跳转.redirectUris("http://www.baidu.com")// 配置申请的权限范围.scopes("all")// 配置grant_type,表示授权类型.authorizedGrantTypes("authorization_code", "implicit", "refresh_token");}
}

3)测试,获取令牌
4)对获取到的令牌,解密之后得到

{"user_name": "Nico","scope": ["all"],"exp": 1704786179,"authorities": ["admin"],"jti": "2DyqZildiHtLr7tojfDOwGqjnLE","client_id": "client","enhance": "enhance info"
}

可以看见,在Payload中得到了被加强的内容

4.3 解析JWT

1)添加依赖

<!--JWT依赖-->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>

2)修改UserController类,使用jjwt工具类来解析Authorization头中存储的JWT内容

@GetMapping("/getCurrentUser")public Object getCurrentUser(Authentication authentication,HttpServletRequest request) {String header = request.getHeader("Authorization");String token = null;if(header!=null){token = header.substring(header.indexOf("bearer") + 7);}else {token = request.getParameter("access_token");}return Jwts.parser().setSigningKey("123456".getBytes(StandardCharsets.UTF_8)).parseClaimsJws(token).getBody();}

3)测试,不过这一次我们需要将得到的令牌放在header的Authorization中了,因为我们的逻辑是这么写的
接着访问上面的getCurrentUser接口,即会得到如下结果:
在这里插入图片描述

学习总结

  1. 稍微学习了OAuth2的一些内容,只不过还是有一些疑惑的点

感谢

  1. 感谢站内大佬【作者:歪桃】的文章《Spring Security oauth2(一)快速入门,搭建授权服务器》
  2. 感谢大佬【作者:阮一峰】的文章《理解OAuth 2.0》

相关文章:

【分布式微服务专题】SpringSecurity OAuth2快速入门

目录 前言阅读对象阅读导航前置知识笔记正文一、OAuth2 介绍1.1 使用场景*1.2 基本概念&#xff08;角色&#xff09;1.3 优缺点 二、OAuth2的设计思路2.1 客户端授权模式2.1.0 基本参数说明2.1.1 授权码模式2.1.2 简化&#xff08;隐式&#xff09;模式2.1.3 密码模式2.1.4 客…...

Spring Boot实现国际化

src\main\resources\i18n\messages_zh_CN.properties message.hello你好&#xff0c;世界&#xff01; message.welcome欢迎&#xff01; src/main/resources/i18n/messages_en_US.properties message.helloHello World! message.welcomeWelcome! 默认语言 src\main\resources\…...

面试题之ElasticSearch

面试题之ElasticSearch 1.es的基础知识2. es的集群、节点、分片、副本分片的定义&#xff1f;3. es为什么快&#xff1f;4. 倒排索引的原理是什么&#xff1f;5. es的segment是什么&#xff1f;6. es的分段存储和分段索引的概念及区别&#xff1f;7. 索引相关的问题&#xff1f…...

第10章 通信业务

文章目录 10.1.1 通信行业1、通信行业的界定2、通信行业的特点 10.1.2 通信企业10.1.3 通信终端1、通信终端的分类2、终端发展趋势 10.2.1 通信业务的定义及分类10.2.2 基础电信业务1、第一类基础电信业务A11 固定通信业务A12 蜂窝移动通信业务A13 第一类卫星通信业务A14 第一类…...

c++ 指针的安全问题

指针是一个强大的工具&#xff0c;但它们可能导致多种安全问题。接下来我们一起研究一下会出现的安全问题。欢迎大家补充说明&#xff01;&#xff01;&#xff01; 悬挂指针&#xff08;也称为悬空指针或迷途指针&#xff09; 是指向一块已经释放或无效内存的指针。悬挂指针…...

高质量训练数据助力大语言模型摆脱数据困境 | 景联文科技

目前&#xff0c;大语言模型的发展已经取得了显著的成果&#xff0c;如OpenAI的GPT系列模型、谷歌的BERT模型、百度的文心一言模型等。这些模型在文本生成、问答系统、对话生成、情感分析、摘要生成等方面都表现出了强大的能力&#xff0c;为自然语言处理领域带来了新的突破。 …...

elasticsearch查询

&#xff08;1&#xff09;简单查询 curl -XGET http://127.0.0.1:9201/_search curl -XGET http://127.0.0.1:9201/test231208/_search curl -XGET http://127.0.0.1:9201/test231208/_doc/_search curl -XGET http://127.0.0.1:9201/test231208/_doc/id &#xff08;2&…...

Vue + JS + tauri 开发一个简单的PC端桌面应用程序

Vue JS tauri 开发一个简单的PC端桌面应用程序 文章目录 Vue JS tauri 开发一个简单的PC端桌面应用程序1. 环境准备1.1 安装 Microsoft Visual Studio C 生成工具[^2]1.2 安装 Rust[^3] 2. 使用 vite 打包工具创建一个 vue 应用2.1 使用Vite创建前端Vue项目2.2 更改Vite打包…...

7.5 MySQL对数据的增改删操作(❤❤❤)

7.5 MySQL对数据的基本操作 1. 提要2. 数据添加2.1 insert语法2.2 insert 子查询2.3 ignore关键字 3. 数据修改3.1 update语句3.2 update表连接 4. 数据删除4.1 delete语句4.2 delete表连接4.3 快速删除数据表全部数据 1. 提要 2. 数据添加 2.1 insert语法 2.2 insert 子查询 …...

kibana查看和展示es数据

本文来说下使用kibana查看和展示es数据 文章目录 数据准备查询所有文档示例kibana查看和展示es数据 数据准备 可以使用es的命令或者java程序来往&#xff0c;es进行新增数据 查询所有文档示例 在 apifox 中&#xff0c;向 ES 服务器发 GET请求 &#xff1a;http://localhost:92…...

若依修改侧边栏

引用&#xff1a;https://blog.csdn.net/Sabrina_cc/article/details/125871591 子菜单选中后&#xff0c;文字和背景改变&#xff1a; .el-submenu__title i{color: #e8e8e8 !important;} #app .sidebar-container .theme-dark .nest-menu .el-submenu .is-active > .el-su…...

Linux篇之Centos中将系统时间设置为本地时间

要在 CentOS 上将系统时间设置为本地时间&#xff0c;可以按照以下步骤进行操作&#xff1a; 1.首先&#xff0c;你需要确定你想要设置的本地时间。例如&#xff0c;如果你想要将系统时间设置为当前时间&#xff08;假设是北京时间&#xff09;&#xff0c;则可以使用以下命令获…...

翼龙-2H无人机

一、概述 翼龙-2&#xff0c;是成都飞机工业集团研制的无人驾驶飞行器&#xff0c;是空中侦察、精确打击和应急通讯的平台。成都飞机工业集团于2015年9月的北京国际航空航天展览会上介绍了翼龙-2的概念。在2016年珠海航展期间&#xff0c;翼龙-2的原型机首次向公众展示。 因为…...

解析Transformer模型

原文地址&#xff1a;https://zhanghan.xyz/posts/17281/ 进入Transformer RNN很难处理冗长的文本序列&#xff0c;且很容易受到所谓梯度消失/爆炸的问题。RNN是按顺序处理单词的&#xff0c;所以很难并行化。 用一句话总结Transformer&#xff1a;当一个扩展性极佳的模型和一…...

【深度学习】RTX2060 2080如何安装CUDA,如何使用onnx runtime

文章目录 如何在Python环境下配置RTX 2060与CUDA 101. 安装最新的NVIDIA显卡驱动2. 使用conda安装CUDA Toolkit3. 验证onnxruntime与CUDA版本4. 验证ONNX需求版本5. 安装ONNX与onnxruntime6. 编写ONNX推理代码 如何在Python环境下配置RTX 2060与CUDA 10 RTX 2060虽然是一款较早…...

力扣刷MySQL-第二弹(详细解析)

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;力扣刷题讲解-MySQL &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出…...

LiveGBS流媒体平台GB/T28181功能-基础配置接入控制白名单黑名单配置控制设备安全接入设备单独配置接入密码

LiveGBS基础配置接入控制白名单黑名单配置控制设备安全接入设备单独配置接入密码 1、白名单配置应用场景2、接入控制2.1、白名单2.2、黑名单 3、搭建GB28181视频直播平台 1、白名单配置应用场景 LiveGBS国标流媒体服务&#xff0c;支持白名单配置。 可在设备注册前&#xff0…...

企业网站建站源码系统:Thinkphp5内核企业网站建站模板源码 带完整的安装代码包以及搭建教程

随着互联网的快速发展&#xff0c;企业对于网站的需求日益增强。为了满足这一市场需求&#xff0c;小编给大家分享一款基于Thinkphp5内核的企业网站建站源码系统。该系统旨在为企业提供一套功能强大、易于使用的网站建设解决方案&#xff0c;帮助企业快速搭建自己的官方网站&am…...

SC20-EVB ubuntu14.04 Andriod 5.1 SDK编译下载

1.ubuntu14.04安装环境配置 vi /etc/profile to add export JAVA_HOME/usr/lib/jvm/java-7-openjdk-amd64 export JRE_HOME J A V A H O M E / j r e e x p o r t C L A S S P A T H . : {JAVA_HOME}/jre export CLASSPATH.: JAVAH​OME/jreexportCLASSPATH.:{JAVA_HOME}/lib…...

OpenCV——图像按位运算

目录 一、算法概述1、逻辑运算2、函数解析3、用途 二、代码实现三、结果展示 OpenCV——图像按位运算由CSDN点云侠原创&#xff0c;爬虫自重。如果你不是在点云侠的博客中看到该文章&#xff0c;那么此处便是不要脸的爬虫。 一、算法概述 1、逻辑运算 OpenCV4 针对两个图像之…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析

今天聊的内容&#xff0c;我认为是AI开发里面非常重要的内容。它在AI开发里无处不在&#xff0c;当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗"&#xff0c;或者让翻译模型 "将这段合同翻译成商务日语" 时&#xff0c;输入的这句话就是 Prompt。…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

Python 实现 Web 静态服务器(HTTP 协议)

目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1&#xff09;下载安装包2&#xff09;配置环境变量3&#xff09;安装镜像4&#xff09;node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1&#xff09;使用 http-server2&#xff09;详解 …...

Python训练营-Day26-函数专题1:函数定义与参数

题目1&#xff1a;计算圆的面积 任务&#xff1a; 编写一个名为 calculate_circle_area 的函数&#xff0c;该函数接收圆的半径 radius 作为参数&#xff0c;并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求&#xff1a;函数接收一个位置参数 radi…...

人工智能 - 在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型

在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型。这些平台各有侧重&#xff0c;适用场景差异显著。下面我将从核心功能定位、典型应用场景、真实体验痛点、选型决策关键点进行拆解&#xff0c;并提供具体场景下的推荐方案。 一、核心功能定位速览 平台核心定位技术栈亮…...

向量几何的二元性:叉乘模长与内积投影的深层联系

在数学与物理的空间世界中&#xff0c;向量运算构成了理解几何结构的基石。叉乘&#xff08;外积&#xff09;与点积&#xff08;内积&#xff09;作为向量代数的两大支柱&#xff0c;表面上呈现出截然不同的几何意义与代数形式&#xff0c;却在深层次上揭示了向量间相互作用的…...