OAuth 2.0

发布于:2025-02-11 ⋅ 阅读:(71) ⋅ 点赞:(0)

简介

OAuth 是一种开放标准的授权协议或框架,它提供了一种安全的方式,使第三方应用程序能够访问用户在其他服务上的受保护资源,而无需共享用户的凭证(如用户名和密码)。OAuth 的核心思想是通过“授权令牌”来代替直接使用用户名和密码进行身份验证。OAuth 不是属于任何一家公司的产品或服务。OAuth 是一个开放标准的授权框架,它由互联网工程任务组(IETF)定义,并通过社区贡献不断发展和完善。
OAuth 的开发和维护是由多个组织和个人共同参与的,不是由单一公司控制或拥有的。因此,它被广泛应用于各种互联网服务中,包括但不限于Google、Facebook、Twitter、Microsoft等大公司提供的服务。这些公司实现了OAuth协议来支持第三方应用的安全授权流程。
OAuth 名字里面的O代表Open,名字里的Auth是 “Authentication”(身份验证)或 “Authorization”(授权)的缩写。OAuth 的全称可以理解为 Open Authorization 或 Open Authentication,
主要特点

  • 安全性:避免了直接暴露用户的敏感信息,减少了潜在的安全风险。
  • 灵活性:支持多种授权模式,适用于不同的应用场景。
  • 广泛适用性:被众多互联网服务提供商采用,如WeiChat、Google、Facebook、Twitter等。

OAuth 1.0 和 OAuth 2.0 的区别

OAuth 1.0 和 OAuth 2.0 是两种不同的授权协议版本,虽然它们的目标都是为了安全地授权第三方应用访问用户资源,但在设计和实现上有显著差异。以下是两者的主要区别:

  1.  签名机制
    - OAuth 1.0:
      - 使用复杂的签名算法(如 HMAC-SHA1 或 RSA-SHA1)对每个请求进行签名。
      - 需要在每次请求中包含签名参数,增加了开发和调试的复杂性。
    - OAuth 2.0:
      - 不再强制要求对每个请求进行签名,而是依赖于传输层安全(TLS/SSL)来保护通信。
      - 简化了客户端的实现,减少了出错的可能性。
  2. 授权类型
    - OAuth 1.0:
      - 主要支持“三腿”(Three-legged)授权流程,即涉及客户端、用户和授权服务器。
    - OAuth 2.0:
      - 支持多种授权类型,包括但不限于:
        - 授权码模式(Authorization Code):适用于Web应用程序。
        - 隐式模式(Implicit):适用于浏览器或移动应用等无法安全存储密钥的客户端。
        - 密码模式(Resource Owner Password Credentials):适用于信任度高的客户端。
        - 客户端凭证模式(Client Credentials):适用于客户端直接访问资源而无需用户参与的场景。
  3. 令牌类型
    - OAuth 1.0:
      - 使用临时令牌(Request Token)和访问令牌(Access Token),需要额外的步骤来交换临时令牌为访问令牌。
      
    - OAuth 2.0:
      - 直接使用访问令牌(Access Token),简化了流程。
      - 引入了刷新令牌(Refresh Token),允许在访问令牌过期后获取新的访问令牌,而不需要再次让用户授权。
  4. 安全性
    - OAuth 1.0:
      - 依赖于签名机制来确保请求的完整性和真实性,但签名算法复杂且容易出错。
      
    - OAuth 2.0:
      - 更加依赖于HTTPS/TLS来保证通信的安全性,简化了安全配置。
      - 提供了更灵活的安全选项,例如通过短寿命的访问令牌和刷新令牌来增强安全性。
  5. 易用性和灵活性
    - OAuth 1.0:
      - 实现较为复杂,尤其是签名算法的处理,增加了开发和维护成本。
      
    - OAuth 2.0:
      - 设计更加简洁,易于实现和集成。
      - 提供了更多的灵活性,适用于各种类型的客户端和应用场景。
  6. 社区和支持
    - OAuth 1.0:
      - 已经逐渐被弃用,社区支持较少。
      
    - OAuth 2.0:
      - 广泛采用,拥有活跃的社区和丰富的文档及工具支持。

 总结
