自学内容网 自学内容网

Spring Boot实战:SSO和OAuth2.0

SSO和OAuth2.0

SSO(单点登录)和OAuth 2.0 是两个在认证和授权场景中常用的技术概念。它们的应用场景、目标和工作机制不同,但在一些方面也有联系。以下是它们的区别和联系的详细分析。

一、SSO(Single Sign-On)

1.1 定义

SSO(单点登录)是一种认证机制,它允许用户只需登录一次即可访问多个独立的应用系统,而不需要在每个系统上重复登录。SSO的核心是用户身份认证,即确定用户是谁,并在多个应用系统之间共享这一登录状态。

1.2 工作机制

SSO通常的工作流程如下:

  1. 用户访问一个需要认证的系统。
  2. 如果用户未登录,系统将用户重定向到SSO认证中心。
  3. 用户在认证中心登录,认证通过后,SSO认证中心生成一个令牌并返回给系统。
  4. 系统验证该令牌,用户成功登录并获得访问权限。
  5. 当用户访问其他系统时,系统使用同样的令牌或共享会话信息进行认证,允许用户无缝访问。
1.3 应用场景
  • 企业内部多个系统的统一登录。
  • 门户网站中多个子系统共享同一登录入口。
  • 需要在不同应用之间频繁切换的场景。
1.4 核心目标
  • 用户身份认证:主要目标是解决用户的单点登录问题,即多个系统共享用户的登录状态。

二、OAuth 2.0

2.1 定义

OAuth 2.0 是一种授权协议,允许第三方应用在不暴露用户凭据的情况下,安全地访问用户的资源。OAuth 2.0 的核心是授权,即用户授权第三方应用访问其资源的权限,而不是直接进行用户认证。

2.2 工作机制

OAuth 2.0的典型授权流程如下:

  1. 客户端(第三方应用)请求用户授权。
  2. 用户被引导到授权服务器,在登录并同意授权后,授权服务器会返回一个授权码给客户端。
  3. 客户端使用授权码向授权服务器请求访问令牌。
  4. 授权服务器颁发access_token,客户端使用该令牌访问资源服务器上的受保护资源。
2.3 应用场景
  • 第三方应用希望访问用户的数据或服务(如社交媒体API、支付系统)。
  • 用户授权应用访问其云存储文件、社交平台上的照片等场景。
  • 应用间的API授权和集成。
2.4 核心目标
  • 授权:主要解决第三方应用访问用户资源的问题,让用户能够控制第三方应用访问的权限和范围。

三、SSO与OAuth 2.0的区别

特性SSO(单点登录)OAuth 2.0(授权)
核心概念用户身份认证,解决多系统共享登录状态授权,允许第三方应用代表用户访问资源
重点认证,确定用户身份授权,控制第三方应用对资源的访问
典型场景用户在多个应用之间切换时无需重复登录第三方应用请求访问用户资源(API、数据)
主要角色用户、SSO认证中心、多个应用系统资源拥有者、客户端(应用)、授权服务器、资源服务器
令牌使用方式会话、Cookie 或 SSO令牌用于标识用户身份通过access_tokenrefresh_token 授权访问资源
授权范围不涉及资源授权,主要是身份认证用户可以细化对第三方应用的授权范围
安全目标提供用户统一的登录体验,减少多次登录风险控制第三方应用对用户数据的访问权限
实现复杂度相对简单,主要关注登录状态共享较高,涉及授权、令牌管理、授权范围和安全性

四、SSO与OAuth 2.0的联系

尽管SSO和OAuth 2.0在概念上有所不同,但它们在某些情况下可以结合使用,尤其是在现代的分布式系统和开放平台中。

4.1 SSO和OAuth 2.0的结合使用
  • OAuth 2.0 作为 SSO 的实现手段:OAuth 2.0通过授权服务器认证用户,并生成授权码或访问令牌(access token),允许用户使用相同的令牌在多个系统间访问资源,实现类似SSO的效果。例如,用户在一个门户网站(SSO认证中心)登录后,可以使用OAuth 2.0生成的令牌访问其他子系统,而不需要每次都重新登录。
  • OpenID Connect (OIDC):OIDC是基于OAuth 2.0的认证层协议,结合了OAuth 2.0的授权和SSO的认证功能。OIDC在OAuth 2.0的基础上,添加了用户身份信息,允许用户通过OAuth 2.0协议认证并共享登录状态。因此,OpenID Connect可以被视为一种结合了SSO和OAuth 2.0的协议,是一种标准的SSO解决方案。
