前言
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: