返回到文章

采纳

编辑于 24天前

Springboot客户端和服务端Auth2.0完整Demo

OAuth 2.0

OAuth 2 最小可运行的完整 Demo,实现:

  • 服务端:Spring Authorization Server(充当 “Gitee”),支持 OAuth2 授权码模式,带 /userinfo 接口
  • 客户端:Spring Boot 应用,接入 OAuth2 登录,走服务端的 /authorize/token/userinfo

一、服务端(Authorization Server)

1. pom.xml

<dependencies>
    <!-- Web & Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <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>
    </dependency>
</dependencies>

2. 配置类

@Configuration
public class AuthorizationServerConfig {

    @Bean
    public SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        http.exceptionHandling(e -> e.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")));
        return http.build();
    }

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

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

        RegisteredClient client = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("demo-client")
                .clientSecret(encoder.encode("demo-secret"))
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
                .redirectUri("http://localhost:8080/login/oauth2/code/gitee")
                .scope("user_info")
                .build();

        return new InMemoryRegisteredClientRepository(client);
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user = User.withUsername("testuser")
                .password("{noop}123456")
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(user);
    }

    @Bean
    public AuthorizationServerSettings authServerSettings() {
        return AuthorizationServerSettings.builder()
                .issuer("http://localhost:9000")
                .build();
    }
}

3. /userinfo 接口

@RestController
public class UserInfoController {

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

启动服务端,端口 9000

二、客户端(OAuth2 Client)

1. pom.xml

<dependencies>
    <!-- Web & Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

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

    <!-- Thymeleaf (可选,用于自定义登录页) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
</dependencies>

2. application.yml

server:
  port: 8080

spring:
  security:
    oauth2:
      client:
        registration:
          gitee: # 这里的名字随便,但要和 redirectUri 的 {registrationId} 一致
            client-id: demo-client
            client-secret: demo-secret
            client-authentication-method: client_secret_post
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
            scope: user_info
        provider:
          gitee:
            authorization-uri: http://localhost:9000/oauth2/authorize
            token-uri: http://localhost:9000/oauth2/token
            user-info-uri: http://localhost:9000/userinfo
            user-name-attribute: username

3. 安全配置

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain clientSecurityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(auth -> auth
                    .requestMatchers("/", "/login").permitAll()
                    .anyRequest().authenticated()
            )
            .oauth2Login(oauth2 -> oauth2.loginPage("/login"));
        return http.build();
    }
}

4. 控制器

@Controller
public class HomeController {

    @GetMapping("/")
    @ResponseBody
    public String home(@AuthenticationPrincipal OAuth2User user) {
        if (user != null) {
            return "Hello, " + user.getAttribute("username") +
                   " (scope=" + user.getAttribute("scope") + ")";
        }
        return "未登录,<a href='/oauth2/authorization/gitee'>点此登录</a>";
    }

    @GetMapping("/login")
    public String login() {
        return "login"; // 自定义页面,也可以省略直接用默认登录
    }
}

三、运行流程

  1. 启动 服务端 (端口 9000)

    • 提供授权端点 /oauth2/authorize、令牌端点 /oauth2/token、用户信息端点 /userinfo
    • 登录账号:testuser/123456
  2. 启动 客户端 (端口 8080)

    • 访问 http://localhost:8080/ → 点击登录
    • 跳转到服务端的 /login 登录页
    • 登录后 → 授权 → 回调客户端
    • 客户端调用 /userinfo 拿到 profile
  3. 页面显示:

    Hello, testuser (scope=user_info)
    

这样就是一个 完整的 OAuth2 授权中心 + 客户端闭环,逻辑和 Gitee 几乎一致。