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

Java重要面试名词整理(二十一):SpringSecurity

文章目录

  • SpringSecurity篇
    • 概念
      • 用户授权(访问控制)
      • JWT
      • JWT续期问题
      • Spring Authorization Server 是什么
      • OAuth2.0协议介绍
        • 角色
        • OAuth 2.0的运行流程
    • 授权模式详解
        • 客户端模式
        • 密码模式
        • 授权码模式
        • 简化模式
        • token刷新模式
    • OAuth 2.1 协议介绍
      • 授权码模式+PKCE扩展
      • 设备授权码模式
      • 拓展授权模式
    • OpenID Connect 1.0协议
    • Spring Authorization Server 实战
      • 认证(授权)服务器搭建
        • 授权码模式测试
    • SSO
    • OAuth2.1详细变化
      • 推荐使用 Authorization Code + PKCE
      • 隐式授权( Implicit Grant)已弃用
      • 密码授权 (Resource Owner Password Credentials Grant)已弃用
      • 使用 access_token 时, 不应该通过 URL 传递 token
      • 刷新令牌 (Refresh Token) 应该是一次性的
      • 回调地址(Redirect URI)应该精确匹配

SpringSecurity篇

概念

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。Spring Security 主要实现了Authentication(认证,解决who are you? ) 和 AccessControl(访问控制,也就是what are you allowed to do?,也称为Authorization)。SpringSecurity在架构上将认证与授权分离,并提供了扩展点。

认证 :用户认证就是判断一个用户的身份是否合法的过程,用户去访问系统资源时系统要求验证用户的身份信息,身份合法方可继续访问,不合法则拒绝访问。常见的用户身份认证方式有:用户名密码登录,二维码登录,手机短信登录,指纹认证等方式。

授权: 授权是用户认证通过根据用户的权限来控制用户访问资源的过程,拥有资源的访问权限则正常访问,没有权限则拒绝访问。

在 Java 生态中,目前有 Spring Security 和 Apache Shiro 两个安全框架,可以完成认证和授权的功能。

  • Spring Security

  • Apache Shiro:一个功能强大且易于使用的Java安全框架,提供了认证,授权,加密,和会话管理。

用户授权(访问控制)

授权的方式包括 web授权和方法授权,web授权是通过url拦截进行授权,方法授权是通过方法拦截进行授权。

web授权: 基于url的访问控制

Spring Security可以通过 http.authorizeRequests() 对web请求进行授权保护 ,Spring Security使用标准Filter建立了对web请求的拦截,最终实现对资源的授权访问。配置顺序会影响之后授权的效果,越是具体的应该放在前面,越是笼统的应该放到后面。

JWT

什么是JWT

JSON Web Token(JWT)是一个开放的行业标准(RFC 7519),它定义了一种简介的、自包含的协议格式,用于在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任。JWT可以使用HMAC算法或使用RSA的公钥/私钥对来签名,防止被篡改。 官网: https://jwt.io/ 标准: https://tools.ietf.org/html/rfc7519

使用 JWT 主要用来做下面两点:

  • 认证(Authorization):这是使用 JWT 最常见的一种情况,一旦用户登录,后面每个请求都会包含 JWT,从而允许用户访问该令牌所允许的路由、服务和资源。单点登录是当今广泛使用 JWT 的一项功能,因为它的开销很小。
  • 信息交换(Information Exchange):JWT 是能够安全传输信息的一种方式。通过使用公钥/私钥对 JWT 进行签名认证。此外,由于签名是使用 head 和 payload 计算的,因此你还可以验证内容是否遭到篡改。

JWT组成

一个JWT实际上就是一个字符串,它由三部分组成,头部(header)、载荷(payload)与签名(signature)。

头部(header)

头部用于描述关于该JWT的最基本的信息:类型(即JWT)以及签名所用的算法(如HMACSHA256或RSA)等

载荷(payload)

第二部分是载荷,就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分:

  • 标准中注册的声明(建议但不强制使用)

iss: jwt签发者

sub: jwt所面向的用户

aud: 接收jwt的一方

exp: jwt的过期时间,这个过期时间必须要大于签发时间

nbf: 定义在什么时间之前,该jwt都是不可用的.

iat: jwt的签发时间

jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

  • 公共的声明 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
  • 私有的声明 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。

签名(signature)

jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

  • header (base64后的)
  • payload (base64后的)
  • secret(盐,一定要保密)

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分

JWT续期问题

