Spring 具有重叠配置的安全检查多个 <http> 块中的授权

Spring Security with overlapping configurations checks authorization in multiple <http> blocks

我有一个使用 Spring Security 4.2.18 的应用程序,其安全性在 xml 中配置。现在我在项目中引入 Spring Boot 2.5.4。升级后,我对某些请求的安全配置有问题。

我有一个匹配特定请求的块和一个匹配其余所有请求的块。

<http pattern="/api/**" use-expressions="true" authentication-manager-ref="apiAuthenticationManager" >
    <http-basic/>

    <intercept-url pattern="/api/**" access="hasRole('ROLE_API')"/>
    <csrf disabled="true"/>
</http>


<http pattern="/**" auto-config="true" use-expressions="true" create-session="always" disable-url-rewriting="true"
      authentication-manager-ref="authenticationManager">
    <form-login login-processing-url="/resources/j_spring_security_check" login-page="/login"
                username-parameter="j_username"
                password-parameter="j_password"
                authentication-failure-url="/login?login_error=t" default-target-url="/redirectlogin"/>
    <logout logout-url="/resources/j_spring_security_logout"/>

    ...

    <intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/>

    ...

</http>

<authentication-manager id="apiAuthenticationManager">
    <authentication-provider user-service-ref="apiUserDetailsService">
    </authentication-provider>
</authentication-manager>

ApiUserDetailsService遵循规范:

@Service
@Transactional
public class ApiUserDetailsService implements UserDetailsService {

    ...
    
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
        boolean foundAccount = apiConfig.getUsername().equals(username);
        if (foundAccount) {
            return new User(username, apiConfig.getPassword(), singletonList(new SimpleGrantedAuthority("ROLE_API")));
        }
        throw new UsernameNotFoundException("Could not finder user with name " + username);
    }

}

如果我在 /api 下发出请求并使用不正确的基本身份验证凭据,我之前会收到 401 未经授权的响应。自升级以来,我被重定向到 /login 并最终进入重定向循环,因为那里也使用了凭据。

如果我删除第二个 <http> 块中的 <intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/>,我会得到预期的行为。

如何配置我的应用程序,使我的请求不会尝试使用第二个 <http> 块中的规则进行授权?我曾尝试使用 EntryPoint,但在使用通用 <http> 块的方法错误地授权用户之前不会调用它。

您是否尝试过将 <intercept-url pattern="/login*" access="permitAll" /> 放在 <intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/> 的正上方,例如:

在第二个 http 块中:

...
<intercept-url pattern="/login*" access="permitAll" />
<intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/>
...

问题不在于配置重叠,而是失败的身份验证被重定向到 /login,后者又由第二个 <http> 配置处理。

我通过创建 AuthenticationEntryPoint 的实现来解决它,它在响应中设置 401 Unathorized 状态:

@Component
public class BasicAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) {
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
    }
}

然后在我的基本身份验证配置中引用它:

<http pattern="/api/**" use-expressions="true" authentication-manager-ref="apiAuthenticationManager" >
    <http-basic entry-point-ref="basicAuthenticationEntryPoint"/>

    <intercept-url pattern="/api/**" access="hasRole('ROLE_API')"/>
    <csrf disabled="true"/>
</http>