前言

OAuth 是一种授权框架,用于创建权限策略,并允许应用程序对用户在 HTTP 服务(如 GitHub 和 Google)上的账户进行有限访问。它的工作原理是允许用户授权第三方应用访问他们的数据,而无需分享他们的凭证。本文将指导你如何在Spring Boot应用中使用 Spring Security 实现 OAuth2,并通过 OAuth2 提供商启用安全的登录和用户数据访问。

简而言之,这篇文章的内容是讲解如何在 Spring Boot 项目中集成 OAuth2,使得用户可以通过 OAuth2 提供商(比如 Google 或 github)进行安全登录,而不需要直接暴露密码等敏感信息。

什么是OAuth2?

OAuth2(开放授权 2.0)是一个授权框架,允许应用程序在不暴露用户凭证的情况下,获得对 GitHub 或 Google 等 HTTP 服务上用户账户的有限访问权限。OAuth2 为用户提供了一种方法,使他们能够在不与第三方应用共享密码的情况下访问自己的资源。

简单来说,OAuth2 允许用户授权第三方应用访问他们在其他平台上的数据,但同时避免了密码泄露的风险,确保了用户的账户安全。

关键组件

  • Resource Owner (资源所有者):是授权应用访问其账户的用户。
  • Client (客户端):是请求访问用户账户的应用程序。
  • Authorization Server (授权服务器):是验证用户身份并向客户端颁发访问令牌的服务器。
  • Resource Server (资源服务器):是托管受保护资源并接受访问令牌以允许应用访问这些资源的服务器。

OAuth2 授权流程

OAuth2 定义了几种授权流程,以适应不同的使用场景:

1、Authorization Code Grant (授权码授权):

  • 适用于服务器端应用程序。此流程涉及应用程序用授权码交换访问令牌。

  • 这是最常见的 OAuth2 流程,安全性较高,适用于需要安全存储客户端密钥的应用程序。

2、Implicit Grant (简化授权):

  • 适用于客户端应用程序(例如浏览器中的单页应用)。在这种流程中,访问令牌会直接返回,而不需要进行授权码交换。
  • 该流程较为简单,但安全性较低,因为令牌直接暴露给客户端。

3、Resource Owner Password Credentials Grant (资源所有者密码凭证授权):

  • 适用于信任客户端的应用程序,在这种情况下,客户端可以直接请求资源所有者的凭证(用户名和密码)。
  • 该流程不推荐用于不受信任的应用,因为它需要用户直接向客户端提供凭证。

4、Client Credentials Grant (客户端凭证授权):

  • 适用于客户端需要访问自己的资源,而不是资源所有者的资源的情况。
  • 该流程不涉及用户交互,通常用于机器对机器的授权场景,例如后台服务的认证。

先决条件

  • 1、对 Spring Boot 和 Spring Security 有良好的了解;
  • 2、在本地系统中安装 JDK 和 IntelliJ IDEA
  • 3、Google Console 帐户作为 OAuth2 提供者 或者github
  • 4、使用 Maven 进行依赖管理和构建

实现 OAuth2 与 Spring Security 集成

步骤1:创建一个新的Spring Boot项目

使用IntelliJ Idea创建一个新的Spring Boot项目,在创建项目时,选择以下选项:

  • Project Name: oauth2-spring-security
  • Language: Java
  • Type: Maven
  • Packaging: Jar
    在这里插入图片描述

步骤2:添加依赖项

将以下依赖项添加到Spring项目中。
在这里插入图片描述
创建项目后,IDE中的文件夹结构如下图所示:
在这里插入图片描述

步骤3:配置应用程序属性

打开应用程序。属性文件,并将谷歌OAuth配置代码添加到项目中。

spring.application.name=oauth2-spring-security
spring.security.oauth2.client.registration.google.client-id=xxxxxxxxxxxx-hjjujqg0rsjlocde5esmjelr7p6rl5a1.apps.googleusercontent.com
spring.security.oauth2.client.registration.google.client-secret=xxxxxxx-x-_hDO3tFTOsCatT2P7CZQDUKb4l
spring.security.oauth2.client.registration.google.redirect-uri=http://localhost:8080/login/oauth2/code/{registrationId}
spring.security.oauth2.client.registration.google.scope=profile, emailspring.security.oauth2.client.registration.github.client-id=xxxxxxxxBsTQfkTtULzE
spring.security.oauth2.client.registration.github.client-secret=xxxxxxxxxxx816d1d112f411f7f5df3da721a322c