OAuth 2.0 在多个方面进行了改进,简化了授权流程,增强了灵活性,并依赖现代的安全措施(如TLS)。因此,OAuth 2.0 成为了当前最常用的授权协议版本,广泛应用于各种Web和移动应用中。

OAuth 2.0 的主要角色

  •  资源所有者(Resource Owner):通常是用户,拥有受保护资源的人。
  • 客户端(Client):希望访问受保护资源的应用程序,或三方应用程序。
  • 授权服务器(Authorization Server):负责颁发访问令牌。
  • 资源服务器(Resource Server):存储受保护资源并根据访问令牌提供资源。 

OAuth 2.0授权时序图

OAuth 2.0的授权与身份验证

 1. 授权(Authorization)

 OAuth 2.0 提供了多种授权模式(Grant Types),每种模式适用于不同的应用场景。以下是常见的授权模式及其技术实现:

 1.1 授权码模式(Authorization Code Grant)

- 适用场景:Web应用、移动应用等需要高安全性且能够安全存储客户端密钥的应用。
- 技术实现:
  - 重定向URI(redirect_uri):用户授权后,授权服务器将用户重定向到指定的URL,并附带授权码。
  - 授权码(Authorization Code):临时代码,用于换取访问令牌。
  - 客户端凭证(Client Credentials):客户端ID和客户端密钥,用于验证客户端身份。
  - TLS/SSL:确保通信的安全性。

 1.2 隐式模式(Implicit Grant)

- 适用场景:浏览器应用、单页应用(SPA)等无法安全存储客户端密钥的应用。
- 技术实现:
  - 重定向URI(redirect_uri):用户授权后,授权服务器直接将访问令牌附加到重定向URI的片段标识符中。
  - 访问令牌(Access Token):直接返回给客户端应用,无需额外步骤。
  - TLS/SSL:确保通信的安全性。

 1.3 密码模式(Resource Owner Password Credentials Grant)

- 适用场景:信任度高的客户端应用,如第一方应用。
- 技术实现:
  - 用户名和密码:用户直接提供凭证给客户端应用。
  - 客户端凭证(Client Credentials):客户端ID和客户端密钥,用于验证客户端身份。
  - TLS/SSL:确保通信的安全性。

 1.4 客户端凭证模式(Client Credentials Grant)

- 适用场景:客户端直接访问资源而无需用户参与的场景,如服务到服务通信。
- 技术实现:
  - 客户端凭证(Client Credentials):客户端ID和客户端密钥,用于验证客户端身份。
  - TLS/SSL:确保通信的安全性。

同时如果当访问令牌过期时,客户端可以使用刷新令牌来请求新的访问令牌,而不需要用户重新进行身份验证。

2. 验证(Authentication and Validation)

 2.1 访问令牌验证(Access Token Validation)

- JWT格式的访问令牌:
  - 签名验证:使用公钥或对称密钥验证JWT的签名,确保令牌未被篡改。
  - 过期时间检查:验证'exp'(过期时间)声明,确保令牌仍在有效期内。
  - 受众检查:验证'aud'(受众)声明,确保令牌是发给当前应用的。
  - 签发者检查:验证'iss'(签发者)声明,确保令牌来自可信的授权服务器。

- 不透明的访问令牌:
  -  introspection 端点:通过调用授权服务器的'/token/introspect'端点,验证令牌的有效性和其他属性。

 2.2 ID Token 验证(仅限OpenID Connect)

