SpringSecurity中因为自定义密码验证规则导致的Spring循环依赖问题

一、问题

SpringSecurity自定义账号密码校验,启动报错,出现依赖循环。

二、大致背景

SpringBoot版本:2.6.14,基于SpringSecurity实现自定义账号密码校验登录功能。SpringSecurity的配置类SecurityConfig配置了自定义的账号密码校验器UsernamePasswordAuthenticationProvider,同时还配置了AuthenticationManager以及PasswordEncoder等。UsernamePasswordAuthenticationProvider主要功能为基于配置的PasswordEncoder(BCryptPasswordEncoder)实现自定义的账号密码校验。

(1)SecurityConfig

在这里插入图片描述

(2)UsernamePasswordAuthenticationProvider

在这里插入图片描述

三、具体报错

项目启动失败,报错Spring循环依赖

在这里插入图片描述

(1)报错分析

根据错误信息所提到的依赖:

1.loginController 依赖于 LoginService(loginServiceImpl 的实例)。
2.loginServiceImpl 依赖于 AuthenticationManager。
3.SecurityConfig(配置类)中定义了 AuthenticationManager(通常是通过配置方法隐式或显式地创建的),并且它也依赖于 UsernamePasswordAuthenticationProvider。
4.UsernamePasswordAuthenticationProvider 又依赖于 PasswordEncoder。
然而,错误描述中并没有明显的依赖循环,但可能的问题在于Spring Security的配置方式或Bean的创建顺序上。

(2)问题定位与分析

从错来看,循环应该发生在SecurityConfig与UsernamePasswordAuthenticationProvider之间。

SecurityConfig配置了UsernamePasswordAuthenticationProvider作为自定义的账号密码校验器,UsernamePasswordAuthenticationProvider仅依赖于UserDetailsServiceImpl和PasswordEncoder。

UserDetailsServiceImpl与SecurityConfig没有直接依赖关系,在SecurityConfig中passwordEncoder()方法使用@Bean注解,将BCryptPasswordEncoder的实例注册为一个bean。当应用程序上下文启动时,Spring会根据配置信息创建所有的bean实例。对于这些组件来说,如果Spring首先创建SecurityConfig类的实例,并调用其中的passwordEncoder()方法。在passwordEncoder()方法中,会创建并返回一个BCryptPasswordEncoder实例,并将其注册为一个bean。而SecurityConfig依赖于UsernamePasswordAuthenticationProviderUsernamePasswordAuthenticationProvider有依赖于PasswordEncoder的实例BCryptPasswordEncoder。从而形成了循环依赖!!!

四、解决问题

(1)使用构造函数注入

将其中一个类的依赖关系移至构造函数,而不是通过字段注入。 将PasswordEncoder 通过构造函数new一个对象给UsernamePasswordAuthenticationProvider,而不是通过字段注入。这样,可以确保在 SecurityConfig 实例化之前,UsernamePasswordAuthenticationProvider 的依赖 PasswordEncoder 已经被满足。

代码:

//使用构造函数注入PasswordEncoder
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

(2)使用 @Lazy注解

使用 @Lazy 注解可以延迟创建 bean 的实例,从而打破循环依赖。在 UsernamePasswordAuthenticationProvider类中对 PasswordEncoder的注入点使用 @Lazy 注解。这将使得 UsernamePasswordAuthenticationProvider 在首次使用PasswordEncoder时才PasswordEncoder被实例化,从而解决循环依赖问题。

代码:

//使用 @Lazy注解延迟创建 PasswordEncoder 的实例
@Autowired
@Lazy
private PasswordEncoder passwordEncoder;
实例
@Autowired
@Lazy
private PasswordEncoder passwordEncoder;
  • 24
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Security,通过配置访问控制列表(Access Control List,ACL)来实现授权规则。授权规则可以在Spring Security的XML配置文件定义。 以下是一个示例配置文件,其定义了两个授权规则: ``` <http auto-config="true"> <intercept-url pattern="/admin/**" access="ROLE_ADMIN" /> <intercept-url pattern="/**" access="ROLE_USER" /> <form-login /> </http> <authentication-manager> <authentication-provider> <user-service> <user name="user" password="password" authorities="ROLE_USER" /> <user name="admin" password="password" authorities="ROLE_ADMIN" /> </user-service> </authentication-provider> </authentication-manager> ``` 上述配置文件,`<http>` 标签用于配置访问控制规则,其 `<intercept-url>` 标签用于定义 URL 匹配模式和对应的授权规则。此处定义了两个规则:对于 `/admin/**` 的请求,要求用户拥有 `ROLE_ADMIN` 角色;对于其他所有请求,要求用户拥有 `ROLE_USER` 角色。 `<authentication-manager>` 标签用于配置身份认证的方式,此处使用了内存的用户信息进行认证。 如果要自定义授权规则,可以实现 `AccessDecisionVoter` 接口,并将自定义的投票器配置到 `<http>` 标签: ``` <http auto-config="true"> <intercept-url pattern="/admin/**" access="ROLE_ADMIN" /> <intercept-url pattern="/**" access="ROLE_USER" /> <form-login /> <access-decision-manager> <decision-voters> <bean class="com.example.CustomVoter" /> </decision-voters> </access-decision-manager> </http> ``` 以上配置文件,`<access-decision-manager>` 标签用于配置访问决策管理器,其 `<decision-voters>` 标签用于定义投票器列表。`<bean>` 标签用于定义自定义投票器的实现类。在自定义投票器,可以编写自己的授权规则逻辑。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值