基本原理
本节先展示一下 Security 的默认安全登录方式,让读者快速直观地认识 Security。然后,分析 Security 的基本原理,以及部分源码,为进一步学习打下基础。
默认安全登录
首先,在项目中引入依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
为了启动 Spring Security,需要将 it-security-demo 中的配置项在配置文件中注释掉,这是为了启动项目方便,不需要每次输入密码。现在启动 Spring Security,将配置项在 application.properties 中注释,如下。
#security.basic.enabled = false
此时启动项目,就会启动 Security,这样可以看到在控制台上展示密码生成日志,如图9.1所示。

多次启动后,会发现图中的密码每次都不同,这是因为 Security 的密码是随机生成的。我们随意访问一个 RESTful API,会自动跳出一个登录对话框,这也是 Security 内部的默认跳转。图9.2是系统默认的登录对话框,以及登录成功后的效果(验证后会跳转到图9.2下方地址栏中的地址)。

既然密码每次是随机的,那么用户名呢?在 Spring Security 中,用户名也被固定了,只能是 user。
目前为止,我们已经成功启动 Spring Security,并且登录进系统。但这不是我们想要的效果,其中暴露了很多问题,下面列举部分不足。
-
密码随机生成。用户也不知道自己的密码,系统将不能登录。
-
登录用户唯一。当用户申请使用系统后,每个用户都有自己的账号。没有唯一的密码。
-
登录页面是以弹框的方式打开,不是自定义的首页。系统没有自定义首页。
-
缺少多种登录方式,例如以第三方的方式登录。
对于这些问题,Spring Security 并不是不能解决,因为 Security 允许开发者根据自己的情况开发扩展,实现接口。上文列举的部分不足,我们将会在本章的后续章节一一解决。
Security原理说明
Spring Security 最核心的其实是过滤器链,一组 Filter。所有发送的请求都会经过过滤器链,同样响应也会经过过滤器链,在系统启动时 Spring Boot 会自动地把它们配置进去。
基本原理图
下面是 Security 的原理图,如图9.3所示。

上面的图是一个最常见的介绍 Security 的原理图,下面就分别说明它们的功能,以及整体的运行流程。
-
UsernamePasswordAuthenticationFilter:表单登录。
-
BasicAuthenticationFilter:HTTP登录。
-
… :这里还有很多过滤器,可以根据自己的情况加入过滤器。
-
ExceptionTranslationFilter:这个过滤器必须要在 FilterSecurityInterceptor 的前面,位置不能动,它的作用是处理 FilterSecurityInterceptor 抛出的异常。
-
FilterSecurityInterceptor:这个过滤器处于整个过滤器链的最后一环,也是进入 RESTful 程序的前一个程序,这里将会做最后一次验证。
整体流程
每个过滤器的作用都已经被说明了,下面将描述,从认证开始进入 RESTful API 的整个流程。
首先,用户输入用户名和密码,然后单击登录。其中绿色部分的每一种过滤器代表着一种认证方式,主要用于检查当前请求有没有涉及用户信息,如果当前没有,就会跳入到下一个绿色的过滤器中,请求成功会显示标记。绿色认证方式可以配置,比如短信认证、微信认证等。如果我们不配置 BasicAuthenticationFilter,那么它就不会生效。
FilterSecurityInterceptor 过滤器是最后一个,它会决定当前的请求可不可以访问 RESTful API,判断规则会放在这里面。当不通过时会把异常抛给前面的 ExceptionTranslationFilter过滤器。
ExceptionTranslationFilter 接收异常信息时,将跳转页面引导用户进行认证。橘黄色和蓝色的位置不可更改。当没有认证的 request 进入过滤器链时,首先进入 FilterSecurityInterceptor,判断当前是否进行了认证,如果没有认证则进入 ExceptionTranslationFilter,处理抛出的异常,然后跳转到认证页面。部分源码如下所示。
UsernamePasswordAuthenticationFilter.java
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported:" +request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
……
}
这是表单过滤器,我们可以知道用户名与密码是从这里获取的,并封装返回 Authentication。Authentication 就是后续 Filter 决定页面跳转的依据。