使用 Spring Security 實現 OAuth2:一步一步的操作指南

前言

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. 參考資料..…