APP下载

Spring Security中设计模式的运用浅析

2023-10-14覃茏伟兰全祥

大众科技 2023年9期
关键词:设计模式适配器过滤器

覃茏伟 兰全祥 

Spring Security中设计模式的运用浅析

覃茏伟 兰全祥

(攀枝花学院,四川 攀枝花 617000)

Spring Security作为Web开发中十分重要的安全框架之一,常被用于Web应用的认证和授权。为了进一步了解Spring Security框架的设计和实现,加深对常见设计模式的理解,文章详细介绍了Spring Security框架中策略模式、代理模式、适配器模式、责任链模式、模板方法模式的运用,对上述设计模式的概念、基本原理、作用等进行描述,分析Spring Security中关键类库在设计模式中承担的作用及执行流程,为开发人员提供一定的学习参考。

Spring Security;设计模式;认证;授权

引言

在Web应用开发过程中,系统安全性和数据安全性是开发人员必须要考虑的,而Spring Security就是一个为企业应用系统提供访问控制的安全框架。该框架的目的是在系统开发中减少编写大量重复的安全控制方面的代码,提高程序开发效率[1]。

在程序开发过程中,经过长时间的实践,开发人员总结出了面临一般问题的解决方案,即设计模式。研究Spring Security框架中的设计模式,不仅能够进一步了解设计模式在开发中的运用,还能深入了解Spring Security框架的安全认证机制,能够为框架和设计模式的学习者提供新的学习思路和学习参考。针对Spring Security框架的主要作用以及常用的设计模式,本文主要讨论Spring Security框架中的策略模式、代理模式、适配器模式、责任链模式和模板方法模式的运用。

1 Spring Security的概述

Spring Security作为应用程序开发中常用的安全框架,其最大的优势是能够便捷、高效地完成认证和授权,且该框架能够为开发者提供一个高度自定义的开发环境,开发者可以根据不同需求灵活、便捷地进行配置。

1.1 认证功能

Spring Security中的认证是由AuthenticationManager接口来负责的,该接口的核心方法是Authentication authenticate (Authentication authentication)throws AuthenticationException,当正常返回 Authentication 时,表示认证成功;当抛出异常时,则表示认证失败。

该接口的主要实现类为 ProviderManager,在该实现类中管理了众多AuthenticationProvider实例。在一次完整的认证流程中,Spring Security允许存在多个AuthenticationProvider用来实现多种不同的认证方式,且都由 ProviderManager 进行统一管理,认证信息将被Authentication 的实现类进行保存,且用户信息将被Spring Security的SecurityContextHolder对象进行保存。

1.2 授权功能

在 Spring Security 的授权体系中,有两个关键接口分别是AccessDecisionManager和AccessDecisionVoter。

当用户请求一个资源(通常是一个接口或者方法)时,用户的角色信息会被封装成一个ConfigAttribute 对象。使用ConfigAttribute对象的getAttribute()方法可以获得一个带有“ROLE_”前缀的用户角色信息。AccessDecisionVoter会根据用户角色信息和所请求资源的 ConfigAttribute进行比较,进而投出赞成、反对或者弃权票。AccessDecisionManager作为访问决策管理器,最终将决定用户的此次访问是否被允许。

1.3 核心原理

Spring Security的核心原理是将若干个过滤器组成过滤器链,然后所有的Servlet请求经过这些过滤器进行认证、授权等,从而保障程序的安全性。主要过滤器包括Username Password Authentication Filter过滤器(处理表单认证信息)、Basic Authentication Filter过滤器(检测和处理http basic认证)、Other Security Filter过滤器、Exception Translation Filter过滤器(处理Access Denied Exception和Authentication Exception异常)和Filter Security Interceptor过滤器(授权并决定是否可以访问资源服务)等[2],Spring Security主要过滤器如图1所示。

图1 Spring Security主要过滤器

2 Spring Security的设计模式

