OAuth2.0

1.分布式系统认证需求

分布式系统的每个服务都会有认证、授权的需求,如果每个服务都实现一套认证授权逻辑会非常冗余,考虑分布式系统共享性的特点,需要有独立的认证服务处理系统认证授权的请求;考虑分布式系统开放性的特点,不仅对系统内部服务提供认证,对第三方系统也需要提供认证。分布式认证的需求总结如下:

  1. 统一认证授权

    提供独立的认证服务,统一处理认证授权。

    无论是不同类型的用户,还是不同种类的客户端,均采用一致的认证、权限、会话机制,实现统一认证授权。

    要实现统一则认证方式必须课扩展,支持各种认证需求,比如:用户密码认证、短信验证码、二维码、人脸识别,并可以灵活的切换。

  2. 应用接入认证

    应提供扩展和开放能力,提供安全的系统对接机制,并可开放部分API给第三方使用,一方应用(内部系统服务)和三方应用(第三方应用)均采用统一机制接入。

2.分布式认证方案

2.1选型分析

2.1.1基于session的认证方式

在分布式的环境下,基于session的认证会出现一个问题,每个应用服务都需要在session中存储用户身份信息,通过负载均衡将本地的请求分配到另一个应用服务需要将session信息带过去,否则会重新认证。

img

这个时候,通常的做法有下面几种:

  • Session复制:多台应用服务器之间同步session,使session保持一致,对外透明
  • Session黏贴:当用户访问集群中某台服务器后,强制指定后续所有请求均落到此机器上
  • Session集中存储:将Session存入分布式缓存中,所有服务器应用实例统一从分布式缓存中存取Session。

总体来讲,基于session认证的认证方式,可以更好的在服务端对会话进行控制,且安全性较高。但是,session机 制方式基于cookie,在复杂多样的移动客户端上不能有效的使用,并且无法跨域,另外随着系统的扩展需提高 session的复制、黏贴及存储的容错性。

2.1.2基于token的认证方式

基于token的认证方式,服务端不用存储认证数据,易维护扩展性强, 客户端可以把token存在任意地方,并且可以实现web和app统一认证机制。其缺点也很明显,token由于自包含信息,因此一般数据量较大,而且每次请求都需要传递,因此比较占带宽。另外,token的签名验签操作也会给cpu带来额外的处理负担。

img

2.2技术方案

根据选型的分析,决定采用基于token的认证方式,它的优点是:

  1. 适合统一认证的机制,客户端、一方应用、三方应用都遵循一致的认证机制。
  2. token认证方式对第三方应用接入更适合,因为它更开放,可使用当前有流行的开放协议Oauth2.0、JWT等。
  3. 一般情况服务端无需存储会话信息,减轻了服务端的压力。

分布式系统认证技术方案见下图:
img
流程描述:

  1. 用户通过接入方(应用)登录,接入方采取OAuth2.0方式在统一认证服务(UAA)中认证。
  2. 认证服务(UAA)调用验证该用户的身份是否合法,并获取用户权限信息。
  3. 认证服务(UAA)获取接入方权限信息,并验证接入方是否合法。
  4. 若登录用户以及接入方都合法,认证服务生成jwt令牌返回给接入方,其中jwt中包含了用户权限及接入方权限。
  5. 后续,接入方携带jwt令牌对API网关内的微服务资源进行访问。
  6. API网关对令牌解析、并验证接入方的权限是否能够访问本次请求的微服务。
  7. 如果接入方的权限没问题,API网关将原请求header中附加解析后的明文Token,并将请求转发至微服务。
  8. 微服务收到请求,明文token中包含登录用户的身份和权限信息。因此后续微服务自己可以干两件事:1,用 户授权拦截(看当前用户是否有权访问该资源)2,将用户信息存储进当前线程上下文(有利于后续业务逻辑随时 获取当前用户信息)

流程所涉及到UAA服务、API网关这三个组件职责如下:

  1. 统一认证服务(UAA)
    它承载了OAuth2.0接入方认证、登入用户的认证、授权以及生成令牌的职责,完成实际的用户认证、授权功能。
  2. API网关
    作为系统的唯一入口,API网关为接入方提供定制的API集合,它可能还具有其它职责,如身份验证、监控、负载均衡、缓存等。API网关方式的核心要点是,所有的接入方和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。