步骤4:创建用户类

package org.example.model;import lombok.Data;@Data
public class User {private String name;private String email;// Getters and Setterspublic String getName() {return name;}public void setName(String name) {this.name = name;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}
}

这个类用name和email属性定义User模型。它使用Lombok注释来简化样板代码。

步骤5:创建UserService类

这个服务类负责从OAuth2User数据创建User对象。

package org.example.service;import org.example.model.User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;@Service
public class UserService {public User createUser(OAuth2User oAuth2User) {User user = new User();// Set user attributes from OAuth2Useruser.setName(oAuth2User.getAttribute("name"));user.setEmail(oAuth2User.getAttribute("email"));return user;}
}

步骤6:创建SecurityConfig类

package org.example.config;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.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;/*** @author Administrator*/
@Configuration
@EnableWebSecurity
public class SecurityConfig {@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http// Authorize requests.authorizeHttpRequests(authorizeRequests ->authorizeRequests.requestMatchers("/", "/login").permitAll().anyRequest().authenticated())// Configure OAuth2 login.oauth2Login(oauth2Login ->oauth2Login.loginPage("/login").defaultSuccessUrl("/home", true));return http.build();}
}

步骤8:主类

package org.example;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class Oauth2SpringSecurityApplication {public static void main(String[] args) {SpringApplication.run(Oauth2SpringSecurityApplication.class, args);}}

步骤9:创建登录HTML文件

<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org/">
<head><title>Login</title><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"><style>body {background-color: #f8f9fa;}.navbar {background-color: #81c784; /* Light Green */}.navbar-brand {color: white !important;}.container {margin-top: 100px;}.card {border: none;border-radius: 10px;box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);}.card-header {background-color: #81c784; /* Light Green */color: white;border-top-left-radius: 10px;border-top-right-radius: 10px;}.btn-custom {background-color: #81c784; /* Light Green */color: white;border-radius: 5px;}.btn-custom:hover {background-color: #66bb6a; /* Darker Green */color: white;}</style>
</head>
<body>
<nav class="navbar navbar-expand-lg"><a class="navbar-brand" href="#">My App</a>
</nav>
<div class="container"><div class="row justify-content-center"><div class="col-md-6"><div class="card"><div class="card-header text-center">Login with OAuth2</div><div class="card-body text-center"><p class="card-text">Please login using one of the following options:</p><a class="btn btn-custom" href="/oauth2/authorization/google">Login with Google</a></div></div></div><div class="col-md-6"><div class="card"><div class="card-header text-center">Login with OAuth2</div><div class="card-body text-center"><p class="card-text">Please login using one of the following options:</p><a class="btn btn-custom" href="/oauth2/authorization/github">Login with github</a></div></div></div></div>
</div>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>

步骤10:创建主HTML文件

<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org/">
<head><title>Home</title><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"><style>body {background-color: #f8f9fa;}.navbar {background-color: #81c784; /* Light Green */}.navbar-brand, .nav-link {color: white !important;}.container {margin-top: 50px;}.card {border: none;border-radius: 10px;box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);}.card-header {background-color: #81c784; /* Light Green */color: white;border-top-left-radius: 10px;border-top-right-radius: 10px;}</style>
</head>
<body>
<nav class="navbar navbar-expand-lg"><a class="navbar-brand" href="#">My App</a><div class="collapse navbar-collapse"><ul class="navbar-nav ml-auto"><li class="nav-item"><a class="nav-link" href="/logout">Logout</a></li></ul></div>
</nav>
<div class="container"><div class="row justify-content-center"><div class="col-md-8"><div class="card"><div class="card-header">Welcome, <span th:text="${name}"></span>!</div><div class="card-body"><p class="card-text">You are now logged in using OAuth2.</p></div></div></div></div>
</div>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.5.5</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>org.example</groupId><artifactId>oauth2-spring-security</artifactId><version>0.0.1-SNAPSHOT</version><name>oauth2-spring-security</name><description>oauth2-spring-security</description><url/><licenses><license/></licenses><developers><developer/></developers><scm><connection/><developerConnection/><tag/><url/></scm><properties><java.version>17</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-oauth2-client</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-springsecurity6</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><annotationProcessorPaths><path><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></path></annotationProcessorPaths></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>

步骤11:运行应用程序

D:\Java\corretto-17.0.5\bin\java.exe -XX:TieredStopAtLevel=1 -Dspring.output.ansi.enabled=always -Dcom.sun.management.jmxremote -Dspring.jmx.enabled=true -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true "-Dmanagement.endpoints.jmx.exposure.include=*" "-javaagent:D:\JetBrains\IntelliJ IDEA 2024.1.4\lib\idea_rt.jar=4012:D:\JetBrains\IntelliJ IDEA 2024.1.4\bin" -Dfile.encoding=UTF-8 -classpath D:\02\oauth2-spring-security\target\classes;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter-oauth2-client\3.5.5\spring-boot-starter-oauth2-client-3.5.5.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter\3.5.5\spring-boot-starter-3.5.5.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter-logging\3.5.5\spring-boot-starter-logging-3.5.5.jar;C:\Users\Administrator\.m2\repository\ch\qos\logback\logback-classic\1.5.18\logback-classic-1.5.18.jar;C:\Users\Administrator\.m2\repository\ch\qos\logback\logback-core\1.5.18\logback-core-1.5.18.jar;C:\Users\Administrator\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.24.3\log4j-to-slf4j-2.24.3.jar;C:\Users\Administrator\.m2\repository\org\apache\logging\log4j\log4j-api\2.24.3\log4j-api-2.24.3.jar;C:\Users\Administrator\.m2\repository\org\slf4j\jul-to-slf4j\2.0.17\jul-to-slf4j-2.0.17.jar;C:\Users\Administrator\.m2\repository\jakarta\annotation\jakarta.annotation-api\2.1.1\jakarta.annotation-api-2.1.1.jar;C:\Users\Administrator\.m2\repository\org\yaml\snakeyaml\2.4\snakeyaml-2.4.jar;C:\Users\Administrator\.m2\repository\org\springframework\security\spring-security-config\6.5.3\spring-security-config-6.5.3.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-beans\6.2.10\spring-beans-6.2.10.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-context\6.2.10\spring-context-6.2.10.jar;C:\Users\Administrator\.m2\repository\org\springframework\security\spring-security-core\6.5.3\spring-security-core-6.5.3.jar;C:\Users\Administrator\.m2\repository\org\springframework\security\spring-security-crypto\6.5.3\spring-security-crypto-6.5.3.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-expression\6.2.10\spring-expression-6.2.10.jar;C:\Users\Administrator\.m2\repository\io\micrometer\micrometer-observation\1.15.3\micrometer-observation-1.15.3.jar;C:\Users\Administrator\.m2\repository\io\micrometer\micrometer-commons\1.15.3\micrometer-commons-1.15.3.jar;C:\Users\Administrator\.m2\repository\org\springframework\security\spring-security-oauth2-client\6.5.3\spring-security-oauth2-client-6.5.3.jar;C:\Users\Administrator\.m2\repository\org\springframework\security\spring-security-oauth2-core\6.5.3\spring-security-oauth2-core-6.5.3.jar;C:\Users\Administrator\.m2\repository\com\nimbusds\oauth2-oidc-sdk\9.43.6\oauth2-oidc-sdk-9.43.6.jar;C:\Users\Administrator\.m2\repository\com\github\stephenc\jcip\jcip-annotations\1.0-1\jcip-annotations-1.0-1.jar;C:\Users\Administrator\.m2\repository\com\nimbusds\content-type\2.2\content-type-2.2.jar;C:\Users\Administrator\.m2\repository\com\nimbusds\lang-tag\1.7\lang-tag-1.7.jar;C:\Users\Administrator\.m2\repository\org\springframework\security\spring-security-oauth2-jose\6.5.3\spring-security-oauth2-jose-6.5.3.jar;C:\Users\Administrator\.m2\repository\com\nimbusds\nimbus-jose-jwt\9.37.3\nimbus-jose-jwt-9.37.3.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter-security\3.5.5\spring-boot-starter-security-3.5.5.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-aop\6.2.10\spring-aop-6.2.10.jar;C:\Users\Administrator\.m2\repository\org\springframework\security\spring-security-web\6.5.3\spring-security-web-6.5.3.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter-thymeleaf\3.5.5\spring-boot-starter-thymeleaf-3.5.5.jar;C:\Users\Administrator\.m2\repository\org\thymeleaf\thymeleaf-spring6\3.1.3.RELEASE\thymeleaf-spring6-3.1.3.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\thymeleaf\thymeleaf\3.1.3.RELEASE\thymeleaf-3.1.3.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\attoparser\attoparser\2.0.7.RELEASE\attoparser-2.0.7.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\unbescape\unbescape\1.1.6.RELEASE\unbescape-1.1.6.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter-web\3.5.5\spring-boot-starter-web-3.5.5.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter-json\3.5.5\spring-boot-starter-json-3.5.5.jar;C:\Users\Administrator\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.19.2\jackson-databind-2.19.2.jar;C:\Users\Administrator\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.19.2\jackson-annotations-2.19.2.jar;C:\Users\Administrator\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.19.2\jackson-core-2.19.2.jar;C:\Users\Administrator\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.19.2\jackson-datatype-jdk8-2.19.2.jar;C:\Users\Administrator\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.19.2\jackson-datatype-jsr310-2.19.2.jar;C:\Users\Administrator\.m2\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.19.2\jackson-module-parameter-names-2.19.2.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter-tomcat\3.5.5\spring-boot-starter-tomcat-3.5.5.jar;C:\Users\Administrator\.m2\repository\org\apache\tomcat\embed\tomcat-embed-core\10.1.44\tomcat-embed-core-10.1.44.jar;C:\Users\Administrator\.m2\repository\org\apache\tomcat\embed\tomcat-embed-el\10.1.44\tomcat-embed-el-10.1.44.jar;C:\Users\Administrator\.m2\repository\org\apache\tomcat\embed\tomcat-embed-websocket\10.1.44\tomcat-embed-websocket-10.1.44.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-web\6.2.10\spring-web-6.2.10.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-webmvc\6.2.10\spring-webmvc-6.2.10.jar;C:\Users\Administrator\.m2\repository\org\thymeleaf\extras\thymeleaf-extras-springsecurity6\3.1.3.RELEASE\thymeleaf-extras-springsecurity6-3.1.3.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\slf4j\slf4j-api\2.0.17\slf4j-api-2.0.17.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-devtools\3.5.5\spring-boot-devtools-3.5.5.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot\3.5.5\spring-boot-3.5.5.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\3.5.5\spring-boot-autoconfigure-3.5.5.jar;C:\Users\Administrator\.m2\repository\org\projectlombok\lombok\1.18.38\lombok-1.18.38.jar;C:\Users\Administrator\.m2\repository\net\minidev\json-smart\2.5.2\json-smart-2.5.2.jar;C:\Users\Administrator\.m2\repository\net\minidev\accessors-smart\2.5.2\accessors-smart-2.5.2.jar;C:\Users\Administrator\.m2\repository\org\ow2\asm\asm\9.7.1\asm-9.7.1.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-core\6.2.10\spring-core-6.2.10.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-jcl\6.2.10\spring-jcl-6.2.10.jar org.example.Oauth2SpringSecurityApplication.   ____          _            __ _ _/\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/  ___)| |_)| | | | | || (_| |  ) ) ) )'  |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot ::                (v3.5.5)2025-09-07T21:35:35.024+08:00  INFO 3272 --- [oauth2-spring-security] [  restartedMain] o.e.Oauth2SpringSecurityApplication      : Starting Oauth2SpringSecurityApplication using Java 17.0.5 with PID 3272 (D:\02\oauth2-spring-security\target\classes started by Administrator in D:\02\oauth2-spring-security)
2025-09-07T21:35:35.036+08:00  INFO 3272 --- [oauth2-spring-security] [  restartedMain] o.e.Oauth2SpringSecurityApplication      : No active profile set, falling back to 1 default profile: "default"
2025-09-07T21:35:35.200+08:00  INFO 3272 --- [oauth2-spring-security] [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2025-09-07T21:35:35.200+08:00  INFO 3272 --- [oauth2-spring-security] [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
2025-09-07T21:35:37.926+08:00  INFO 3272 --- [oauth2-spring-security] [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2025-09-07T21:35:37.963+08:00  INFO 3272 --- [oauth2-spring-security] [  restartedMain] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2025-09-07T21:35:37.963+08:00  INFO 3272 --- [oauth2-spring-security] [  restartedMain] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.44]
2025-09-07T21:35:38.074+08:00  INFO 3272 --- [oauth2-spring-security] [  restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2025-09-07T21:35:38.074+08:00  INFO 3272 --- [oauth2-spring-security] [  restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2872 ms
2025-09-07T21:35:39.463+08:00  INFO 3272 --- [oauth2-spring-security] [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2025-09-07T21:35:39.540+08:00  INFO 3272 --- [oauth2-spring-security] [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path '/'
2025-09-07T21:35:39.562+08:00  INFO 3272 --- [oauth2-spring-security] [  restartedMain] o.e.Oauth2SpringSecurityApplication      : Started Oauth2SpringSecurityApplication in 5.87 seconds (process running for 9.315)

步骤12:测试应用程序

登录页http://localhost:8080/login
在这里插入图片描述
Google OAuth Authentication:
在这里插入图片描述
在这里插入图片描述
首页 http://localhost:8080/home
在这里插入图片描述
github OAuth Authentication:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/98291.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/98291.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/98291.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VMware共享文件夹设置

启用共享文件夹 编辑虚拟机设置-选项-共享文件夹&#xff0c;上面的选项选择启用下面点击添加一个路径&#xff0c;跟着向导走 设置共享文件夹在主机的路径&#xff0c;和文件夹名称添加完成后可以点击这个共享文件夹条目&#xff0c;查看属性虚拟机里安装vm-tools sudo apt up…

华为云昇腾云服务

华为云&#xff0c;一切皆服务共建智能世界云底座面向未来的智能世界&#xff0c;数字化是企业发展的必由之路。数字化成功的关键是以云原生的思维践行云原生&#xff0c;全数字化、全云化、AI驱动&#xff0c;一切皆服务。华为云将持续创新&#xff0c;携手客户、合作伙伴和开…

Axum 最佳实践:如何构建优雅的 Rust 错误处理系统?(三)

引言 作为开发者&#xff0c;我们都经历过这样的场景&#xff1a;项目上线后&#xff0c;你打开日志监控&#xff0c;铺天盖地的 500 Internal Server Error 扑面而来。这些错误像个黑洞&#xff0c;吞噬着你的调试时间&#xff0c;你甚至不知道它们是从数据库查询失败&#x…

MySQL高可用方案解析:从复制到云原生

MySQL 的高可用 (High Availability, HA) 方案旨在确保数据库服务在硬件故障、软件崩溃、网络中断或计划维护时仍能持续可用&#xff0c;最小化停机时间&#xff08;通常目标为 99.9% 至 99.999% 可用性&#xff09;。以下是 MySQL 领域成熟且广泛应用的几种主流高可用方案&…

腾讯云语音接口实现会议系统

1.前言 在现代企业协作环境中&#xff0c;高效的会议管理是提升团队生产力的关键。本文将深入解析一个完整的会议管理系统&#xff0c;涵盖从会议创建到总结生成的完整生命周期。该系统构建一个基于AI技术的智能会议系统&#xff0c;实现会议全流程的智能化管理&#xff0c;包括…

【LeetCode 每日一题】1277. 统计全为 1 的正方形子矩阵

Problem: 1277. 统计全为 1 的正方形子矩阵 文章目录整体思路完整代码时空复杂度时间复杂度&#xff1a;O(m * n)空间复杂度&#xff1a;O(m * n)整体思路 这段代码旨在解决一个经典的二维矩阵问题&#xff1a;统计全为 1 的正方形子矩阵个数 (Count Square Submatrices with …

【论文阅读】MedResearcher-R1: 基于知识引导轨迹合成框架的专家级医学深度研究员

论文链接&#xff1a;https://arxiv.org/pdf/2508.14880 【导读】当通用大模型还在“背题库”时&#xff0c;蚂蚁集团联合哈工大推出的 MedResearcher-R1 已把“临床查房”搬进训练场&#xff01;这篇 2025 年 9 月发布的论文&#xff0c;首次让开源 32B 模型在医学深度研究基准…

基于大语言模型的事件响应优化方案探索

程序员的技术管理推荐阅读 当愿望遇上能力鸿沟&#xff1a;一位技术管理者眼中的团队激励思考 从“激励”到“保健”&#xff1a;80后与90后程序员&#xff0c;到底想要什么&#xff1f; 从“激励”到“保健”&#xff1a;80后与90后程序员&#xff0c;到底想要什么&#xff1f…

数字化浪潮下,传统加工厂如何智能化转型?

在制造业向高端化、服务化升级的今天&#xff0c;传统加工厂正面临前所未有的挑战。订单碎片化、人力成本攀升、设备OEE&#xff08;综合效率&#xff09;长期低于50%、质量波动难以追溯……这些痛点不仅压缩着企业利润空间&#xff0c;更让其在应对市场需求变化时显得迟缓。当…

谓语动词选择指南

文章目录谓语动词的重要性谓语动词类别一. 助动词1. be&#xff08;am, is, are, was, were, been, being&#xff09;表示 存在、状态、身份、特征。2. have&#xff08;have, has, had&#xff09;表示 拥有、经历 或 完成时态的助动词。3. do&#xff08;do, does, did&…

代码随想录学习摘抄day7(二叉树11-21)

一个朴实无华的目录题型226.翻转二叉树思路&#xff1a;把每一个节点的左右孩子交换一下101. 对称二叉树思路&#xff1a;使用队列来比较两个树&#xff08;根节点的左右子树&#xff09;是否相互翻转222.完全二叉树的节点个数思路&#xff1a;本题直接就是求有多少个节点&…

Python+DRVT 从外部调用 Revit:批量创建楼板

今天继续批量创建常用的基础元素&#xff1a;楼板。这次以简单的轮廓为矩形的楼板为例。让我们来看一看如何让Revit自动干活&#xff1a; from typing import List import math # drvt_pybind 支持多会话、多文档&#xff0c;先从简单的单会话、单文档开始 # MyContext是在Pyt…

猿辅导数据分析面试题及参考答案

给定用户成绩表,编写SQL查询排名靠前的用户(例如前10名),并说明rank()和dense_rank()的区别。 要查询成绩表中排名靠前的用户(如前10名),需先明确排名依据(通常为成绩降序),再通过排序和限制结果行数实现。假设用户成绩表名为user_scores,包含user_id(用户ID)和s…

在树莓派集群上部署 Distributed Llama (Qwen 3 14B) 详细指南

项目地址&#xff1a;https://github.com/b4rtaz/distributed-llama 本文档将指导您如何使用一个树莓派5作为Root节点和三个树莓派4作为Worker节点&#xff0c;共同搭建一个4节点的分布式LLM推理集群&#xff0c;并运行10.9GB的Qwen 3 14B模型。 中间要用到github和huggingface…

C++ 容器——unordered_xxx

自 C11 开始&#xff0c;STL 引入了基于 hash table 的 unordered_set、unordered_map 等容器&#xff0c;正如其名它们是无序容器。一定数量&#xff08;据说有测试数据是10000000&#xff09;元素时无序容器的性能要比对应的有序容器优。一、容器数据结构unordered_set、unor…

分布式常见面试题整理

一、分布式理论&#xff1a; CAP理论 分布式系统最多同时满足一致性&#xff08;C&#xff09;、可用性&#xff08;A&#xff09;、分区容错性&#xff08;P&#xff09;中的两个&#xff0c;无法三者兼得。 BASE理论 对CAP中一致性和可用性的权衡&#xff0c;强调基本可用&a…

Python基础入门常用198英语单词详解

最近&#xff0c;我总结了一份Python学习者入门常用单词表&#xff0c;列出了Python学习中常见的198个高频单词&#xff0c;供初学者学习使用。 这些单词都比较简单&#xff0c;非常易于理解&#xff0c;在掌握好单词的基础上&#xff0c;再去学Python可以达到事半功倍的效果。…

EP-SPY 網路追蹤規避實驗:山脈通聯測試

EP-SPY V3.0 https://github.com/MartinxMax/ep-spy 基於 GI6E 編碼的無線電通信工具&#xff0c;用於保護您的隱私。 https://github.com/MartinxMax/gi6e 編寫了偽協議以防止內容被解密無法通過網絡追蹤&#xff0c;抵抗官方監控無線音頻廣播&#xff0c;用於隱蔽信息傳輸…

苹果 FoundationModels 秘典侠客行:隐私为先的端侧 AI 江湖

引子 话说侠客岛之上&#xff0c;有一对年轻侠侣 ——「青锋剑客」凌云与「素心仙子」苏凝&#xff0c;二人自幼习武&#xff0c;尤擅拆解各路奇功秘籍。 近日听闻苹果谷&#xff08;Apple&#xff09;于 WWDC 2025 武林大会之上&#xff0c;亮出一门全新绝学「FoundationMod…

华为基于IPD的产品质量计划模板

目录 模板:产品质量计划模板....................................... 1 1. 介绍...................................................................... 5 1.1. 范围和目的.................................................... 5 1.2. 参考资料..…