SpringSecurity简介

SpringSecurity是Spring家族中的安全框架,该框架依托Spring实现,提供了认证和授权的核心功能,此外还支持OpenID、LDAP等多种认证方式,并且能很方便的与SpringWeb或是SpringWebFlux集成。SpringSecurity起源于2004年发布的AcegiSecurity,后来该框架提供了集成到Spring的功能并逐渐发展形成了今天的SpringSecurity。

官方文档:https://spring.io/projects/spring-security

SpringSecurity的适用场景

SpringSecurity可能并不是一个设计良好的框架,主要有如下缺点:

缺点1:SpringSecurity是个非常臃肿的重量级框架,除了认证鉴权的核心流程,还存在着很多大部分系统都不会用到的功能。SpringSecurity诞生于Servlet/JSP后端MVC的时代,与如今的前后端分离工程天然不兼容,我们必须通过SpringSecurity的扩展点对其做大量魔改后才能正常使用。

缺点2:SpringSecurity内部做了大量抽象和封装,使其理论上可以用于非Spring工程或者传统Spring(非SpringBoot)工程,但配置比较复杂。在实际应用场景中,几乎可以说就是绑定SpringBoot使用的。

缺点3:SpringSecurity对工程侵入性强,流程不透明,SpringSecurity默认为工程注入了很多组件,如果对SpringSecurity内部实现不了解很容易造成误用,导致接口无法访问或是认证鉴权存在漏洞两个极端情况,而且出问题了根本不知道是哪个过滤器配置的问题。

缺点4:认证和鉴权并不是个复杂的功能,实现也相对灵活,即使是初级程序员具备相关信息安全知识后,也能为自己的工程写出一个比较靠谱的认证和鉴权实现。然而SpringSecurity试图将这些流程整合到一个框架中,为了实现高度可扩展性,引入了层层抽象,内部套娃式的执行流程人为造成了该框架的极大复杂性,与“小而美”的Unix哲学相背离,是Java领域过度设计的典型之一。

缺点5:SpringSecurity的扩展点命名混乱,无法让使用者做到“望文生义”,此外官方文档不知所云,缺乏真正有效的使用文档和最佳实践,想要使用该框架必须查看源码,了解其内部的执行流程后才能正确使用。不过好在认证鉴权不是个复杂的功能,做到这一点还算容易。

缺点6:对真实业务场景覆盖不足,真实开发中常用的验证码、多因素认证、Token认证鉴权等功能都需要我们基于它的扩展点来自己实现。

当然,也有一些人会对上述观点逐一反驳,毕竟SpringSecurity也不是一无是处,否则早就不存在了。SpringSecurity能和SpringBoot及其它Spring框架良好集成,它同时支持SpringWeb和SpringWebflux两套技术栈,在很多工程中也被广泛采用。认证和鉴权本身并不是个复杂的需求,SpringSecurity还没难用到完全不可用的地步,当引入SpringSecurity带来的便利性大于其使用成本时还是值得一用的。

总而言之,一般中小型项目中通常不建议轻易引入SpringSecurity,我们应该优先考虑自己使用过滤器、拦截器或是AOP实现,避免引入SpringSecurity造成的额外心智负担;如果项目中存在比较明确的认证鉴权流程,可以考虑使用Shiro等轻量级框架;如果明确项目使用SpringBoot(尤其是SpringCloud)且SpringSecurity内置的功能确实能很好的满足我们的需求,那么可以考虑使用该框架。

引入SpringSecurity起步依赖

SpringBoot工程中引入SpringSecurity比较简单,我们直接在pom.xml中引入起步依赖即可。起步依赖中包含一些自动配置将SpringSecurity的核心组件注入到Spring容器并和Spring集成,我们可以直接使用。

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

我这里使用的是SpringBoot2.7和SpringSecurity5.7版本。

使用SpringSecurity

引入SpringSecurity后,我们在SpringBoot中编写一个Controller,然后使用浏览器访问,我们会发现直接302跳转到了一个登录页。

@RestController
@RequestMapping("/dashboard")
public class DashboardController {
    @GetMapping("/pageInfo")
    public Object getPageInfo() {
        return "Hello, world!";
    }
}

初学者可能已经懵了,我们工程中没有任何页面,为什么跳转到了一个登录页?非初学者可能也沉默了,我们工程是个前后端分离项目,服务端只提供接口,直接302跳转到一个后端输出的登录页面是闹哪样?

其实这是历史原因造成的,SpringSecurity诞生于Servlet/JSP时代,那时候HTML页面都是采用后端模板(JSP、Velocity、Thymeleaf等)输出的,因此页面也会在后端Java工程中由后端程序员开发,服务端路由既可以指向AJAX接口也可以指向HTML页面,SpringSecurity默认我们采用了这种古早的开发方式,因此直接给我们输出了一个登陆页面。不过SpringSecurity具有高度的可扩展性,后续章节会介绍如何对框架进行扩展来实现我们的需求。

在工程的启动日志中,会有类似这样一条输出:

Using generated security password: c5ef888b-887b-4f0f-b7fc-fabcf55527f5
This generated password is for development use only. Your security configuration must be updated before running your application in production.

在没有任何额外配置时,SpringSecurity会在内存中生成一对用户名和密码供我们登录系统,我们可以使用user用户名和该密码登录。

实际开发中,我们一般都是将用户名和密码存储在数据库中,从数据库读取这些信息并校验,这需要扩展SpringSecurity的UserDetailsService来实现,有关这些内容将在后文介绍。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap