使用 Spring Security 成功登录后如何正确更新登录日期时间?
Posted
技术标签:
【中文标题】使用 Spring Security 成功登录后如何正确更新登录日期时间?【英文标题】:How to correctly update the login date time after successful login with Spring security? 【发布时间】:2013-03-07 18:11:57 【问题描述】:我正在使用 Spring 3.2.0 和相同版本的 Spring 安全性。成功登录后,用户将被重定向到以下受保护页面之一。
public final class LoginSuccessHandler implements AuthenticationSuccessHandler
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException
Set<String> roles = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
if (roles.contains("ROLE_ADMIN"))
response.sendRedirect("admin_side/Home.htm");
return;
我正在使用休眠。如何在成功登录时更新数据库中的登录日期时间(上次登录)?我在登录页面上有一个提交按钮,其POST
请求似乎没有映射到其相应登录控制器中的方法。登录表单的操作实际上映射到 Servlet - j_spring_security_check
。
如果需要的话,整个spring-security.xml
文件如下。
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<http pattern="/Login.htm*" security="none"></http>
<http auto-config='true'>
<!--<remember-me key="myAppKey"/>-->
<session-management session-fixation-protection="newSession">
<concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</session-management>
<intercept-url pattern="/admin_side/**" access="ROLE_ADMIN" requires-channel="any"/>
<form-login login-page="/" default-target-url="/admin_side/Home.htm" authentication-failure-url="/LoginFailed.htm" authentication-success-handler-ref="loginSuccessHandler"/>
<logout logout-success-url="/Login.htm" invalidate-session="true" delete-cookies="JSESSIONID"/>
</http>
<authentication-manager>
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select email_id, password, enabled from user_table where lower(email_id)=lower(?)"
authorities-by-username-query="select ut.email_id, ur.authority from user_table ut, user_roles ur where ut.user_id=ur.user_id and lower(ut.email_id)=lower(?)"/>
</authentication-provider>
</authentication-manager>
<beans:bean id="loginSuccessHandler" class="loginsuccesshandler.LoginSuccessHandler"/>
<global-method-security>
<protect-pointcut expression="execution(* dao.*.*(..))" access="ROLE_ADMIN"/>
</global-method-security>
<!--<global-method-security secured-annotations="enabled" />-->
</beans:beans>
【问题讨论】:
【参考方案1】:另一种方法是为AuthenticationSuccessEvent
注册一个处理程序。
@Service
public class UserService implements
ApplicationListener<AuthenticationSuccessEvent>
@Override
public void onApplicationEvent(AuthenticationSuccessEvent event)
String userName = ((UserDetails) event.getAuthentication().
getPrincipal()).getUsername();
User user = this.userDao.findByLogin(userName);
user.setLastLoginDate(new Date());
【讨论】:
这需要一些 XML 配置吗?当我尝试时,从未调用过onApplicationEvent()
方法。此外,这个userDao
对象在这里如何可用?在这种情况下,我不知道这个东西实际上是在哪里实现的,而不是它是如何实现的。
通过this 回答,我已在applicationContext.xml
文件中将其注册为bean 并触发了事件,但我不确定如何使userDao
可用。使用@Autowied
?当我尝试自动接线时,我收到了 - An Authentication object was not found in the SecurityContext
你如何注入你的 dao 不是这个答案的范围,但@Autowire 应该可以工作。 “在 SecurityContext 中未找到 Authentication 对象”您是否尝试注入它?
我正在尝试在实现 ApplicationListener<AuthenticationSuccessEvent>
的类中像 - @Autowired private UserSevice userSevice;
一样自动装配。
当我输入像System.out.println("userName = "+userSevice.getUser(userName).getFirstName());
这样的调试语句时,一旦按下提交按钮,它就会失败并显示此消息 - An Authentication object was not found in the SecurityContext
。当我删除此语句时,该事件被触发并且它起作用。自动装配似乎不起作用。【参考方案2】:
为什么不直接在认证成功处理程序中做呢?
public final class LoginSuccessHandler implements AuthenticationSuccessHandler
@Autowired
private UserService userService;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException
String userName = authentication.getPrincipal().getName();
this.userService.updateLastLoginDateForUserByName(userName);
Set<String> roles = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
if (roles.contains("ROLE_ADMIN"))
response.sendRedirect("admin_side/Home.htm");
return;
【讨论】:
这种方法有效,但在尝试将UserService
接口自动连接为我更感兴趣的拉尔夫答案时,它无法与ApplicationListener<AuthenticationSuccessEvent>
一起使用,指示-An Authentication object was not found in the SecurityContext
。你知道为什么吗?
奇怪。在 SecurityContextHolder 类(clearContext()、getContext() 等)中设置断点并调试它
@Tiny,Ralph 的解决方案也有同样的问题。 (我是新手!)
@Eric :我无法澄清,但AuthenticationSuccessHandler
就足够了(如this answer所示),如果需要在登录成功后立即执行某些操作(我更想知道为什么成功登录后ApplicationListener<AuthenticationSuccessEvent>
不起作用,正如我的this 问题所示)。【参考方案3】:
您还可以继承 spring AuthenticationProvider
接口并将其注入到 <authentication-manager />
元素中。
类会是这样的
public class AuthenticationProvider extends DaoAuthenticationProvider
// inject whatever tou want
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException
return super.authenticate(authentication);
// do what ever you want here
(假设您使用的是DaoAuthenticationProvider
)
那么你只需要注册 bean
<bean class="x.y.z.AuthenticationProvider" id="myAuthProvider" scope="singleton" />
<authentication-manager>
<authentication-provider ref="myAuthProvider">
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select email_id, password, enabled from user_table where lower(email_id)=lower(?)"
authorities-by-username-query="select ut.email_id, ur.authority from user_table ut, user_roles ur where ut.user_id=ur.user_id and lower(ut.email_id)=lower(?)"/>
</authentication-provider>
</authentication-manager>
(不要相信代码的正确性,我是临时写的。只是为了展示我的想法。)
斯特凡诺
【讨论】:
以上是关于使用 Spring Security 成功登录后如何正确更新登录日期时间?的主要内容,如果未能解决你的问题,请参考以下文章
使用 Spring Security 成功登录后如何正确更新登录日期时间?
使用spring security成功登录后如何将对象添加到视图中?