- 签名验证:使用公钥或对称密钥验证JWT的签名,确保ID Token未被篡改。
- 过期时间检查:验证'exp'(过期时间)声明,确保ID Token仍在有效期内。
- 受众检查:验证'aud'(受众)声明,确保ID Token是发给当前应用的。
- 签发者检查:验证'iss'(签发者)声明,确保ID Token来自可信的授权服务器。
- nonce检查:验证'nonce'声明,确保ID Token与原始授权请求中的'nonce'参数匹配,防止重放攻击。
- at_hash检查:如果同时使用了访问令牌,验证'at_hash'声明以确保访问令牌和ID Token的一致性。

使用Spring搭建OAuth 2.0 服务

1.创建Spring项目

2. 添加OAuth2 依赖

<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Spring Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <!-- Spring Security OAuth2 Authorization Server -->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-oauth2-authorization-server</artifactId>
        <version>0.2.3</version> <!-- 请使用最新版本 -->
    </dependency>

    <!-- Spring Data JPA -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <!-- H2 Database -->
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!-- Spring Boot DevTools -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>

    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

    <!-- Spring Boot Starter Test -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

3. 配置数据库

spring:
  datasource:
    url: jdbc:h2:mem:testdb
    driver-class-name: org.h2.Driver
    username: sa
    password:
  h2:
    console:
      enabled: true
      path: /h2-console
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        format_sql: true

 4. 配置Spring Security

 创建一个配置类来设置Spring Security,包括用户认证和授权服务器配置。

4.1. 创建用户实体和仓库

首先,创建一个简单的用户实体和JPA仓库。
User.java  

package com.example.oauth2server.model;

import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import javax.persistence.*;
import java.util.Collection;
import java.util.List;

@Entity
@Data
public class User implements UserDetails {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;
    private String password;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return List.of(new SimpleGrantedAuthority("ROLE_USER"));
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

UserRepository.java

package com.example.oauth2server.repository;

import com.example.oauth2server.model.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
    User findByUsername(String username);
}
4.2. 配置用户认证

创建一个配置类来设置用户认证。
SecurityConfig.java 

package com.example.oauth2server.config;

import com.example.oauth2server.model.User;
import com.example.oauth2server.repository.UserRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
public class SecurityConfig {

    private final UserRepository userRepository;

    public SecurityConfig(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return username -> userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found"));
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
 4.3. 配置授权服务器

创建一个配置类来设置授权服务器。
AuthorizationServerConfig.java

package com.example.oauth2server.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;

@Configuration
@EnableAuthorizationServer
@Order(1)
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    private final PasswordEncoder passwordEncoder;

    public AuthorizationServerConfig(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()");
    }

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

    @Bean
    public TokenStore tokenStore() {
        return new InMemoryTokenStore();
    }

    @Override
    public void configure(org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("client")
                .secret(passwordEncoder.encode("secret"))
                .authorizedGrantTypes("authorization_code", "refresh_token", "password")
                .scopes("read", "write")
                .redirectUris("http://localhost:8080/callback")
                .accessTokenValiditySeconds(3600)
                .refreshTokenValiditySeconds(2592000);
    }
}
4.4. 配置资源服务器