3.OAuth2.0

3.1 OAuth2.0介绍

OAuth(开放授权)是一个开放标准,允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方应用或分享他们数据的所有内容。OAuth2.0是OAuth协议的延续版本,但不向后兼容OAuth 1.0即完全废止了OAuth1.0。很多大公司如Google,Yahoo,Microsoft等都提供了OAUTH认证服务。

下边分析一个Oauth2认证的例子,通过例子去理解OAuth2.0协议的认证流程,本例子是网站使用微信认证的过程,这个过程的简要描述如下:

  1. 客户端请求第三方授权用户进入程序的登录页面,点击微信的图标以微信账号登录系统,用户是自己在微信里信息的资源拥有者。
  2. 资源拥有者同意给客户端授权:资源拥有者扫描二维码表示资源拥有者同意给客户端授权,微信会对资源拥有者的身份进行验证, 验证通过后,微信会询问用户是否给授权网站访问自己的微信数据,用户点击“确认登录”表示同意授权,微信认证服务器会颁发一个授权码,并重定向到网站。
  3. 客户端获取到授权码,请求认证服务器申请令牌:客户端应用程序请求认证服务器,请求中携带授权码。
  4. 认证服务器向客户端响应令牌:微信认证服务器验证了客户端请求的授权码,如果合法则给客户端颁发令牌,令牌是客户端访问资源的通行证。 此交互过程用户看不到,当客户端拿到令牌后,用户在网站看到已经登录成功。
  5. 客户端携带令牌访问资源服务器的资源:网站携带令牌请求访问微信服务器获取用户的基本信息。
  6. 资源服务器返回受保护资源:资源服务器校验令牌的合法性,如果合法则向用户响应资源信息内容。

以上认证授权详细的执行流程如下:

img

OAauth2.0协议rfc6749当中的认证流程:

img

OAauth2.0包括以下角色:

  1. 客户端
    本身不存储资源,需要通过资源拥有者的授权去请求资源服务器的资源,比如:Android客户端、Web客户端(浏览器端)、微信客户端等。
  2. 资源拥有者
    通常为用户,也可以是应用程序,即该资源的拥有者。
  3. 授权服务器(也称认证服务器)
    用于服务提供商对资源拥有的身份进行认证、对访问资源进行授权,认证成功后会给客户端发放令牌 (access_token),作为客户端访问资源服务器的凭据。本例为微信的认证服务器。
  4. 资源服务器
    存储资源的服务器,本例子为微信存储的用户信息。

现在还有一个问题,服务提供商能允许随便一个客户端就接入到它的授权服务器吗?答案是否定的,服务提供商会给准入的接入方一个身份,用于接入时的凭据:

  • client_id:客户端标识
  • client_secret:客户端秘钥

因此,准确来说,授权服务器对两种OAuth2.0中的两个角色进行认证授权,分别是资源拥有者、客户端。

4.Spring Cloud Security OAuth2

4.1环境介绍

Spring-Security-OAuth2是对OAuth2的一种实现,和Spring Security相辅相成,与Spring Cloud体系的集成也非常便利。

OAuth2.0的服务提供方涵盖两个服务,即授权服务 (Authorization Server,也叫认证服务) 和资源服务 (Resource Server),使用 Spring Security OAuth2 的时候你可以选择把它们在同一个应用程序中实现,也可以选择建立使用同一个授权服务的多个资源服务。

授权服务 (Authorization Server)应包含对接入端以及登入用户的合法性进行验证并颁发token等功能,对令牌的请求端点由 Spring MVC 控制器进行实现,下面是配置一个认证服务必须要实现的endpoints:

  • AuthorizationEndpoint 服务于认证请求。默认 URL: /oauth/authorize
  • TokenEndpoint 服务于访问令牌的请求。默认 URL: /oauth/token

**资源服务 (Resource Server)**,应包含对资源的保护功能,对非法请求进行拦截,对请求中token进行解析鉴权等,下面的过滤器用于实现 OAuth 2.0 资源服务

  • OAuth2AuthenticationProcessingFilter用来对请求给出的身份令牌解析鉴权。

微服务环境以认证中心(UAA)Order服务为例,认证流程如下:

