入门

本指南将引导您完成使用受Spring Security保护的资源创建简单Web应用程序的过程。

创建不安全的Web应用程序

在将安全性应用于Web应用程序之前,您需要一个Web应用程序来保护安全。本节中的步骤将指导您创建一个非常简单的Web应用程序。然后在下一节中使用Spring Security保护它。

Web应用程序包括两个简单视图:主页和“Hello World”页面。主页在以下Thymeleaf模板中定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<meta charset="UTF-8">
<title>Spring Security Example</title>
</head>
<body>
<h1>Welcome!</h1>

<p>Click <a th:href="@{/hello}">here</a> to see a greeting.</p>
</body>
</html>

如您所见,这个简单的视图包含指向页面“/ hello”的链接。这在以下Thymeleaf模板中定义:

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Hello World!</title>
</head>
<body>
<h1>Hello world!</h1>
</body>
</html>

Web应用程序基于Spring MVC。因此,您需要配置Spring MVC并设置视图控制器以公开这些模板。这是在应用程序中配置Spring MVC的配置类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.learn.security.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

/**
* @author YoungDream
* @since 2019/3/25 21:46
*/
@Configuration
public class MyMvcConfig extends WebMvcConfigurationSupport {
@Override
protected void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
registry.addViewController("/home").setViewName("home");
registry.addViewController("/hello").setViewName("hello");
registry.addViewController("/login").setViewName("login");
}
}

addViewControllers()方法(覆盖同名方法WebMvcConfigurer)添加了四个视图控制器。其中两个视图控制器引用名称为“home”(定义于home.html)的视图,另一个引用名为“hello”(定义于hello.html)的视图。第四个视图控制器引用另一个名为“login”的视图。您将在下一部分中创建该视图。

此时,您可以跳转到使应用程序可执行并运行应用程序而无需登录任何内容。

通过创建基本简单Web应用程序,您可以为其添加安全性。

设置Spring Security

使用Maven,这将是一个额外的条目添加到<dependencies>

1
2
3
4
5
6
7
8
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
...
</dependencies>

这是一个安全配置,可确保只有经过身份验证的用户才能看到秘密问候语:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.learn.security.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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

/**
* @author YoungDream
* @since 2019/3/25 21:51
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.logout()
.permitAll();
}

@Bean
@Override
protected UserDetailsService userDetailsService() {
PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
String password = passwordEncoder.encode("password");
//记住打印出来的密码并在下一步中使用
System.out.println(password);
//这里需要注意User.withDefaultPasswordEncoder()被弃用
UserDetails userDetails = User.withUsername("user").password(password).roles("USER").build();
return new InMemoryUserDetailsManager(userDetails);
}
}

WebSecurityConfig类与注释@EnableWebSecurity,使Spring Security的网络安全支持,并提供了Spring MVC的整合。它还扩展WebSecurityConfigurerAdapter并覆盖了其几种方法,以设置Web安全配置的一些细节。

configure(HttpSecurity)方法定义了哪些URL路径应该被保护,哪些不应该被保护。具体而言,“/”和“/ home”路径配置为不需要任何身份验证。必须对所有其他路径进行身份验证。

当用户成功登录时,它们将被重定向到先前请求的身份验证页面。有一个自定义的“/ login”页面loginPage(),每个人都可以查看它。

至于该userDetailsService()方法,它使用单个用户设置内存中用户存储。该用户被赋予用户名“user”,密码为“password”,角色为“USER”。

现在我们需要创建登录页面。“登录”视图已有一个视图控制器,因此您只需创建登录视图本身:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org"
>
<head>
<meta charset="UTF-8">
<title>Spring Security Example</title>
</head>
<body>
<div th:if="${param.error}">
Invalid username and password.
</div>
<div th:if="${param.logout}">
You have been logged out.
</div>
<form th:action="@{/login}" method="post">
<div><label> User Name : <input type="text" name="username"/> </label></div>
<div><label> Password: <input type="password" name="password"/> </label></div>
<div><input type="submit" value="Sign In"/></div>
</form>
</body>
</html>

如您所见,此Thymeleaf模板只显示一个表单,该表单捕获用户名和密码并将其发布到“/ login”。根据配置,Spring Security提供了一个拦截该请求并对用户进行身份验证的过滤器。如果用户无法进行身份验证,页面将重定向到“/ login?error”,我们的页面将显示相应的错误消息。成功注销后,我们的应用程序将发送到“/ login?logout”,我们的页面会显示相应的成功消息。

最后,我们需要为用户提供显示当前用户名和注销的方法。更新为hello.html当前用户打招呼并包含“注销”表单,如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Hello World!</title>
</head>
<body>
<h1 th:inline="text" th:text="${'Hello '+#httpServletRequest.remoteUser+' !'}">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
<form th:action="@{/logout}" method="post">
<input type="submit" value="Sign Out"/>
</form>
</body>
</html>

我们使用Spring Security的集成显示用户名HttpServletRequest#getRemoteUser()。“注销”表单将“POST”发送到“/logout”。成功注销后,它会将用户重定向到“/ login?logout”。

最后为了方便,我们将端口号设置为 80

1
2
server:
port: 80

启动程序发现Security的HelloWorld完成了…