(一)初识 SpringSecurity

序言

  应用程序的安全性通常体现在三个方面:

  • 认证:认证是确认某主体在某系统中是否合法、可用的过程。这里的主体既可以是登录系统的用户,也可以是接入的设备或者其他系统
  • 授鉴权: 授权是指当主体通过认证之后,赋予主体对应权限,鉴权是判断是否允许主体执行某项操作的过程
  • 防护:防护是指系统对不法攻击的抵御能力

  作为 Java 开发,要想使应用具备以上特性,你当然可以自己实现一套框架,但是技术发展这么多年,市面上应当已存在了成熟的框架,我们又为何要自己去造轮子呢?

  本文将带你了解 Spring Security,通过它将使我们更便捷地实现安全性功能。

Spring Security 是什么?

  Spring Security 是一个强大的、高度可定制的身份验证和访问控制框架,其关注于为 Java 应用程序提供身份验证和授权。
  像所有 Spring 项目一样,Spring Security 的真正威力在于它可以如何轻松地扩展以满足定制需求。

为何选择 Spring Security ?

  安全框架新增种类繁多,但仍有令人信服的理由让你选择 Spring Secutiry,因为其:

  • 支持绝大多数认证机制,比如:
    • HTTP BASIC authentication headers:一个基于 IETF RFC 的标准
    • HTTP Digest authentication headers:一个基于 IETF RFC 的标准
    • HTTP X.509 client certificate exchange:一个基于 IETF RFC 的标准
    • LDAP:一种常见的跨平台身份验证方式
    • Form-based authentication:用于简单的用户界面需求
    • OpenID authentication:一种去中心化的身份认证方式
    • Authentication based on pre-established request headers:类似于 Computer Associates SiteMinder, 一种用户身份验证及授权的集中式安全基础方案。
    • Jasig Central Authentication Service:单点登录方案
    • Transparent authentication context propagation for Remote Method Invocation(RMI)and HttpInvoker:一个Spring远程调用协议
    • Automatic “remember-me” authentication:允许在指定到期时间前自行重新登录系统
    • Anonymous authentication:允许匿名用户使用特定的身份安全访问资源
    • Run-as authentication:允许在一个会话中变换用户身份的机制
    • Java Authentication and Authorization Service:JAAS,Java 验证和授权 API
    • Java EE container authentication:允许系统继续使用容器管理这种身份验证方式
    • Kerberos:一种使用对称密钥机制,允许客户端与服务器相互确认身份的认证协议
  • 防护性强,提供会话固定,点击劫持,跨站点请求伪造攻击解决方案
  • 易集成,作为 Spring 家族成员,可与 Servlet API、Spring Web MVC 无缝集成

如何使用 Spring Secutiry?

快速入门——默认的 HTTP 认证

  既然 Spring Secutiry 这么强大,那我们就来学会如何使用它吧!

  Spring Security 使用步骤如下:

  • ① 创建 Spring Boot 项目
  • ② 添加相关 Spring Secutiry 依赖到 pom.xml 文件
  • ③ 启动项目

① 创建 Spring Boot 项目

  略。

② 添加相关 Spring Secutiry 依赖到 pom.xml 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>

③ 启动项目访问相关端口

  在引入 Spring Security 项目之后,虽然没有进行任何相关的配置或编码,但 Spring Security 有一个默认的运行状态,要求在经过 HTTP 基本认证后才能访问对应的 URL 资源,因此会出现这么一个界面:

  默认使用的用户名为 user, 密码则是动态生成并打印到控制台的一串随机码。
  当然,以上参数值是可以修改的,可以在配置文件新增以下配置:

1
2
spring.security.user.name=root
spring.security.user.password=123456

  重新启动应用,通过变更后的配置可以通过认证。

进阶—— URL 安全策略

  虽然在快速入门中我们学会了 Spring Security 的基本使用,但一般情况下 URL 是通过 RBAC(Role-based access control,基于角色的访问控制)进行控制的,因此,在 Spring Security 中,也定义了相关的 URL 安全策略。

  从快速入门中,我们知道默认情况下的 Spring Security 会拦截所有请求,那么如何进行自定义呢。