 创建一个配置类来设置资源服务器。
ResourceServerConfig.java

package com.example.oauth2server.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/oauth/**").permitAll()
            .anyRequest().authenticated();
    }
}
4.5. 创建控制器
package com.example.oauth2server.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping("/getAuth")
    public String hello() {
        return xxxx.xxx();
    }
}
 4.6. 初始化数据
package com.example.oauth2server.config;

import com.example.oauth2server.model.User;
import com.example.oauth2server.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class DataLoader {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Bean
    public CommandLineRunner dataLoader() {
        return args -> {
            if (userRepository.findByUsername("user") == null) {
                User user = new User();
                user.setUsername("user");
                user.setPassword(passwordEncoder.encode("password"));
                userRepository.save(user);
            }
        };
    }
}

OpenID Connect 协议

OpenID Connect (OIDC) 是一个基于 OAuth 2.0 的身份验证协议,它提供了一种简单的方式让客户端(如 Web 应用、移动应用等)验证终端用户的身份,并获取其基本个人信息。以下是 OpenID Connect 的一些关键特性:
身份验证层:OpenID Connect 在 OAuth 2.0 授权框架之上增加了一个身份验证层,允许应用程序确认用户的身份。
ID Token:OpenID Connect 引入了 ID Token,这是一个 JWT(JSON Web Token),包含了用户的身份信息(如用户标识符、发行者、过期时间等)。ID Token 用于向客户端证明用户的身份。
UserInfo Endpoint:除了 ID Token,OpenID Connect 还提供了 UserInfo Endpoint,客户端可以通过该端点获取用户的详细信息(如姓名、电子邮件等)。
安全性和隐私:OpenID Connect 支持多种安全机制,包括使用加密签名和加密传输,确保通信的安全性。
主要流程
授权请求:客户端重定向用户到授权服务器进行身份验证。
用户认证:用户在授权服务器上输入凭据进行身份验证。
授权响应:授权服务器验证用户后,返回授权码或 ID Token 给客户端。
令牌交换:客户端使用授权码从授权服务器获取访问令牌和 ID Token。
用户信息获取:客户端可以使用访问令牌调用 UserInfo Endpoint 获取更多用户信息。
使用场景
单点登录 (SSO):多个应用程序可以共享同一个身份验证过程,用户只需登录一次即可访问多个服务。
第三方登录:允许用户通过第三方身份提供商(如 Google、Facebook 等)登录应用程序。
API 访问控制:结合 OAuth 2.0,可以实现对 API 的安全访问控制。

通过OAth2.0 获取到的token, 被黑客接货后可以利用吗? 

通过 OAuth 2.0 获取到的令牌(Token)如果被黑客截获,确实可能被利用,具体取决于令牌的类型和使用方式。以下是几种常见的情况及其应对措施:

 1. 访问令牌(Access Token)

  风险:如果访问令牌被截获,攻击者可以在其有效期内使用该令牌访问受保护的资源。
  缓解措施:
    短生命周期:将访问令牌的有效期设置得较短(例如几分钟),以减少潜在损害。
    刷新令牌(Refresh Token):使用刷新令牌来获取新的访问令牌,刷新令牌应存储在更安全的地方,并且需要额外的身份验证步骤(如客户端凭证)。
    令牌绑定:某些实现可以将访问令牌与特定的客户端或用户会话绑定,确保即使令牌被截获,也只能在特定环境中使用。

 2. 授权码(Authorization Code)

  风险:如果授权码被截获,攻击者可以在其有效期内交换访问令牌。
  缓解措施:
    PKCE(Proof Key for Code Exchange):使用 PKCE 扩展,客户端生成一个代码挑战(code challenge),并在请求授权码时发送给授权服务器。授权服务器在返回授权码时会验证这个挑战,从而防止授权码被拦截后滥用。
    短生命周期:授权码通常有很短的有效期(例如几分钟),并且只能使用一次。

 3. ID Token

  风险:如果 ID Token 被截获,攻击者可以解析其中的信息,但无法直接用于访问资源。
  缓解措施:
    签名验证:确保客户端和服务端都验证 ID Token 的签名,防止伪造。
    加密传输:始终通过 HTTPS 传输令牌,确保通信的安全性。

 其他安全措施

  HTTPS:所有涉及令牌的通信必须通过 HTTPS 进行,以防止中间人攻击。
  令牌存储:确保令牌在客户端和服务器端都安全存储,避免明文存储或暴露在日志中。
  多因素认证(MFA):启用多因素认证,增加额外的安全层,即使令牌被截获,攻击者也难以完全控制账户。
  监控和警报:实施监控和警报机制,检测异常的令牌使用行为,及时响应潜在的安全事件。

 总结

虽然 OAuth 2.0 提供了强大的身份验证和授权机制,但如果令牌被截获,仍然存在一定的风险。通过采用上述最佳实践,可以显著降低这些风险,确保系统的安全性。
 


网站公告

今日签到

点亮在社区的每一天
去签到