【问题标题】:Spring Security authentication loggingSpring Security 身份验证日志记录
【发布时间】:2012-07-12 11:52:35
【问题描述】:

我正在使用 Spring Security 3.1 对网站的用户进行身份验证。当由于 Spring Security 无法连接到数据库而导致登录失败时,我的日志中会出现以下语句:

2012-07-12 11:42:45,419 [ajp-bio-8009-exec-1] DEBUG      org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Authentication request failed: org.springframework.security.authentication.AuthenticationServiceException: Could not get JDBC Connection; nested exception is java.sql.SQLException: Connections could not be acquired from the underlying database!

我的问题是,为什么这是 DEBUG 语句而不是 ERROR?为了找出实际的错误,我必须翻遍一大堆调试语句。

编辑

这是我的身份验证管理器:

<bean id="securityDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:comp/env/securityDS"/>
    <property name="resourceRef" value="true"/>
</bean>

<bean id="encoder" class="org.springframework.security.crypto.password.StandardPasswordEncoder" />

<security:authentication-manager>
    <security:authentication-provider>
        <security:password-encoder ref="encoder" />
        <security:jdbc-user-service 
            data-source-ref="securityDataSource"
            authorities-by-username-query="SELECT username, authority FROM login WHERE username = ?"
            users-by-username-query="SELECT username, password, enabled FROM login WHERE username = ?"
        />        
    </security:authentication-provider>
</security:authentication-manager>

【问题讨论】:

    标签: spring logging spring-security log4j


    【解决方案1】:

    该消息打印在AbstractAuthenticationProcessingFilter.unsuccessfulAuthentication:

    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException failed) throws IOException, ServletException {
        SecurityContextHolder.clearContext();
    
        if (logger.isDebugEnabled()) {
            logger.debug("Authentication request failed: " + failed.toString());
    

    身份验证失败的方式有多种,包括基于用户输入。例如,在AbstractUserDetailsAuthenticationProvider.authenticate 中,如果找不到用户名,则可能会抛出BadCredentialsException

            try {
                user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
            } catch (UsernameNotFoundException notFound) {
                logger.debug("User '" + username + "' not found");
    
                if (hideUserNotFoundExceptions) {
                    throw new BadCredentialsException(messages.getMessage(
                            "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
                } else {
                    throw notFound;
                }
            }
    

    由于身份验证失败可能有正当理由,AbstractAuthenticationProcessingFilter 记录错误是没有意义的。如果存在系统错误,则该错误应该在下游记录。

    我怀疑问题出在DaoAuthenticationProvider(请参阅我的内联评论):

    protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
        UserDetails loadedUser;
    
        try {
            loadedUser = this.getUserDetailsService().loadUserByUsername(username);
        }
        catch (DataAccessException repositoryProblem) {
            // *** ERROR SHOULD BE LOGGED HERE ***
            throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
        }
    

    也许应该在这里记录一个错误——你可以用 Spring 记录一个 JIRA 来请求它。尽管他们可能假设每个人都将提供一个自定义的UserDetailsService 并在那里捕获/记录他们自己的异常。如果您使用的是JdbcDaoImpl,则不会。我认为JdbcDaoImpl 旨在作为一个例子,并不可靠。根据文档:

    好消息是我们提供了一些 UserDetailsS​​ervice 实现,包括使用内存映射的实现 (InMemoryDaoImpl) 和另一个使用 JDBC (JdbcDaoImpl)。大多数用户 但是,他们倾向于编写自己的实现,通常 简单地坐在现有的数据访问对象 (DAO) 之上 代表他们的员工、客户或其他用户 应用。

    【讨论】:

    • 如何知道我使用的是哪种 UserDetailService?
    • 你能发布你的配置的&lt;authentication-manager&gt; 部分吗?
    • 是的,&lt;security:jdbc-user-service&gt;JdbcDaoImpl。所以基本上如果你想要更好的错误记录,你需要编写自己的UserDetailsService。好消息是这样做非常容易 - 您只需实现一种方法 (loadUserByUsername)。
    • 所以我不得不放弃 Spring Security 的开箱即用实现的便利性和可靠性,并使用我自己的只是为了更好地记录登录失败?这听起来像是在浪费时间……更不用说为引入新错误打开大门了。
    • 在这种情况下,最好记录您的数据库连接不正确/数据库已关闭等情况。您必须决定什么对您的情况更重要。我建议您查看JdbcDaoImpl 的来源,看看它是如何工作的。
    【解决方案2】:

    我的解决方案:

    @Component
    public class AuthenticationEventListener implements ApplicationListener<AbstractAuthenticationEvent> {
    
       private static Logger logger = Logger.getLogger(AuthenticationEventListener.class);
    
       @Override
       public void onApplicationEvent(AbstractAuthenticationEvent authenticationEvent) {
          if (authenticationEvent instanceof InteractiveAuthenticationSuccessEvent) {
             // ignores to prevent duplicate logging with AuthenticationSuccessEvent
             return;
          }
          Authentication authentication = authenticationEvent.getAuthentication();
          String auditMessage = "Login attempt with username: " + authentication.getName() + "\t\tSuccess: " + authentication.isAuthenticated();
          logger.info(auditMessage);
       }
    
    }
    

    不需要其他配置。

    【讨论】:

    • 你在哪里添加 AuthenticationEventListener 在配置?是否必须将其构建到主 web-app jar 文件中?
    • 我已经设法将记录器与 JDBC 一起使用,如下所示:
    猜你喜欢
    • 2018-10-14
    • 2021-07-31
    • 1970-01-01
    • 2011-12-09
    • 2022-01-15
    • 2014-02-26
    • 1970-01-01
    • 2014-06-09
    • 2013-01-15
    相关资源
    最近更新 更多