自定义安全策略

  对于安全策略,Spring Security 中肯定存在了一个相应的类包含安全策略相关方法,那么我们找到该类重写它的方法就行了。
  因此若想自定义安全策略,需要:

  • 继承相应的配置类重写对应方法
  • 将创建的配置类交由 Spring 管理

  因此,我们首先需要在配置类上添加@EnableWebSecurity注解,使该类自动被 Spring 发现并注册

1
2
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter

  之后,重写继承类的方法:

1
2
3
4
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {

}

  configure()方法继承自WebSecurityConfigurerAdapter类,其默认声明了一些安全特性:

  • 验证所有请求
  • 允许用户使用默认的表单页面登录进行身份验证
  • 允许用户使用 HTTP 基本认证

  这些安全特性我们都需要吗?应当是不需要的。
  我们需要验证所有的请求吗?
  不需要,一般肯定存在谁都可以访问的请求(如登录请求)。
  我们需要使用默认的表单页面吗?
  不需要,一般肯定是自己提供更优美的页面。
  我们一定会使用 HTTP 认证这种方式进行认证吗?
  不一定,一般开发者构建的系统更为复杂,那么认证流程自然得重新适配。

  因此,这些安全特性允许开发者自定义,这些都通过HttpSecurity

  HttpSecurity允许我们为特定的 HTTP 请求配置安全策略,其提供了很多配置相关的方法:

  • authorizeRequests():返回了一个 URL 拦截注册器,可调用它提供的以下方法来对指定的 URL 指定安全策略
    • anyRequest():对任何请求执行何种策略
    • antMatchers():采用 ANT 模式的 URL 匹配器,在 ANT 模式中:
      • ?:表示匹配任意单个字符
      • *:表示匹配 0 或任意数量的字符
      • **:表示匹配 0 或者更多的目录
    • regexMatchers():正则模式的 URL 匹配器
    • ……
  • formLogin():声明了表单认证方式,可指定自定义的登录页面或路由
    -httpBasic()
  • csrf(): Spring Security 提供的跨站请求伪造防护功能
1
2
3
4
5
6
7
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
}

工作原理

  Spring Security 设计时采用了责任链的模式,为了适配各种业务场景,通过FilterChainProxy构造了一条非常长的过滤器链,默认(Security 5.2.2 版本)过滤器包括:

  • WebAsyncManagerIntegrationFilter:关联 Spring Web 上下文和 Spring Security上下文
  • SecurityContextPersistenceFilter:本次请求前构建SecurityContext对象(设置Authentication对象)到ThreadLocal中,请求后移除,方便在后续过滤器中获取授权信息
  • HeaderWriterFilter:往请求头或响应头里写入信息
  • CsrfFilter:跨站请求伪造安全
  • LogoutFilter:注销相关
  • UsernamePasswordAuthenticationFilter(重要)认证授权相关,基于表单的登录信息进行校验,成功后构造授权对象Authentication封装到SecurityContextHolder中,后续用于资源权限判断
  • DefaultLoginPageGeneratingFilter:若未配置登录页面则系统初始化时将配置这个过滤器,前后端分离用不到
  • BasicAuthenticationFilter:检测和处理 http basic 认证,由于该认证方式不安全,基本用不到
  • RequestCacheAwareFilter:用来处理请求的缓存
  • SecurityContextHolderAwareRequestFilter:主要是包装请求对象 request
  • AnonymousAuthenticationFilter:匿名访问相关,若检测到 SecurityContextHolder中不存在 Authentication对象将提供提供一个匿名 Authentication
  • SessionManagementFilter:管理 Session
  • ExceptionTranslationFilter:用于处理前面的认证异常AuthenticationException:和后续的资源权限异常AccessDeniedException
  • FilterSecurityInterceptor(重要)鉴权相关,判断资源是否有权访问(与配置的规则有关),将调用 AccessDecisionManager 来验证当前已认证成功的用户是否有权限访问该资源

  当然,若需要扩展还可以进行相应的增加,Spring Security 还提供了其他过滤器,后续我们会使用。

  不过现在,我们的关注点并不在此,我们最想知道的是:在 Security 中是如何完成认证、授权和鉴权的。

  在 Security 中,通过两个核心的过滤器来完成以上任务:

  • UsernamePasswordAuthenticationFilter
  • FilterSecurityInterceptor

  具体流程,我们将在后续文章中进行分析。

参考

0%