使用Spring Security连接Google的OpenID进行身份验证
Spring Security5中包含了一个简单地使用OpenID Connect的功能。(在Spring文件中称为OAuth2.0登录。)
尤其是与Google、GitHub、Facebook和Okta的集成,只需进行简单的设定即可运行。本次我们尝试使用Google进行操作,以下是相关内容。
如果您对OpenID Connect一无所知的话,以下文章将会对您非常有帮助:
https://qiita.com/TakahikoKawasaki/items/498ca08bbfcc341691fe
谷歌方面的准备
按照以下页面的指示,进行Google OpenID Connect的设置:
https://developers.google.com/identity/protocols/OpenIDConnect
简单解释一下,大致感觉如下。
-
- 前往Google API控制台。
-
- 從「認證資訊」選擇「建立認證資訊」→選擇「OAuth用戶端ID」。
-
- 應用程式類型和名稱可以隨意填寫。
- 在已授權的重定向URI中添加http://localhost:8080/login/oauth2/code/google。
请根据您自己创建应用程序的环境来调整最后的重定向URI的协议、主机名和端口号。由于”/login/oauth2/code/google”的部分是Spring Security的默认设置,如果要更改默认行为,则还需要更改此部分。
应用程序的设置
本次我们将使用Spring Boot来创建应用程序。
pom.xml – 請提供POM檔案
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>oidc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>oidc</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
除了Spring Security之外,还需要spring-security-oauth2-client和spring-security-oauth2-jose。由于Spring Initializr无法添加它们,因此需要手动修改pom.xml文件。此外,由于本次使用Thymeleaf来进行页面显示,因此需要添加Thymeleaf和用于Spring Security的Thymeleaf扩展的依赖关系。
应用程序.yml
这次我们将使用yml进行设置,但也可以使用properties进行相同的设置。
spring:
security:
oauth2:
client:
registration:
google:
client-id: [クライアントID]
client-secret: [クライアントシークレット]
请复制并粘贴Google创建的认证信息中的客户端ID和客户端密钥。
应用程序类
暂时使用默认设置没有问题。OpenID Connect相关的设置也会自动配置。
@SpringBootApplication
public class OidcApplication {
public static void main(String[] args) {
SpringApplication.run(OidcApplication.class, args);
}
}
控制器
为了确认动作,我们将创建一个Controller类。(也可以在主类内创建。)
@Controller
public class HelloController {
@GetMapping("/")
public String index(@AuthenticationPrincipal OidcUser user, Model model) {
model.addAttribute("username", user.getFullName());
return "hello";
}
认证信息存储在OidcUser对象中,可以使用getName()方法获取唯一ID。(不太喜欢叫Name。)
此外,还可以获取头像链接和电子邮件地址。
虽然OidcUser类提供了许多方法来获取生日等各种数据,但Google并不会返回所有相关数据,因此有一些方法是无法使用的(会返回null)。
百里香叶
我們將建立一個顯示認證資訊的畫面。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<title>OpenID Connect Sample</title>
<meta charset="utf-8"/>
</head>
<body>
<div>名前:<span sec:authentication="principal.attributes['name']"></span></div>
<div>名前:<span th:text="${username}"></span></div>
<div>ID:<span sec:authentication="principal.name"></span></div>
</body>
</html>
顺便提一句,当使用sec:authentication=”principal.fullName”时会出错。
因为在控制器中使用了getFullName方法,所以认为可以访问,但事实却不是这样。
尝试使用其他的get…方法时,有些会引发错误,有些则不会。
getFullName等已经在IdTokenClaimAccessor接口中默认实现了,但是只有默认实现的方法似乎无法调用。
我花了一些时间追踪源代码,发现BeanWrapperImpl利用Introspector收集BeanInfo并进行访问。
然后,根据下面的链接,似乎Introspector不支持接口的默认实现。
https://bugs.openjdk.java.net/browse/JDK-8071693
确认行动
只需要按照以上的方式,就可以非常容易地实现OpenID Connect!
我認為管理使用者的密碼是一件非常麻煩且令人擔憂的事情,因此很多人可能希望使用OpenID Connect。由於Spring Security可以處理大部分繁瑣的工作,因此導入Spring Security的門檻也會降低。