OAuth 2.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 依赖

XML 复制代码
<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. 配置数据库

XML 复制代码
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

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

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

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

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

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. 创建控制器
java 复制代码
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. 初始化数据
java 复制代码
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);
            }
        };
    }
}
相关推荐
怪咖学生2 个月前
<meta property=“og:type“ content=“website“>
dreamweaver
燕双嘤3 个月前
Require:基于雪花算法完成一个局部随机,全局离散没有热点切唯一的数值Id生成器。
算法·dreamweaver
迷途小书童的Note3 个月前
超级Prompt!
prompt·dreamweaver
2401_851917004 个月前
elasticsearch文档Delete By Query API(一)
大数据·elasticsearch·dreamweaver
软件技术NINI5 个月前
html制作卡通图案代码,使用HTML和CSS3绘制基本卡通图案的示例分享
css·html·dreamweaver
Daniel521-Spark5 个月前
三十六、MyBatis-Plus(2)
mybatis·dreamweaver
rorg6 个月前
Laravel 中 使用模型作为标志
php·laravel·dreamweaver
苏格拉真没有底7 个月前
如何使用 Selenium 和 Beautiful Soup 抓取动态内容
selenium·microsoft·dreamweaver
软件技术NINI7 个月前
html制作小猪佩奇卡通图案代码,使用HTML和CSS3绘制基本卡通图案的示例分享
前端·css·html·dreamweaver