Spring Security 身份验证日志记录
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11456774/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring Security authentication logging
提问by Bryan Larson
I am using Spring Security 3.1 to authenticate users for a website. When a login fails because spring security is unable to connect to the database, I get the following statement in my log:
我正在使用 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!
My question is, why is this a DEBUG statement instead of an ERROR? I have to wade through a whole lot of debug statements just to find the actual error.
我的问题是,为什么这是 DEBUG 语句而不是 ERROR?我必须通过大量的调试语句才能找到实际的错误。
EDIT
编辑
Here is my authentication manager:
这是我的身份验证管理器:
<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>
回答by AlonL
My solution:
我的解决方案:
@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);
}
}
No other configurations are required.
不需要其他配置。
回答by sourcedelica
That message is printed out in AbstractAuthenticationProcessingFilter.unsuccessfulAuthentication:
该消息打印在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());
There are a number of ways an authentication can fail, including based on user input. For example, in AbstractUserDetailsAuthenticationProvider.authenticate, a BadCredentialsExceptioncould be thrown if the username is not found:
身份验证失败的方式有多种,包括基于用户输入。例如,在 中AbstractUserDetailsAuthenticationProvider.authenticate,BadCredentialsException如果找不到用户名,则可能会抛出a :
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;
}
}
Since there could be legitimate reasons why an authentication can fail, it doesn't make sense for AbstractAuthenticationProcessingFilterto log an error. If there is a system error the error should have been logged further downstream.
由于可能存在身份验证失败的合法原因,因此AbstractAuthenticationProcessingFilter记录错误是没有意义的。如果存在系统错误,则应在下游进一步记录该错误。
I suspect that the problem is in DaoAuthenticationProvider(see my comment inline):
我怀疑问题出在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);
}
Perhaps an error should be logged here - you can log a JIRA with Spring to request that. Though maybe they are assuming that everyone is going to provide a custom UserDetailsServiceand will catch/log their own exceptions there. If you are using JdbcDaoImplit does not. I think JdbcDaoImplis intended to be an example and is not robust. Per the docs:
也许应该在这里记录一个错误——你可以用 Spring 记录一个 JIRA 来请求它。尽管也许他们假设每个人都将提供自定义UserDetailsService并将在那里捕获/记录他们自己的异常。如果你正在使用JdbcDaoImpl它没有。我认为这JdbcDaoImpl是一个例子,并不健壮。根据文档:
The good news is that we provide a number of UserDetailsService implementations, including one that uses an in-memory map (InMemoryDaoImpl) and another that uses JDBC (JdbcDaoImpl). Most users tend to write their own, though, with their implementations often simply sitting on top of an existing Data Access Object (DAO) that represents their employees, customers, or other users of the application.
好消息是我们提供了许多 UserDetailsService 实现,包括一个使用内存映射 (InMemoryDaoImpl) 和另一个使用 JDBC (JdbcDaoImpl)。然而,大多数用户倾向于编写自己的实现,他们的实现通常只是位于代表他们的员工、客户或应用程序其他用户的现有数据访问对象 (DAO) 之上。