JWT(JSON Web Token)通常是在用户登录后签发的,用于验证用户身份和授权。JWT 的有效期限(或称“过期时间”)通常是一段时间(例如1小时),过期后用户需要重新登录以获取新的JWT。然而,在某些情况下,用户可能会在JWT到期之前使用应用程序,这可能会导致应用程序不可用或需要用户重新登录。为了避免这种情况,通常有两种解决方案来处理JWT续期问题:

刷新令牌(Refresh Token)

刷新令牌是一种机制,它允许应用程序获取一个新的JWT,而无需用户进行身份验证。当JWT过期时,应用程序使用刷新令牌向身份验证服务器请求一个新的JWT,而无需提示用户输入其凭据。这样,用户可以继续使用应用程序,而不必重新登录。

自动延长JWT有效期

在某些情况下,JWT可以自动延长其有效期。例如,当用户在JWT过期前继续使用应用程序时,应用重新设置token过期时间。

Spring Authorization Server 是什么

Spring Authorization Server 是一个框架,它提供了 OAuth 2.1 和 OpenID Connect 1.0 规范以及其他相关规范的实现。它建立在 Spring Security 之上,为构建 OpenID Connect 1.0 身份提供者和 OAuth2 授权服务器产品提供了一个安全、轻量级和可定制的基础。说白了,Spring Authorization Server 就是一个认证(授权)服务器。

Springboot2.x Oauth2实现:

  • Spring Security Oauth2 支持搭建授权服务器和资源服务器

Springboot3.x Oauth2实现:

  • Spring Security6 自身提供资源和客户端类库支持
  • Spring Authorization Server支持授权服务器搭建

OAuth2.0协议介绍

OAuth 2.0 (Open Authorization)是一种开放标准的授权协议,允许用户授权第三方应用访问其在某个服务提供者上的受保护资源,而无需将其实际的凭证(如用户名和密码)分享给第三方应用。这种方式可以增加安全性,同时允许用户更好地控制其数据的访问权限。

OAuth2.0协议:https://datatracker.ietf.org/doc/html/rfc6749

角色
  • 客户端(client):使用授权服务器作为认证渠道的平台,一般指的是第三方应用。例如,微信提供 OAuth 2.0 认证平台,我们的 APP 支持微信登录,那么我们的 APP 对于微信服务来说就是客户端。又例如,我们是政府某一平台的服务,我们平台维护的数据代表着足够高的权威,那么其他政府部门或合作方,需要从我们的平台中查询数据,或者利用我们平台的认证进行登录,那么其他部门或合作方的应用就是客户端。
  • 资源服务器(resource server):简单的说,就是提供接口给客户端访问的服务器,访问资源服务器上受保护的接口,则需要带上令牌(token)。例如分布式微服务中的用户服务、订单服务等部署的服务器都属于资源服务器。
  • 资源所有者(resource owner):拥有该资源的主体对象,一般指用户。客户端向资源服务器请求获取用户数据时,资源所有者参与确认授权或拒绝操作。
  • 授权服务器(authorization server):对客户端和用户进行身份认证、授权的服务器,认证授权成功,则颁发令牌(token)。
OAuth 2.0的运行流程

(A)用户打开客户端以后,客户端要求用户给予授权。

(B)用户同意给予客户端授权。

(C)客户端使用上一步获得的授权,向授权服务器申请令牌。

(D)授权服务器对客户端进行认证以后,确认无误,同意发放令牌。

(E)客户端使用令牌,向资源服务器申请获取资源。

(F)资源服务器确认令牌无误,同意向客户端开放资源。

令牌(token)与密码(password)的作用是一样的,都可以进入系统,但是有三点差异。

(1)令牌是短期的,到期会自动失效,用户自己无法修改。密码一般长期有效,用户不修改,就不会发生变化。

(2)令牌可以被数据所有者撤销,会立即失效。密码一般不允许被他人撤销。

(3)令牌有权限范围(scope)。对于网络服务来说,只读令牌就比读写令牌更安全。密码一般是完整权限。

上面这些设计,保证了令牌既可以让第三方应用获得权限,同时又随时可控,不会危及系统安全。这就是 OAuth 2.0 的优点。

授权模式详解

授权码模式:应用最广泛,适合web应用/app/前端

资源所有者密码模式:官方应用

客户端凭证模式:适合无用户参与的应用

刷新令牌模式:适合令牌访问过期后刷新令牌

客户端模式