4.2 SSO与OAuth 2.0的配合场景
  • 门户系统:在一些复杂的系统中,用户可以通过SSO实现单点登录进入门户网站,之后门户网站使用OAuth 2.0的授权机制授予用户对不同子系统的访问权限。
  • 微服务架构:在微服务架构中,可以结合SSO和OAuth 2.0。用户通过SSO登录系统后,系统使用OAuth 2.0生成访问令牌,用户携带该令牌在不同的微服务间访问受保护的资源。每个微服务可以验证令牌的有效性,从而保证访问的安全性和便捷性。
  • 跨组织系统:在跨组织系统中,用户可以通过SSO登录主系统,然后通过OAuth 2.0授权机制访问其他组织的系统或资源。例如,一个企业用户可以在企业门户上登录(SSO),然后通过OAuth 2.0授权机制访问其他合作伙伴的API资源。

五、总结

  • SSO主要解决用户认证问题,让用户在多个系统之间可以使用单一的登录状态,实现“登录一次,访问多处”的效果。
  • OAuth 2.0主要解决授权问题,让第三方应用在不获取用户凭据的情况下,可以安全地访问用户的资源。
  • SSO和OAuth 2.0在某些场景下可以结合使用。OAuth 2.0可以充当SSO的实现方式,通过授权服务器生成的令牌可以实现单点登录效果,而OpenID Connect(基于OAuth 2.0的认证协议)就是一个将SSO和OAuth 2.0结合的标准。
  • 在实际应用中,OAuth 2.0适合用于API访问、授权控制,而SSO适合用于统一的用户登录体验。结合SSO和OAuth 2.0可以构建一个完整的认证和授权解决方案,尤其适合分布式系统和微服务架构。

Spring Boot实现SSO

在Spring Boot项目中实现单点登录(SSO)功能,允许第三方系统通过SSO登录你的系统,可以通过OAuth 2.0协议和JWT(JSON Web Token)来实现。SSO的目标是让用户只需在SSO认证中心登录一次,即可访问多个系统而无需重复登录。下面将详细介绍如何实现这种方案。

一、SSO(单点登录)的基本原理

SSO的工作原理是多个系统共享用户的登录状态,通过集中式认证和授权服务器来统一管理登录。具体流程如下:

  1. 用户访问第三方系统:用户访问第三方系统A,但未登录。
  2. 重定向到SSO认证中心:第三方系统将用户重定向到SSO认证中心(授权服务器)。
  3. 用户登录并认证:用户在SSO认证中心登录并完成认证。
  4. 生成令牌并返回:SSO认证中心生成授权码或访问令牌,返回给第三方系统。
  5. 第三方系统验证令牌:第三方系统使用该令牌访问你的系统或资源服务器,以验证用户身份。
  6. 跨系统共享认证状态:当用户再次访问其他系统(如系统B),无需再次登录。

二、技术选型

在Spring Boot中,可以使用以下技术来实现SSO:

  • Spring Security OAuth2:提供OAuth 2.0的授权和认证支持。
  • JWT:使用JWT作为令牌,保存用户的认证信息,使系统能够基于令牌验证用户的身份。
  • Spring Security:用于管理用户的认证和授权。

三、实现步骤

3.1 添加依赖

首先,在pom.xml中添加Spring Security、OAuth2和JWT的依赖。

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

    <!-- OAuth2 依赖 -->
    <dependency>
        <groupId>org.springframework.security.oauth.boot</groupId>
        <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    </dependency>

    <!-- JWT 支持 -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>
</dependencies>
3.2 配置授权服务器(Authorization Server)

授权服务器是整个SSO的核心,它负责用户的认证和生成访问令牌。在Spring Boot中可以通过配置OAuth 2.0来实现。

1. 创建AuthorizationServerConfig

在Spring Boot项目中,创建一个AuthorizationServerConfig类来配置授权服务器。

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
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.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

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

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("third-party-client-id")
                .secret("{noop}third-party-client-secret")
                .authorizedGrantTypes("authorization_code", "refresh_token")
                .scopes("read", "write")
                .redirectUris("http://localhost:8081/callback");  // 回调地址
    }

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

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("mySecretKey");  // 设置JWT的签名密钥
        return converter;
    }
}

此配置实现了OAuth 2.0的授权模式,并且通过JWT生成访问令牌,第三方系统可以使用授权码获取令牌,并使用它来访问受保护的资源。

3.3 配置资源服务器(Resource Server)

资源服务器托管你的受保护资源,并通过验证令牌的合法性来控制资源的访问。

1. 创建ResourceServerConfig
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("/public/**").permitAll()  // 公共资源
            .antMatchers("/private/**").authenticated();  // 受保护资源
    }
}
2. 创建受保护资源的控制器

定义一个受保护资源的控制器,用于测试SSO的访问控制。

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

@RestController
public class ResourceController {

    @GetMapping("/private/data")
    public String getPrivateData() {
        return "This is a protected resource!";
    }