Spring Security框架运用了诸多设计模式,包括策略模式、代理模式、适配器模式、模板方法模式、责任链模式、建造者模式、观察者模式、装饰器模式等。本文将重点介绍策略模式、代理模式、适配器模式、责任链模式和模板方法模式在Spring Security中的运用。

2.1 策略模式

2.1.1 策略模式介绍

策略模式是一种行为型模式,它将对象和行为分开,将行为定义为一个接口,并将其作为抽象策略类(Strategy)。每一个不同的具体行为需要实现抽象策略类,并将其作为具体策略类(ConcreteStrategy),即不同的策略。除此之外,再由上下文类(Context)根据需求对不同的策略进行选择和替换[3]。

策略模式关注的是行为的选择,即可以在不同的行为之间进行替换。例如,在一个业务处理流程中有许多种方式可以完成该业务,这些方式之间的区别仅在于解决方式不同,即不同的行为,可以使用策略模式让上下文类(Context)动态地选择一种方式进行执行。

2.1.2 在Spring Security中的运用

一般情况下,当用户登录成功后,系统会保存用户登录信息。从信息安全的角度出发,为了确保用户信息只有当前线程能够访问,通常会将当前用户信息和当前线程进行绑定,即将用户信息存储在当前线程中。但是系统中难免会出现多线程的业务,或是在某个具体的业务逻辑处理中需要开启子线程,此时就需要每个子线程都能够访问用户信息。这时,就出现了不同的用户信息存储策略,需要开发人员根据不同的需求选择存储策略。

Spring Security在解决用户信息存储的问题上采用了策略模式。该框架定义了SecurityContextHolderStrategy接口作为抽象策略类,并实现了三种不同的具体存放策略以及作为上下文类的SecurityContextHolder,如图2所示。

图2 Spring Security中策略模式示意图

Spring Security在SecurityContextHolder中定义了三个静态常量,分别是MODE_THREADLOCAL、MODE_INHERITABLETHREADLOCAL、MODE_GLOBAL,并让其与某一具体存放策略进行对应,开发人员在开发过程中只需根据具体需求去选择存放策略。

以MODE_THREADLOCAL所对应的存储策略为例,程序在启动过程中会先加载写有该策略信息的配置文件,然后再运行过程中根据所选策略来执行ThreadLocalSecurityContextHolderStrategy中的具体实现,该策略会通过ThreadLocal保存用户信息。由于使用 ThreadLocal 创建的变量只能被当前线程访问,不能被其他线程访问和修改,从而确保了用户信息安全。

2.2 代理模式

2.2.1 代理模式介绍

代理模式是一种对象结构型模式,它给某一个真实对象(委托类)提供一个代理对象(代理类)。为了确保代理类能够实现对委托类的代理,一般情况下需要两个类共同实现公共接口,并由代理对象控制对真实对象的引用,从而控制对象的访问[4]。

代理类能够对委托类进行功能增强(包括功能的预处理和后置处理),由于代理类对象本身并不真正实现服务,因此代理类对象需要与对应的委托类对象进行关联,当需要提供服务时,由代理类对象调用所关联的委托类对象的相关方法来提供服务。代理模式就是在访问委托类对象时增加了一定程度的间接性,这样在程序开发过程中,可以附加多种其他操作,如Spring Security框架中对密码进行加密、加密方法的判断和选择等。

2.2.2 在Spring Security中的运用

为了保证用户密码安全,系统通常不会将用户密码以明文的方式存放在数据库中,而是采用合适的加密方案对密码进行加密后再进行存储。Spring Security中定义一个PasswordEncoder接口,并提供了多个实现类来对应不同的加密方案,方便开发者对密码进行加密,如图3所示。

图3 PasswordEncoder的接口及实现类

但是在系统运行过程中,如果长期使用单一的密码加密方案可能会降低系统安全性,因此在开发过程中往往会在上述加密方案中进行随机选择,让数据库中存放不同加密方案所加密的密文,以保障用户信息的安全。由于用户登录时需要进行密码比对,且登录和注册时需要使用相同的PasswordEncoder接口实现类,因此多种加密方案会对登录校验造成困难,即在登录校验时需要先判断加密方案,然后再调用对应加密方案的PasswordEncoder实现类去进行密码比对。