客户端模式(Client Credentials Grant)指客户端以自己的名义,而不是以用户的名义,向"服务提供商"进行授权。客户端模式是安全级别最低而且要求授权服务器对客户高度信任的模式,因为客户端向授权服务器请求认证授权的过程中,至始至终都没有用户的参与,未经过用户允许,客户端凭提供自己在授权服务器注册的信息即可在授权服务器完成认证授权,而客户端获得认证授权以后,则拥有从资源服务器操作用户数据的权限,这种模式一般应用于公司内部系统或者有着高度保密责任的合作伙伴之间的对接。

密码模式

如果你高度信任某个应用,RFC 6749 也允许用户把用户名和密码,直接告诉该应用。该应用就使用你的密码,申请令牌,这种方式称为"密码模式"(password)。

密码模式是一种安全级别较低而且要求资源拥有者(用户)完全信任客户端的模式,该模式可以理解为在客户端模式的基础上增加了对用户的账号、密码在认证服务器进行校验的操作,是客户端代理用户的操作。在 OAuth 2.1 中,密码模式已经被废除,在第三方平台上,使用密码模式,对于用户来说是一种非常不安全的行为,假设某平台客户端支持 QQ 登录,用户使用自己 QQ 的账号、密码在该平台上输入进行登录,则该平台将拥有用户 QQ 的账号、密码,对于用户来说,将自己 QQ 的账号、密码提供给第三方平台,这种行为是非常不安全的。

密码模式一般适合应用在自己公司内部使用的系统和自己公司的 app 产品

授权码模式

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

授权码模式是 OAuth 2.0 协议中安全级别最高的一种认证模式,他与密码模式一样,都需要使用到用户的账号信息在认证平台的登录操作,但有所不同的是,密码模式是要求用户直接将自己在认证平台的账号、密码提供给第三方应用(客户端),由第三方平台进行代理用户在认证平台的登录操作;而授权码模式则是用户在认证平台提供的界面进行登录,然后通过用户确认授权后才将一次性授权码提供给第三方应用,第三方应用拿到一次性授权码以后才去认证平台获取 token。

适用场景:目前市面上主流的第三方验证都是采用这种模式

简化模式

简化模式(也叫隐式模式)是相对于授权码模式而言的,对授权码模式的交互做了一下简化,省去了客户端使用授权码去认证服务器换取令牌(access_token)的操作,即用户在代理页面选择授权范围提交授权确认后,认证服务器通过客户端注册的回调地址直接就给客户端返回令牌(access_token)了。

这种方式把令牌直接传给前端,是很不安全的。因此,只能用于一些安全要求不高的场景,并且令牌的有效期必须非常短,通常就是会话期间(session)有效,浏览器关掉,令牌就失效了。

token刷新模式

令牌的有效期到了,如果让用户重新走一遍上面的流程,再申请一个新的令牌,很可能体验不好,而且也没有必要。OAuth 2.0 允许用户自动更新令牌。token刷新模式是对 access_token 过期的一种补办操作,这种补办操作,减少了用户重新操作登录的流程。OAuth 2.0 在给客户端颁发 access_token 的时候,同时也给客户端发放了 refresh_token,而 refresh_token 的有效期要远大于 access_token 的有效期。当客户端带着已过期的 access_token 去访问资源服务器中受保护的资源时,将会访问失败,此时就需要客户端使用 refresh_token 去获取新的 access_token。客户端获取到新的 access_token 后,就可以带上他去访问资源服务器中受保护的资源了。

OAuth 2.1 协议介绍

OAuth 2.1去掉了OAuth2.0中的密码模式、简化模式,增加了设备授权码模式,同时也对授权码模式增加了PKCE扩展。

OAuth2.1协议:https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07

授权码模式+PKCE扩展

授权码模式交互过程,见之前 OAuth2.0 授权码模式的讲解。这里要说的是授权码模式如何拓展 PKCE( Proof Key for Code Exchange 代码交换验证密钥)。

在授权码模式的交互工程中,有一个环节比较薄弱,这个环节就是用户在代理页面确认授权的时候,容易受到恶意程序的攻击,从而导致授权码被恶意程序窃取,进而通过授权码窃取令牌,当然这个前提也需要恶意程序已经植入到你的PC或手机当中

A. 客户端通过“/oauth2/authorize”地址向认证服务器发起获取授权码请求的时候增加两个参数,即 code_challenge 和 code_challenge_method,其中,code_challenge_method 是加密方法(例如:S256 或 plain),code_challenge 是使用 code_challenge_method 加密方法加密后的值。

