尝试通过Spring Security + OAuth2实现单点登录(SSO)功能
今天我将通过制作示例来解释一下单点登录(SSO)的概念。
SSO是什么意思?

SS0功能实现
本文将创建一个示例项目来解释。主要利用Spring Security Oauth2来实现单点登录功能。
环境准备完成
如果你已经具备Spring Security、OAuth2.0等方面的知识,就很好了。
-
- Eclipse
-
- Java8
-
- Spring
- OAuth2.0
建立亲自项目
我用Maven创建了一个父项目。将其命名为sso-oauth2-demo。
我将其放置在pom.xml中如下所示。
<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>sso-oauth2-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<spring-boot.version>2.1.9.RELEASE</spring-boot.version>
<spring-security.version>2.1.9.RELEASE</spring-security.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>${spring-security.version}</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
</project>
制作单一登录认证服务器
配置SSO认证服务器
在上述的主项目下创建子模块,并将其命名为auth-server。将以下内容放置在auth-server项目的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>com.example</groupId>
<artifactId>sso-oauth2-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<artifactId>auth-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>auth-server</name>
<description>Demo project for Spring Boot</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
创建一个application.yml文件,并按照下面的方式进行配置。
在此处,我们指定了SSO认证服务器的端口(8300)。
server:
port: 8300
servlet:
context-path: '/auth'
单点登录认证服务器的实现
(1). 实现main类
按照以下方式进行实现:
在main类上方添加@EnableResourceServer注解。此注解非常重要。
@SpringBootApplication
@EnableResourceServer
public class AuthServerApplication
{
public static void main( String[] args )
{
SpringApplication.run(AuthServerApplication.class, args);
}
}
(2) 实现 auth 类
/**
* SSO認証サーバー
* @author hyman
*/
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll")
.checkTokenAccess("isAuthenticated()");
}
@Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("SampleClientId")
.secret(passwordEncoder.encode("secret"))
.authorizedGrantTypes("authorization_code")
.scopes("user_info")
.autoApprove(true)
.redirectUris("http://localhost:8301/login", "http://localhost:8302/login");
}
}
(3). 实现Security类
在这里,用户id和密码将在源代码中进行定义。
用户ID: hyman
密码: hyman123
/**
* security配置
* ここでユーザidとパスワードはソースの中で定義します。
* @author hyman
*
*/
@Configuration
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/login", "/oauth/authorize")
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll()
.and().csrf().disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("hyman") // ユーザーID:hyman
.password(passwordEncoder().encode("hyman123")) // パスワード:hyman123
.roles("USER");
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
(3). 实现SSO认证API类
访问SSO认证服务器的URL为http://localhost:8300/auth/user/me。
@RestController
@RequestMapping(value = "user")
public class UserAction {
@GetMapping(value = "me")
public Principal me(Principal principal) {
System.out.println("アクセスユーザー情報:" + principal);
return principal;
}
}
到此为止,SSO认证服务器的实现已经完成。请参考以下层级,包括包等等。

制作客户端应用程序
从这里开始,我们将开始制作客户端应用程序。本文将创建两个客户端应用程序,分别为client-a和client-b。
客户A的应用程序
A配置 客户A
在亲的sso-oauth2-demo项目下创建客户端a模块。
将下面的内容放入客户端a模块的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>com.example</groupId>
<artifactId>sso-oauth2-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<artifactId>client-a</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>client-a</name>
<description>Demo project for Spring Boot</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
(2)application.xml的配置
根据以下内容,将client-a配置进去。
在这里将client-a的端口定义为8301。
server:
port: 8301
servlet:
session:
cookie:
name: CLIENT_A_SESSION
security:
oauth2:
client:
client-id: SampleClientId
client-secret: secret
access-token-uri: http://localhost:8300/auth/oauth/token
user-authorization-uri: http://localhost:8300/auth/oauth/authorize
resource:
user-info-uri: http://localhost:8300/auth/user/me # SSO認証サーバーのアドレス
spring:
thymeleaf:
cache: false
客户端A的实施。
实现security类
请确保在类的上方加上@EnableOAuth2Sso注解,因为它非常重要,不要忘记。
@EnableOAuth2Sso
@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/login**")
.permitAll()
.anyRequest()
.authenticated();
}
}
在resources文件夹下创建一个templates文件夹,并创建一个用于显示画面的index.html文件。
将以下内容写入index.html文件中。
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Spring Security SSO</title>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
</head>
<body>
<div class="container">
<div class="col-sm-12">
<h1>Spring Security SSO クライアントアプリケーションA</h1>
<a class="btn btn-primary" href="securedPage">Login</a>
</div>
</div>
</body>
</html>
请在与上述相同的位置创建securedPage.html,并按照以下方式进行实施。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Spring Security SSO</title>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
</head>
<body>
<div class="container">
<div class="col-sm-12">
<h1>Secured Page, Client A</h1>
Welcome, <span th:text="${#authentication.name}">Name</span>
</div>
</div>
</body>
</html>
(2).实现Controller类
@Controller
public class IndexAction {
@GetMapping(value = "")
public String index() {
System.out.println("ClientAのindex画面へ遷移する");
return "index.html";
}
@GetMapping(value = "securedPage")
public String home() {
System.out.println("ClientAのsecuredPage画面へ遷移する");
return "securedPage.html";
}
}
当我们达到这个地步,客户A已经结束了。
客户端B应用程序
由于client-b应用程序的构建方式与client-a相同,所以这里省略说明。
然而,在创建client-b应用程序时,有一个地方需要修改client-b的名称。
例如,client-b的端口被改为8302。
在此,SS0功能的实施已经全部完成。
SS0功能验证
首先,每个人都会启动三个项目。
-
- auth-server
-
- client-a
- client-b
然后,我们将访问客户端A。URL为http://localhost:8301。



由于通过client-a成功登录,所以在使用SSO功能访问client-b时,应该能够在client-b的主页上点击登录按钮,并且无需输入用户名和密码就能够成功登录。
好吧,我们来验证一下。

当您点击登录按钮时,您将直接进入client-b的securedPage页面,而不是转到登录页面。

所以,单一登录(SSO)功能非常顺利。
最終結論
感谢您阅读到最后。
您可以在这里下载源代码。
源代码:https://github.com/Hyman1993/Demo-Projects/tree/master/sso-oauth2-demo