为了解决上述问题,Spring Security采用代理模式对需要进行比对的密码进行预处理,即判断用户密码使用的加密方案,再进行匹配。以BCryptPasswordEncoder加密方案为例,Spring Security定义了一个代理类DelegatingePasswordEncoder,该代理类实现了PasswordEncoder接口,然后在类中调用了委托类BCryptPasswordEncoder的matches方法,如图4所示。

图4 DelegatingePasswordEncode代理示意图

具体代理流程:在委托类BCryptPasswordEncoder的matches方法中先对参数中的密文进行解析,从而判断出密码的加密方案id。然后,根据加密方案id生成对应的加密方案对象(PasswordEncoder实现类对象),再通过调用该对象的matches方法完成密码比对。核心代码如下:

public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {

if (rawPassword == null && prefixEncodedPassword == null) { return true; } else {

String id = this.extractId(prefixEncodedPassword);

PasswordEncoder delegate = (PasswordEncoder)this. idToPasswordEncoder.get(id);

if (delegate == null) {

return this.defaultPasswordEncoderForMatches.matches(rawPassword, prefixEncodedPassword);

} else {

String encodedPassword = this.extractEncodedPassword (prefixEncodedPassword);

return delegate.matches(rawPassword, encodedPassword);}}}

若系统中存在多种不同的密码加密方案,采用代理模式可以在进行密码对比时对相应方法进行增强,提前对密文和加密方案进行判断,提高开发效率。Spring Security将对PasswordEncoder各个实现类的访问操作交给代理对象DelegatingePasswordEncoder进行,实现对不同加密方案的统一管理。

此外,Spring Security框架使用DelegatingFilterProxy将过滤器链链接到Web Filter中也是典型的代理模式。

2.3 适配器模式

2.3.1 适配器模式介绍

适配器模式属于结构型模式,一般用作两个不兼容接口之间的桥梁。适配器模式主要包含Target(目标抽象类,包含用户所期望的接口)、Adaptee(被适配类,即被适配的角色)以及Adapter(适配器类)三个角色。虽然Adaptee中已经存在满足客户需求的内容,但是由于接口不匹配,因此需要通过Adapter对接口进行转换,即通过Adapter使Adaptee和Target进行适配。

适配器模式的原理是让Adapter实现目标抽象类Target并关联一个被适配类Adaptee对象,使得后二者产生联系。由于Adapter结合了两个接口的功能,因此该模式能够将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作[5]。

2.3.2 在Spring Security中的运用

在Spring Security中SecurityConfigurer是一个非常重要的角色,SecurityConfigurer主要用来对Spring Security中的过滤器进行配置,从而实现对所有请求的控制。SecurityConfigurer具有一个重要的实现类WebSecurityConfigurer,该类目的是配置WebSecurity。然而,为了实现对WebSecurity的配置和初始化,需要使用HttpSecurity类的方法,即WebSecurityConfigurer这个目标抽象类需要使用HttpSecurity中的内容,但由于两者之间不能直接进行交互,因此引入了WebSecurityConfigerAdapter作为适配器,以使两者能够在一起工作。

Spring Security采用适配器模式,定义了一个WebSecurityConfigerAdapter适配器(适配器类),该类继承WebSecurityConfigurer(目标抽象类)并与HttpSecurity(适配者类)进行关联,三者关系如图5所示。

图5 Spring Security中适配器模式关系示意图

在WebSecurityConfigerAdapter中能够对HttpSecurity类的示例进行初始化,同时实现对WebSecurity的配置,进而完成WebSecurityConfiger所需业务需求,关键代码如下。