B. 认证服务器给客户端返回授权码,同时记录下 code_challenge、code_challenge_method 的值。

C. 客户端使用 code 向认证服务器获取 Access Token 的时候,带上 code_verifier 参数,其值为步骤A加密前的初始值。

D. 认证服务器收到步骤 C 的请求时,将 code_verifier 的值使用 code_challenge_method 的方法进行加密,然后将加密后的值与步骤A中的 code_challenge 进行比较,看看是否一致。

上面交互过程中,恶意程序如果在B处截获授权码后,使用授权码向认证服务器换取 Access Token,但由于恶意程序没有 code_verifier 的值,因此在认证服务器无法校验通过,从而获取 Access Token 失败。

对于如何创建 code_challenge 的值,官网给出了下面两种对应的方法。

  • plain code_challenge = code_verifier 0.3.0被废弃了
  • S256 code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

设备授权码模式

设备授权码模式,是一种为解决不便在当前设备上进行文本输入而提供的一种认证授权模式,例如:智能电视、媒体控制台、数字相框、打印机等。大家也可以脑补一下一些扫码登录的情形。

拓展授权模式

OAuth2.1 也提供拓展授权模式的操作实现。虽然 OAuth2.1 移除了密码模式(password),但是通过拓展授权模式可以实现密码模式。在实际应用中,客户端、授权服务器、资源服务器往往都是同一家公司的产品,那么这个时候,使用账号、密码进行登录的情形也比较常见,此时就需要通过拓展授权模式来实现账号、密码登录了。

拓展授权模式官网文档: https://docs.spring.io/spring-authorization-server/docs/current/reference/html/guides/how-to-ext-grant-type.html

OpenID Connect 1.0协议

OpenID Connect 1.0:https://openid.net/specs/openid-connect-core-1_0.html

OpenID Connect 1.0 是 OAuth 2.0 协议之上的一个简单的身份层。其实就是客户端向认证服务器请求认证授权的时候,多返回一个 id_token,该 id_token 是一串使用 jwt 加密过的字符串

Spring Security OAuth2 Client组件介绍:

  • ClientRegistration:注册的客户端
  • ClientRegistrationRepository:ClientRegistration的存储仓库
  • OAuth2AuthorizedClient: 已授权过的客户端
  • OAuth2AuthorizedClientRepository :已授权过的客户端存储库持久化
  • OAuth2AuthorizationRequestRedirectFilter:该过滤器处理 /oauth2/authorization 路径,转发给 认证中心 对应的路径 /oauth2/authorize
  • OAuth2AuthorizationCodeGrantFilter:负责处理 认证中心 的授权码回调请求,如地址重定向
  • OAuth2LoginAuthenticationFilter:处理第三方认证的回调(该回调有授权码),拿着授权码到第三方认证服务器获取access_token和refresh_token

Spring Authorization Server 实战

版本要求:

  • Spring Authorization Server 版本:1.1.2
  • JDK 版本:17
  • Spring Boot 版本:3.1.4

认证(授权)服务器搭建

Spring Authorization Server重要组件:

  • SecurityFilterChain -> authorizationServerSecurityFilterChain: Spring Security的过滤器链,用于协议端点的。
  • SecurityFilterChain -> defaultSecurityFilterChain: Spring Security的过滤器链,用于Spring Security的身份认证
  • UserDetailsService :主要进行用户身份验证
  • RegisteredClientRepository:主要用于管理客户端
  • JWKSource:用于签名访问令牌
  • KeyPair: 启动时生成的带有密钥的KeyPair实例,用于创建上面的JWKSource
  • JwtDecoder:JwtDecoder的一个实例,用于解码已签名的访问令牌
  • AuthorizationServerSettings:用于配置Spring Authorization Server的AuthorizationServerSettings实例。

1)引入依赖

<!--Spring Authorization Server--> 
<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-oauth2-authorization-server</artifactId> 
</dependency> 

2) 配置授权服务器

直接从官网https://docs.spring.io/spring-authorization-server/docs/current/reference/html/getting-started.html将 SecurityConfig 拷贝放到config下