image-20240211184055555

  1. 客户端请求认证中心(UAA)授权服务进行认证。
  2. 认证后由认证中心颁发令牌。
  3. 客户端携带令牌Token请求资源服务。
  4. 资源服务校验令牌和合法性,合法及返回资源信息。

授权服务配置

可以用 @EnableAuthorizationServer 注解并继承AuthorizationServerConfigurerAdapter来配置OAuth2.0 授权服务器。

AuthorizationServerConfigurerAdapter要求配置以下几个类,这几个类是由Spring创建的独立的配置对象,它们会被Spring传入AuthorizationServerConfigurer中进行配置。

public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer {

	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
	}

	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
	}

	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
	}

}
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        super.configure(security);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        super.configure(clients);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        super.configure(endpoints);
    }
}
  • ClientDetailsServiceConfigurer:用来配置客户端详情服务。
  • AuthorizationServerEndpointsConfigurer:用来配置令牌(token)的访问端点和令牌服务(token services)。
  • AuthorizationServerSecurityConfigurer:用来配置令牌端点的安全约束.

2.2.2.1.配置客户端详细信息

ClientDetailsServiceConfigurer 能够使用内存或者JDBC来实现客户端详情服务(ClientDetailsService), ClientDetailsService负责查找ClientDetails,而ClientDetails有几个重要的属性如下列表:

  • clientId:(必须的)用来标识客户的Id。
  • secret:(需要值得信任的客户端)客户端安全码,如果有的话。
  • scope:用来限制客户端的访问范围,如果为空(默认)的话,那么客户端拥有全部的访问范围。
  • authorizedGrantTypes:此客户端可以使用的授权类型,默认为空。
  • authorities:此客户端可以使用的权限(基于Spring Security authorities)。

客户端详情(Client Details)能够在应用程序运行的时候进行更新,可以通过访问底层的存储服务(例如将客户端详情存储在一个关系数据库的表中,就可以使用 JdbcClientDetailsService)或者通过自己实现 ClientRegistrationService接口(同时你也可以实现 ClientDetailsService 接口)来进行管理。

2.2.2.2.管理令牌

在Spring Security中,TokenStore 是用于管理令牌(Token)的接口。它负责存储和检索授权信息,以便在用户访问受保护的接口时进行验证。下面是几个 TokenStore 的实现类:

  1. InMemoryTokenStore:将 OAuth2 访问令牌保存在内存中,使用 ConcurrentHashMap 管理。这是一种简单且轻量级的实现方式
  2. JdbcTokenStore:将 OAuth2 访问令牌存储在数据库中,通常使用关系型数据库(如 MySQL、PostgreSQL)来持久化令牌数据。这样可以实现跨服务器共享令牌信。
  3. JwkTokenStore:用于处理 JSON Web Key Set(JWKS)中的令牌。JWKS 是一种用于安全传输令牌的标准格式,通常与 OpenID Connect 和 OAuth2 配合使用。
  4. RedisTokenStore:将 OAuth2 访问令牌存储在 Redis 数据库中,具有高性能和可扩展性。这对于分布式系统和微服务架构非常有用

image-20240212131746305

定义TokenConfig
在config包下定义TokenConfig

2.2.2.3.令牌访问端点配置

AuthorizationServerEndpointsConfigurer 是 Spring Security OAuth2 中的一个配置类,用于配置 OAuth2 授权服务器的端点(endpoints)。通过设置以下属性,我们可以决定支持的授权类型(Grant Types):

  1. **authenticationManager**:指定用于验证用户身份的 AuthenticationManager 实例。这是必需的,因为授权服务器需要验证用户的凭据。
  2. **tokenStore**:指定用于存储访问令牌的 TokenStore 实现类。不同的 TokenStore 实现方式决定了令牌的存储位置,如内存、数据库或 Redis。
  3. **userDetailsService**:指定用于加载用户信息的 UserDetailsService 实现类。授权服务器需要根据用户名查找用户信息,以便生成令牌。
  4. **authorizationCodeServices**:指定用于处理授权码授权类型的服务。授权码授权类型通常用于 Web 应用程序的身份验证流程。
  5. **implicitGrantService**:指定用于处理隐式授权类型的服务。隐式授权类型通常用于单页应用程序(SPA)的身份验证流程。
  6. **tokenGranter**:指定自定义的 TokenGranter 实现类,用于支持自定义的授权类型。例如,你可以实现自己的授权类型,然后在这里注册。