public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer {

/*省略其他成员变量*/

private HttpSecurity http;

protected final HttpSecurity getHttp() throws Exception {}//实现对HttpSecurity的初始化

public void init(WebSecurity web) throws Exception {

HttpSecurity http = this.getHttp();

//使用http完成WebSecurityConfiger所需业务

}

2.4 责任链模式

2.4.1 责任链模式介绍

责任链模式属于行为型模式,它将命令的发出者和执行者解耦。命令的发出者将命令发出后,命令到达责任链,并沿着责任链在不同的执行者之间传递,最后由适当的执行者进行处理。发出者无需关心命令由哪一个执行者处理以及处理细节,只需将命令发送到责任链上,实现将命令发出者和执行者进行分离,从而降低了他们之间的耦合度。

当有多个执行者可以处理同一个命令且无法确定最终会由哪一个或多个执行者来进行处理时,在责任链模式中,这些执行者将实现一个公共的Handler接口,并在该接口中定义了处理命令的方法和调用下一个处理者的方法。

2.4.2 在Spring Security中的运用

在Spring Security中使用了诸多的过滤器来对请求进行处理,这些过滤器都实现了一个公共的接口Filter,并在不同的实现类中重写doFilter方法进行不同功能的实现,如图6所示。

在进行用户登录认证时,使用UsernamePasswordAuthenticationFilter 过滤器对表单携带的登录信息进行处理;在防止CSRF(Cross Site Request Forgery,跨域请求伪造)时,使用CsrfFilter过滤器对所有post请求进行验证;当系统没有在配置文件中指定登录页面时,DefaultLoginPageGeneratingFilter过滤器会为系统生成一个默认的登录界面。但当一个请求到达前,系统并不能提前预知应该使用哪一个过滤器对请求进行处理,因此Spring Security引入了过滤器链来解决这一问题。

图6 Spring Security中Filter接口实现示意图

Spring Security采用责任链模式,定义了一个VirtualFilterChain 类,在其中声明了 5 个全局属性:originalChain 表示原生过滤器链(即Web Filter),additionalFilters 表示 Spring Security 中的过滤器链(存储Filter的list集合);firewalledRequest 表示当前请求;size 表示过滤器链中过滤器的个数;currentPosition 则是遍历过滤器链时的下标。

当currentPosition等于size时,表示过滤器链已经执行完毕,此时会退出 Spring Security 过滤器链,并通过调用 originalChain.doFilter 进入到原生过滤链方法中。反之,则依次从 additionalFilters 中取出过滤器nextFilter并调用其doFilter 方法。相关代码如下:

public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {

if (this.currentPosition == this.size) {

this.originalChain.doFilter(request, response);

} else {

++this.currentPosition;

Filter nextFilter = (Filter)this.additionalFilters.get (this.currentPosition - 1);

nextFilter.doFilter(request, response, this);

}}

Spring Security中运用责任链模式,保障过滤器链中的每个过滤器都具有不同的职能并且互不相扰,使得每个过滤器都能独立、逐个处理Servlet请求。

2.5 模板方法模式

2.5.1 模板方法模式介绍

模板方法模式是一种行为型模式,将一个业务逻辑中的方法骨架作为一个模板方法,并将某些方法的具体实现延迟到子类中完成,使子类可以在不改变该业务逻辑执行顺序的情况下更改某些方法的实现。

模板方法模式中的主要角色有抽象类(Abstract Class),该类负责给出算法的轮廓和骨架。抽象类由一个模板方法和若干基础方法构成,其中模板方法就是算法的骨架,并按照某种顺序去调用基础方法。当存在多个业务具有相同逻辑,且业务处理顺序基本一致时,可以考虑模板方法模式。

2.5.2 在Spring Security中的运用

在Spring Security中诸多功能都是基于过滤器链实现的,且这些过滤器链都是通过建造者类来进行配置并创建的,工作流程都十分固定,主要包括初始化(init)、配置(config)、构建(performBuild)。但由于这些过滤器链的方法内部的实现都不尽相同,因此Spring Security通过模板方法来解决这一问题。

Spring Security采用模板方法模式定义了一个AbstractConfiguredSecurityBuilder作为所有过滤器链建造者类的父类,并在父类中规定了工作流程doBuild()方法(即方法调用顺序),并使用final修饰该方法,使得子类无法改变,从而让所有的过滤器链都按照前置初始化(beforeInit)、初始化(init)、前置配置(beforeConfigure)、配置(Configure)、构建(performBuild)的顺序执行建造者类中的方法。然后,这些过滤器链可以根据实际需求重写performBuild()方法,关键代码如下。

protected final O doBuild() throws Exception {

this.beforeInit();

this.init();

this.beforeConfigure();

this.configure();

O result = this.performBuild();

return result;

}

private void init() throws Exception { }

private void configure() throws Exception {}

protected abstract O performBuild() throws Exception{}

Spring Security中采用模板方法模式,将创建过滤器链的工作流程交由父类控制,而行为的实现(performBuild()方法)延迟到子类当中,使得不同子类可以完成不同的实现。

3 结束语

设计模式的意义就是为程序开发提供解决方案,优化代码逻辑,使程序更易于维护、扩展和服用。以Spring Security框架为例,理解框架中所运用到的策略模式、代理模式、适配器模式、责任链模式、模板方法模式等设计模式,不仅能够从主流框架中学习到程序设计和开发思想,关注信息系统安全性,考虑工程实践对于社会的影响,还能够进一步了解设计模式的优缺点及使用场景。理解、掌握设计模式原理,并能够在合适的场景中使用设计模式是提高开发效率,增强代码可维护性的主要途经之一。

[1] 孙恩斯. Spring Security安全框架应用研究[J]. 信息系统工程,2019(3): 72.

[2] 朱运乔. 基于Spring Security认证与授权的Web应用与实现[J]. 电脑编程技巧与维护,2020(11): 14-16.

[3] 许俊. 基于策略模式的存储过程使用研究[J]. 四川职业技术学院学报,2017,27(5): 147-150.

[4] 卢增宁. 设计模式及其在软件设计中的应用[J]. 信息与电脑(理论版),2020,32(16): 127-129.

[5] 高升,方英兰,韩兵,等. 适配器与装饰者模式思想在结构化数据处理中的应用[J]. 北方工业大学学报,2020,32(2): 105-109,116.

Analysis of the Application of Design Patterns in Spring Security

As one of the most important security frameworks for Web development, Spring Security is often used for authentication and authorization of Web applications. In order to further understand the design and implementation of the Spring Security framework and deepen the understanding of common design patterns, this paper introduces the application of policy pattern, proxy pattern, adapter pattern, responsibility chain pattern and template method pattern in Spring Security framework in detail. This paper describes the concept, basic principle and function of the above design pattern, analyzes the role of key class libraries in Spring Security in the design pattern and the execution process, and provides certain reference for developers.

Spring Security; design patterns; authentication; authorization

TP393.08

A

1008-1151(2023)09-0001-05

2022-11-25

四川省高等学校第四批省级创新创业教育示范课程建设项目“Java Web应用开发实训”;2022年全国大学生创新创业训练计划项目(202211360019);2022年校级大学生创新创业训练计划项目(2022cxcy147)。

覃茏伟(2002-),男,四川自贡人,攀枝花学院学生,研究方向为软件开发。

兰全祥(1990-),男,四川攀枝花人,攀枝花学院讲师,硕士,从事计算机应用研究工作。

猜你喜欢

设计模式适配器过滤器
“1+1”作业设计模式的实践探索
三维协同设计模式下的航天项目管理实践与展望
交通机电工程设计模式创新探讨
支持过滤器的REST模型研究与实现
声音过滤器
适配器模式及其应用
新型水文测验GPS适配器设计与应用
基于蓝牙串口适配器的GPS接收机与AutoCAD的实时无线通信
美国麦格普公司新型M—LOK相机三脚架适配器
基于LOGO!的空气过滤器自洁控制系统