import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
 
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;
 
 
/**
 * https://docs.spring.io/spring-authorization-server/docs/current/reference/html/getting-started.html
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig {
 
   /**
    *  Spring Authorization Server 相关配置
    *  主要配置OAuth 2.1和OpenID Connect 1.0
    * @param http
    * @return
    * @throws Exception
    */
   @Bean 
   @Order(1)
   public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
         throws Exception {
      OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
      http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
         //开启OpenID Connect 1.0(其中oidc为OpenID Connect的缩写)
         .oidc(Customizer.withDefaults());  // Enable OpenID Connect 1.0
      http
         // Redirect to the login page when not authenticated from the
         // authorization endpoint
         //将需要认证的请求,重定向到login进行登录认证。
         .exceptionHandling((exceptions) -> exceptions
            .defaultAuthenticationEntryPointFor(
               new LoginUrlAuthenticationEntryPoint("/login"),
               new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
            )
         )
         // Accept access tokens for User Info and/or Client Registration
         // 使用jwt处理接收到的access token
         .oauth2ResourceServer((resourceServer) -> resourceServer
            .jwt(Customizer.withDefaults()));
 
      return http.build();
   }
 
   /**
    *  Spring Security 过滤链配置(此处是纯Spring Security相关配置)
    */
   @Bean 
   @Order(2)
   public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
         throws Exception {
      http
         .authorizeHttpRequests((authorize) -> authorize
            .anyRequest().authenticated()
         )
         // Form login handles the redirect to the login page from the
         // authorization server filter chain
         .formLogin(Customizer.withDefaults());
 
      return http.build();
   }
 
   /**
    * 设置用户信息,校验用户名、密码
    * @return
    */
   @Bean 
   public UserDetailsService userDetailsService() {
      UserDetails userDetails = User.withDefaultPasswordEncoder()
            .username("fox")
            .password("123456")
            .roles("USER")
            .build();
      //基于内存的用户数据校验
      return new InMemoryUserDetailsManager(userDetails);
   }
 
   /**
    * 注册客户端信息
    *
    * 查询认证服务器信息
    * http://127.0.0.1:9000/.well-known/openid-configuration
    *
    * 获取授权码
    * http://localhost:9000/oauth2/authorize?response_type=code&client_id=oidc-client&scope=profile&redirect_uri=http://www.baidu.com
    *
    */
   @Bean 
   public RegisteredClientRepository registeredClientRepository() {
      RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
            .clientId("oidc-client")
            //{noop}开头,表示“secret”以明文存储
            .clientSecret("{noop}secret")
            .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
            .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
            .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
            .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
            //.redirectUri("http://spring-oauth-client:9001/login/oauth2/code/messaging-client-oidc")
            //我们暂时还没有客户端服务,以免重定向跳转错误导致接收不到授权码
            .redirectUri("http://www.baidu.com")
            .postLogoutRedirectUri("http://127.0.0.1:8080/")
            .scope(OidcScopes.OPENID)
            .scope(OidcScopes.PROFILE)
            .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
            .build();
 
      //配置基于内存的客户端信息
      return new InMemoryRegisteredClientRepository(oidcClient);
   }
 
   /**
    * 配置 JWK,为JWT(id_token)提供加密密钥,用于加密/解密或签名/验签
    * JWK详细见:https://datatracker.ietf.org/doc/html/draft-ietf-jose-json-web-key-41
    */
   @Bean 
   public JWKSource<SecurityContext> jwkSource() {
      KeyPair keyPair = generateRsaKey();
      RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
      RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
      RSAKey rsaKey = new RSAKey.Builder(publicKey)
            .privateKey(privateKey)
            .keyID(UUID.randomUUID().toString())
            .build();
      JWKSet jwkSet = new JWKSet(rsaKey);
      return new ImmutableJWKSet<>(jwkSet);
   }
 
   /**
    *  生成RSA密钥对,给上面jwkSource() 方法的提供密钥对
    */
   private static KeyPair generateRsaKey() { 
      KeyPair keyPair;
      try {
         KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
         keyPairGenerator.initialize(2048);
         keyPair = keyPairGenerator.generateKeyPair();
      }
      catch (Exception ex) {
         throw new IllegalStateException(ex);
      }
      return keyPair;
   }
 
   /**
    * 配置jwt解析器
    */
   @Bean 
   public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
      return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
   }
 
   /**
    * 配置认证服务器请求地址
    */
   @Bean 
   public AuthorizationServerSettings authorizationServerSettings() {
      //什么都不配置,则使用默认地址
      return AuthorizationServerSettings.builder().build();
   }
}

在上面 SecurityConfig 代码中,我们已经开启了 OpenID Connect 1.0,于是我们就可以使用 http://127.0.0.1:9000/.well-known/openid-configuration 地址请求查看授权服务器信息了。