AuthorizationServerEndpointsConfigurer 允许我们根据项目需求配置授权服务器的不同端点,以支持不同的授权类型。

配置授权端点的URL(Endpoint URLs)
AuthorizationServerEndpointsConfigurer 这个配置对象有一个叫做 pathMapping() 的方法用来配置端点URL链接,它有两个参数:

  • 第一个参数:String 类型的,这个端点URL的默认链接。
  • 第二个参数:String 类型的,你要进行替代的URL链接。

以上的参数都将以 “/“ 字符为开始的字符串,框架的默认URL链接如下列表,可以作为这个 pathMapping() 方法的 第一个参数:

  • /oauth/authorize:授权端点。
  • /oauth/token:令牌端点。
  • /oauth/confirm_access:用户确认授权提交端点。
  • /oauth/error:授权服务错误信息端点。
  • /oauth/check_token:用于资源服务访问的令牌解析端点。
  • /oauth/token_key:提供公有密匙的端点,如果你使用JWT令牌的话。

需要注意的是授权端点这个URL应该被Spring Security保护起来只供授权用户访问.

2.2.2.4.令牌端点的安全约束

AuthorizationServerSecurityConfigure:用来配置令牌端点(Token Endpoint)的安全约束,在 AuthorizationServer中配置如下

@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
    // 允许访问 token 的公钥,默认 /oauth/token_key 是受保护的
    security.tokenKeyAccess("permitAll()")
            // 允许检查 token 的状态,默认 /oauth/check_token 是受保护的
            .checkTokenAccess("permitAll()");
}

5.OAuth2的四种授权模式

OAuth 2.0 定义了四种授权方式,每种方式适用于不同的场景和需求。

授权码(authorization code)

这是最常用且安全性最高的授权方式。适用于有后端的 Web 应用。流程如下:

  • 用户点击 A 网站提供的链接,跳转到 B 网站并授权用户数据给 A 网站。
  • B 网站返回一个授权码给 A 网站。
  • A 网站使用授权码在后端向 B 网站请求令牌。

image-20240212135331333

  1. 资源拥有者打开客户端,客户端要求资源拥有者给予授权,它将浏览器被重定向到授权服务器,重定向时会附加客户端的身份信息。如:
/oauth/authorize?client_id=music-community&response_type=code&scope=all&redirect_uri=http://www.baidu.com

参数列表如下:

  • client_id:客户端准入标识。
  • response_type:授权码模式固定为code。
  • scope:客户端权限。
  • redirect_uri:跳转uri,当授权码申请成功后会跳转到此地址,并在后边带上code参数(授权码)。
  1. 浏览器出现向授权服务器授权页面,之后同意授权。
  2. 授权服务器将授权码(AuthorizationCode)转经浏览器发送给client(通过redirect_uri)。
  3. 客户端拿着授权码向授权服务器索要访问access_token,请求如下:
/oauth/token? client_id=c1&client_secret=secret&grant_type=authorization_code&code=5PgfcD&redirect_uri=http://www.baidu.com
  1. 授权服务器返回令牌(access_token)

隐藏式(implicit)

适用于纯前端应用,没有后端的情况。令牌直接传给前端,但安全性较低,令牌有效期通常只在会话期间内。

  • 用户跳转到 B 网站,登录并同意授权。
  • B 网站将令牌作为 URL 锚点传给 A 网站。

密码式(password)

用户直接将用户名和密码告知应用,应用使用这些凭据申请令牌。

  • A 网站要求用户提供 B 网站的用户名和密码。
  • A 网站使用这些凭据向 B 网站请求令牌。
/oauth/token?client_id=music-community&client_secret=secret&grant_type=password&username=shangsan&password=123

参数列表如下:

  • client_id:客户端准入标识。
  • client_secret:客户端秘钥。
  • grant_type:授权类型,填写password表示密码模式
  • username:资源拥有者用户名。
  • password:资源拥有者密码。

客户端凭证(client credentials)

适用于客户端应用,不涉及用户的授权。

  • 第三方应用先备案,获取客户端 ID 和客户端密钥。
  • 应用使用这些凭证直接向授权服务器请求令牌。

OAuth2.0
https://xhablog.online/2024/03/03/OAuth2.0/
作者
Xu huaiang
发布于
2024年3月3日
许可协议