    @GetMapping("/public/data")
    public String getPublicData() {
        return "This is a public resource!";
    }
}
3.4 配置第三方系统(客户端应用)

第三方系统作为客户端应用,需要向授权服务器请求授权,并使用获取到的令牌访问受保护资源。

1. 在application.yml中配置OAuth 2.0客户端信息
spring:
  security:
    oauth2:
      client:
        registration:
          sso-client:
            client-id: third-party-client-id
            client-secret: third-party-client-secret
            scope: read,write
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
        provider:
          authserver:
            authorization-uri: http://localhost:8080/oauth/authorize
            token-uri: http://localhost:8080/oauth/token
            user-info-uri: http://localhost:8080/userinfo
2. 使用Spring Security OAuth2登录

SecurityConfig类中启用OAuth2登录。

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/").permitAll()
                .anyRequest().authenticated()
            .and()
            .oauth2Login();  // 启用OAuth2登录
    }
}

四、SSO工作流程

  1. 第三方系统请求授权码:用户访问第三方系统的受保护资源,系统检测到用户未登录,将用户重定向到SSO认证中心(授权服务器)。

  2. SSO认证中心处理认证请求:用户在SSO认证中心登录并通过认证后,授权服务器生成授权码并重定向回第三方系统的回调地址。

  3. 第三方系统获取访问令牌:第三方系统使用授权码向授权服务器请求访问令牌,授权服务器返回访问令牌给第三方系统。

  4. 访问受保护资源:第三方系统携带访问令牌访问资源服务器,资源服务器验证令牌的合法性后返回受保护资源。

  5. 其他系统共享登录状态:如果用户再次访问其他系统,这些系统可以使用同样的认证流程共享用户的登录状态,无需用户再次登录。

五、常见问题及建议

  1. 令牌的安全性:在生产环境中,建议使用HTTPS加密传输,防止令牌被拦截,JWT签名密钥也应妥善保管。
  2. 令牌的过期与刷新:访问令牌通常设置较短的有效期,可以使用刷新令牌机制延长会话,避免用户频繁登录。
  3. SSO登出与会话管理:在SSO环境下,登出操作需要清除各个系统的会话信息,可以通过分布式Session或集中式会话管理。
  4. 日志和监控:对登录、授权和资源访问等操作进行日志记录,方便后期审计和问题排查。

六、总结

通过以上配置,可以在Spring Boot项目中实现一个OAuth 2.0 SSO的认证授权系统,让第三方系统通过SSO登录你的系统。授权服务器集中管理用户认证,生成和验证JWT令牌,资源服务器通过验证令牌控制资源访问,客户端应用通过OAuth2流程安全地访问受保护资源。这种实现方式不仅提供了SSO功能,还具备了灵活的授权控制,非常适合在分布式架构中使用。

Spring Boot实现OAuth2.0授权

在Spring Boot中实现OAuth 2.0授权,允许第三方系统访问你的系统资源,通常包括搭建授权服务器、资源服务器,并通过OAuth 2.0协议来进行认证和授权。这种实现能够确保第三方系统安全地访问受保护的资源,而无需直接提供用户的凭据。

以下是实现OAuth 2.0授权的详细步骤,用于让第三方系统访问你的系统。

一、OAuth 2.0授权的基本工作原理

OAuth 2.0的授权流程主要包含以下几个步骤:

  1. 授权请求:第三方系统通过OAuth 2.0协议向授权服务器发起授权请求。用户被重定向到授权服务器,并在此进行登录。
  2. 用户同意授权:用户登录并同意授权后,授权服务器生成授权码并返回给第三方系统。
  3. 获取访问令牌:第三方系统使用授权码向授权服务器请求访问令牌(access_token)。
  4. 访问受保护资源:第三方系统使用access_token访问资源服务器上的受保护资源。

二、项目结构

OAuth 2.0授权在Spring Boot项目中通常需要三个主要组件:

  1. 授权服务器(Authorization Server):负责处理用户认证和授权请求,并生成访问令牌。
  2. 资源服务器(Resource Server):托管受保护资源,验证令牌的合法性。
  3. 客户端应用(Client Application):第三方系统,通过OAuth 2.0协议获取访问令牌并使用它来访问资源。

三、实现步骤

3.1 添加依赖

在Spring Boot项目的pom.xml文件中,添加Spring Security和OAuth 2.0相关依赖:

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

    <!-- OAuth2 Authorization Server 和 Resource Server 支持 -->
    <dependency>
        <groupId>org.springframework.security.oauth.boot</groupId>
        <artifactId>spring-security-oauth2-autoconfigure</artifactId>
        <version>2.4.0</version>
    </dependency>

    <!-- JWT 支持 -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>
</dependencies>
3.2 配置授权服务器(Authorization Server)

授权服务器负责生成和管理令牌,并处理用户的授权流程。我们可以使用Spring Security OAuth 2.0来配置授权服务器。