可以参考源码:OidcProviderConfigurationEndpointFilter,该类是 Spring Security 过滤链中的一个过滤器,当发起“/.well-known/openid-configuration”请求时,会被 OidcProviderConfigurationEndpointFilter 拦截

上面的配置信息中,其中 authorization_endpoint 为授权码的授权地址,device_authorization_endpoint 为设备授权码的授权地址,token_endpoint 为获取 token 的地址。

授权码模式测试

使用 http://localhost:9000/oauth2/authorize?response_type=code&client_id=oidc-client&scope=profile openid&redirect_uri=http://www.baidu.com 请求获取授权码

SSO

单点登录(Single Sign On),简称为 SSO,是比较流行的企业业务整合的解决方案之一。它的用途在于,不管多么复杂的应用群,只要在用户权限范围内,那么就可以做到,用户只需要登录一次就可以访问权限范围内的所有应用子系统。

OAuth2.1详细变化

OAuth 2.1 是 OAuth 2.0 的下一个版本, OAuth 2.1 根据最佳安全实践(BCP), 对 OAuth 2.0 协议进行整合和精简, 移除不安全的授权流程, 并发布了 OAuth 2.1 规范草案, 下面列出了和 OAuth 2.0 相比的主要区别。

https://fusionauth.io/articles/oauth/differences-between-oauth-2-oauth-2-1

推荐使用 Authorization Code + PKCE

根据 OAuth 2.0 安全最佳实践(Security Best Current Practices) 2.1.1 章节

隐式授权( Implicit Grant)已弃用

根据 OAuth 2.0 安全最佳实践(Security Best Current Practices) 2.1.2 章节

在 OAuth 2.1 规范草案中, 授权模式中已经找不到隐式授权(Implicit Grant), 我们知道, 隐式授权是 OAuth 2.0 中的授权模式, 是授权码模式的简化版本, 用户同意授权后, 直接就能返回访问令牌 access_token, 同时这种也是不安全的。

现在您可以考虑替换为 Authorization Code + PKCE 的授权模式。

密码授权 (Resource Owner Password Credentials Grant)已弃用

根据 OAuth 2.0 安全最佳实践(Security Best Current Practices) 2.4 章节

在 OAuth 2.1 规范草案中, 密码授权也被移除, 实际上这种授权模式在 OAuth 2.0中都是不推荐使用的, 密码授权的流程是, 用户把账号密码告诉客户端, 然后客户端再去申请访问令牌, 这种模式只在用户和客户端高度信任的情况下才使用。

使用 access_token 时, 不应该通过 URL 传递 token

根据 OAuth 2.0 安全最佳实践(Security Best Current Practices) 4.3.2 章节

刷新令牌 (Refresh Token) 应该是一次性的

根据 OAuth 2.0 安全最佳实践(Security Best Current Practices) 4.13.2 章节

回调地址(Redirect URI)应该精确匹配

[根据 OAuth 2.0 安全最佳实践(Security Best Current Practices) 4.1.3 章节](


http://www.kler.cn/a/464197.html

相关文章:

  • 部署项目添加工程名的步骤
  • PTA数据结构作业一
  • BGP基础配置实验
  • 什么是.net framework,什么是.net core,什么是.net5~8,版本对应关系
  • 如何利用 ClickHouse 实现高级分析:MySQL 到 ClickHouse 实时数据同步指南
  • Qt qtcreator配置cmake
  • Python爬虫基础——百度新闻页面结构剖析
  • MySQL:安装配置(完整教程)
  • 散度与旋度的探讨
  • 《ChatGPT介绍》
  • TCP/IP 教程
  • Flink源码解析之:如何根据JobGraph生成ExecutionGraph
  • uniapp H5 对接 声网,截图
  • 【技术新浪潮】DeepSeek-V3:中国AI的开源巨浪,全球AI格局的破局者
  • C# 设计模式:装饰器模式与代理模式的区别
  • 力扣hot100——二叉树
  • 高效使用AI完成编程项目任务的指南:从需求分析到功能实现
  • 华为OD E卷(100分)45-喊7的次数重排
  • 【网站推荐】IP反查域名实战
  • leetcode 729. 我的日程安排表 I 中等
  • 小程序配置文件 —— 15 页面配置
  • 【2024美国数学建模AB题原文翻译】
  • 基于QT(C++)实现的坦克大战
  • 力扣刷题:栈和队列OJ篇(下)
  • 力扣-数据结构-10【算法学习day.81】
  • 浅谈Beam Search