返回到文章

采纳

编辑于 25天前

SpringBoot模拟Gitee OAuth2授权服务器

OAuth 2.0

自己实现一个 OAuth2 授权服务器(充当 Gitee 的角色),以下是 Spring Boot 服务端的核心配置。

下面是一个最小可运行的核心示例,省略掉界面和数据库部分,只演示服务端的基本配置。

官方推荐用 Spring Authorization Server 来搭建 OAuth2 授权中心。

1. 引入依赖(pom.xml)

<dependencies>
    <!-- Spring 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 Authorization Server -->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-oauth2-authorization-server</artifactId>
        <version>1.3.1</version> <!-- 对应 Spring Boot 3.x -->
    </dependency>
</dependencies>

2. 安全配置(Authorization Server)

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;

@Configuration
public class AuthorizationServerConfig {

    @Bean
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        // 应用 Spring Authorization Server 的默认配置
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);

        // 未登录时跳转到登录页
        http.exceptionHandling(e -> e
                .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
        );

        return http.build();
    }
}

3. 客户端注册(相当于 Gitee 的应用配置)

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
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.client.InMemoryRegisteredClientRepository;

import java.util.UUID;

@Configuration
public class ClientConfig {

    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();

        RegisteredClient client = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("demo-client") // 模拟 gitee 给出的 client-id
                .clientSecret(encoder.encode("demo-secret")) // 模拟 gitee 给出的 client-secret
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .redirectUri("http://localhost:8080/login/oauth2/code/gitee") // 回调地址
                .scope("user_info")
                .build();

        return new InMemoryRegisteredClientRepository(client);
    }
}

4. 用户认证(模拟 Gitee 登录)

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class DefaultSecurityConfig {

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user = User.withUsername("testuser")
                .password("{noop}123456") // {noop} 表示明文密码
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(user);
    }

    @Bean
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
                .formLogin();

        return http.build();
    }
}

5. 服务端加一个 /userinfo 接口

import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
public class UserInfoController {

    @GetMapping("/userinfo")
    public Map<String, Object> userInfo(@AuthenticationPrincipal Jwt jwt) {
        return Map.of(
                "sub", jwt.getSubject(),
                "username", jwt.getClaimAsString("sub"),
                "email", jwt.getClaimAsString("email"),
                "scope", jwt.getClaimAsString("scope")
        );
    }
}

6. 暴露 OAuth2 元信息(发现文档)

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;

@Configuration
public class ServerSettingsConfig {

    @Bean
    public AuthorizationServerSettings authorizationServerSettings() {
        return AuthorizationServerSettings.builder()
                .issuer("http://localhost:9000") // 服务端地址
                .build();
    }
}

7. 启动后可用的端点

启动后,Spring Authorization Server 会自动暴露标准 OAuth2 端点,比如:

  • 授权端点:http://localhost:9000/oauth2/authorize
  • 令牌端点:http://localhost:9000/oauth2/token
  • JWK 公钥端点:http://localhost:9000/oauth2/jwks
  • OIDC 配置:http://localhost:9000/.well-known/openid-configuration

客户端(比如上篇文章的 gitee 客户端配置)就可以指向这些地址,完成整个闭环。

这个服务端就相当于自己搭建了一个“迷你版 Gitee”,可以作为 OAuth2 授权中心 使用。