1. 创建AuthorizationServerConfig类

在Spring Boot项目中创建一个AuthorizationServerConfig类,继承AuthorizationServerConfigurerAdapter,配置授权服务器。

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

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

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
            .withClient("client-id")  // 第三方系统的客户端ID
            .secret("{noop}client-secret")  // 客户端密钥
            .authorizedGrantTypes("authorization_code", "refresh_token")  // 支持的授权模式
            .scopes("read", "write")  // 授权范围
            .redirectUris("http://localhost:8081/callback");  // 回调地址
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // 通过JWT生成令牌
        endpoints.tokenStore(new JwtTokenStore(jwtAccessTokenConverter()))
                 .accessTokenConverter(jwtAccessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("mySecretKey"); // 设置签名密钥
        return converter;
    }
}
3.3 配置资源服务器(Resource Server)

资源服务器托管受保护资源,并验证令牌的有效性。资源服务器可以通过JWT验证令牌,确保只有持有有效access_token的请求才能访问资源。

1. 创建ResourceServerConfig类

在Spring Boot项目中创建一个ResourceServerConfig类,继承ResourceServerConfigurerAdapter,配置资源服务器行为。

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("/api/public").permitAll()  // 公共接口
            .antMatchers("/api/private").authenticated();  // 受保护资源
    }
}

2. 受保护资源的控制器

在资源服务器中创建一个控制器,定义受保护的资源。

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

@RestController
public class ResourceController {

    @GetMapping("/api/private")
    public String getPrivateData() {
        return "This is a protected resource!";
    }

    @GetMapping("/api/public")
    public String getPublicData() {
        return "This is a public resource!";
    }
}
3.4 配置客户端应用(Client Application)

客户端应用(即第三方系统)需要通过OAuth 2.0协议获取访问令牌,并使用令牌访问受保护资源。

1. 配置客户端信息

在第三方系统的application.yml中,配置OAuth 2.0客户端信息。

spring:
  security:
    oauth2:
      client:
        registration:
          myclient:
            client-id: client-id
            client-secret: client-secret
            scope: read,write
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
        provider:
          authserver:
            authorization-uri: http://localhost:8080/oauth/authorize
            token-uri: http://localhost:8080/oauth/token
            user-info-uri: http://localhost:8080/userinfo
            jwk-set-uri: http://localhost:8080/oauth/jwks

2. 使用Spring Security OAuth2登录

在客户端应用的SecurityConfig类中启用OAuth2登录,并配置安全策略。

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/").permitAll()
                .anyRequest().authenticated()
            .and()
            .oauth2Login();  // 启用OAuth2登录
    }
}
3.5 授权流程演示
  1. 授权请求:第三方系统(客户端)发起授权请求,用户将被重定向到授权服务器进行登录和授权。典型的授权请求URL如下:

    http://localhost:8080/oauth/authorize?response_type=code&client_id=client-id&redirect_uri=http://localhost:8081/callback
    
  2. 用户登录和授权:用户在授权服务器上登录,并同意授权第三方系统访问其资源。

  3. 获取授权码:授权服务器生成授权码,并通过重定向将其返回给第三方系统的回调地址。

  4. 获取访问令牌:第三方系统使用授权码向授权服务器请求访问令牌。

    POST http://localhost:8080/oauth/token
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=authorization_code
    code=AUTHORIZATION_CODE
    redirect_uri=http://localhost:8081/callback
    client_id=client-id
    client_secret=client-secret
    
  5. 访问受保护资源:第三方系统使用获得的access_token来访问资源服务器上的受保护资源。

    GET /api/private
    Authorization: Bearer access_token
    

资源服务器验证令牌的合法性后,返回受保护的数据。

四、常见问题及建议

  1. 令牌存储与安全性:在生产环境中,建议使用JWT作为令牌存储,并通过HTTPS加密通信,防止令牌泄露。
  2. 令牌过期管理:可以通过刷新令牌(refresh_token)机制来延长会话,避免用户频繁登录。
  3. 客户端凭证管理:客户端密钥(client-secret)需要妥善保护,不要泄露给未授权的用户或系统。
  4. 授权范围的控制:OAuth 2.0的授权范围可以控制第三方应用的访问权限,确保第三方只能访问必要的数据。

五、总结

通过以上步骤,可以在Spring Boot中实现OAuth 2.0授权机制,允许第三方系统在获得用户授权后,安全地访问受保护的资源。授权服务器负责用户的认证和令牌生成,资源服务器负责资源的保护,客户端应用使用令牌访问资源。通过这种方式,可以实现一个标准的OAuth 2.0授权流程,确保数据的安全和用户的访问控制。


原文地址:https://blog.csdn.net/print_helloword/article/details/143512519

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!