“Authentication(认证)”是spring security框架中最重要的功能之一,所谓认证,就是对当前访问系统的用户给予一个合法的身份标识,用户只有通过认证后才可以进入系统,在物理世界中,有点类似“拿工卡刷门禁”的场景。
身份认证在市面上有很多种的实现协议,最常见的就是用户名密码的认证方式,另外还有OAuth2.0,CAS(Central Authentication Service),SAML等,其中OAuth2.0是一种我们比较熟悉的认证协议,例如微信,支付宝提供的第三方登录。
回到身份认证的原本需求:
根据上面的描述,很自然地,我们想到定义一个controller的API接口来提供认证服务,然后定义一个“切面”来校验认证信息,这种方式可以方便地拦截到系统内各个资源的访问请求,不仅可以灵活配置,也不会侵入业务代码。
到此,我们对认证的架构有了一个初步的构想,先画一个简单的草稿
这里所谓的“令牌”,“凭证”,“认证信息”,“受保护资源”都是抽象的概念,并不特指某一种实现,“切面”也不是Spring的AOP,只表示在执行校验逻辑时,不与受保护资源相耦合,它应该是独立运作的模块。
下面具体看一下spring security中的认证架构设计,对比上图,学习一下spring security是如何实践的。
spring security利用了SecurityFilterChain的过滤器中实现了校验逻辑,另外为了实现各种认证协议,spring security也内置了很多种认证实现类,供开发者直接使用,不过这里提供两种方式,一种也是利用SecurityFilterChain的过滤器来实现认证服务,当然也可以实现自定义的Controller来暴露API接口。明确了这两点之后,我们再给出spring security完整的认证架构,图中均以SecurityFilterChain的过滤器实现认证和校验的逻辑,这是比较常见惯用的方法。
首先介绍一下相关的接口和类:
Authentication authenticate(Authentication authentication) throws AuthenticationException;
从图中可以看到,整个认证流程主要围绕以下3个Filter:
try {
Authentication authenticationToken = createAuthentication() // // 例如创建UsernamePasswordAuthenticationToken,OAuth2AuthorizationCodeAuthenticationToken等等,将待认证的信息封装起来,
Authentication authResult = this.authenticationManager.authenticate(someAuthenticationToken); // 交给ProviderManager进行认证,通常由实际的AuthenticationProvider实现类完成具体的认证逻辑,并将认证结果返回
SecurityContext context = this.securityContextRepository.createEmptyContext(); // 创建一个空载的SecurityContext实例
context.setAuthentication(authResult); // 传入经过认证的Authentication对象
this.securityContextHolderStrategy.setContext(context); // 存储SecurityContextHolder中,方便同一个线程执行过程中的其他地方获取
this.securityContextRepository.saveContext(context, request, response); // 进行持久化,方便下次请求访问时,可以获取对应SecurityContext,实现登录态的保持
this.successHandler.onAuthenticationSuccess(request, response, authResult); // 认证成功后的流程,例如跳转到系统首页等
} catch (AuthenticationException ex) {
// Authentication failed
this.securityContextHolderStrategy.clearContext(); // 认证失败时,清空SecurityContext
this.failureHandler.onAuthenticationFailure(request, response, failed); // 认证失败后的流程,例如提示错误信息等
}
说明:spring security对用户名和密码的认证提供了默认实现DaoAuthenticationProvider,但由于默认实现限制比较多,一般在实际的生产活动中不会采用,通常会继承AbstractUserDetailsAuthenticationProvider来定制开发,或者参考它的源码自定义实现AuthenticationProvider接口。
最后,我们对spring security整个认证架构中的认证流程和存取校验流程,再做一个总结:
可见,上述两个核心流程基本围绕着Authentication和SecurityContext这两个接口来建设,前者对外提供认证服务,我们可以进行深度的定制开发,包括Authentication,Filter,AuthenticationProvider都可以自定义实现,并整合进入SecurityFilterChain,后者对内提供存取服务,通常情况下我们也不会对存取流程进行改造,对于绝大多数场景,只需要利用SecurityContextHolder这个工具类读写SecurityContext对象,这基本上已经足够了。这样的设计,在最大程度上固化了存取校验的逻辑,不会因为认证机制和结果的不同,而改变存取校验的逻辑。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有