在 Spring 3.1 中使用记住我的功能登录用户

Posted

技术标签:

【中文标题】在 Spring 3.1 中使用记住我的功能登录用户【英文标题】:Log user in with remember-me functionality in Spring 3.1 【发布时间】:2011-12-10 01:15:05 【问题描述】:

我目前以编程方式登录用户(例如当他们通过 Facebook 或其他方式登录时,而不是使用我的登录表单):

SecurityContextHolder.getContext().setAuthentication(
  new UsernamePasswordAuthenticationToken(user, "", authorities)
);

我想要做的是让用户登录,就好像他们在登录表单中设置了“记住我”选项一样。所以我猜我需要使用RememberMeAuthenticationToken 而不是UsernamePasswordAuthenticationToken?但是构造函数的 key 参数我该放什么呢?

RememberMeAuthenticationToken(String key, Object principal, Collection<? extends GrantedAuthority> authorities) 

更新:我使用的是Persistent Token Approach described here。所以没有像 Simple Hash-Based Token Approach 那样的键。

【问题讨论】:

你的意思是只想在SecurityContextHolder中设置Authentication?还是您也需要设置记住我的 cookie? 我想登录此人,我的应用程序会记住他们,因此他们不必再次登录。 【参考方案1】:

这是构造函数的来源。

public RememberMeAuthenticationToken(String key, Object principal, Collection<? extends GrantedAuthority> authorities) 
    super(authorities);

    if ((key == null) || ("".equals(key)) || (principal == null) || "".equals(principal)) 
        throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
    

    this.keyHash = key.hashCode();
    this.principal = principal;
    setAuthenticated(true);

密钥经过哈希处理,用于确定在安全上下文中用于此用户的身份验证是否不是“伪造”的。

查看RememberMeAuthenicationProvider 来源。

public Authentication authenticate(Authentication authentication) throws AuthenticationException 
    if (!supports(authentication.getClass())) 
        return null;
    

    if (this.key.hashCode() != ((RememberMeAuthenticationToken) authentication).getKeyHash()) 
        throw new BadCredentialsException(messages.getMessage("RememberMeAuthenticationProvider.incorrectKey",
                "The presented RememberMeAuthenticationToken does not contain the expected key"));
    

    return authentication;

所以要回答您的问题,您需要传递代表用户的Authenticationkey 字段的哈希码

【讨论】:

我仍然不知道该怎么做..我如何获得代表用户的Authentication。然后我究竟传递了什么作为关键参数?【参考方案2】:

我假设您已经在配置中设置了&lt;remember-me&gt;

remember-me 的工作方式是设置一个 cookie,当用户在会话过期后返回网站时可以识别该 cookie。

您必须继承您正在使用的RememberMeServicesTokenBasedPersistentTokenBased)并将 onLoginSuccess() 公开。例如:

public class MyTokenBasedRememberMeServices extends PersistentTokenBasedRememberMeServices 
    @Override
    public void onLoginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) 
        super.onLoginSuccess(request, response, successfulAuthentication);
       
 

<remember-me services-ref="rememberMeServices"/>

<bean id="rememberMeServices" class="foo.MyTokenBasedRememberMeServices">
    <property name="userDetailsService" ref="myUserDetailsService"/>
    <!-- etc -->
</bean>

将您的 RememberMeServices 注入到您正在执行编程登录的 bean 中。然后使用您创建的 UsernamePasswordAuthenticationToken 在其上调用 onLoginSuccess()。这将设置 cookie。

UsernamePasswordAuthenticationToken auth = 
    new UsernamePasswordAuthenticationToken(user, "", authorities);
SecurityContextHolder.getContext().setAuthentication(auth);
getRememberMeServices().onLoginSuccess(request, response, auth);  

更新

@at 对此进行了改进,没有 RememberMeServices: 的子类化

UsernamePasswordAuthenticationToken auth = 
    new UsernamePasswordAuthenticationToken(user, "", authorities);
SecurityContextHolder.getContext().setAuthentication(auth);

// This wrapper is important, it causes the RememberMeService to see
// "true" for the "_spring_security_remember_me" parameter.
HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request) 
    @Override public String getParameter(String name)  return "true";             
;

getRememberMeServices().loginSuccess(wrapper, response, auth);  

【讨论】:

在我的安全 XML 配置中,我只是在 &lt;http&gt; 元素中添加了 &lt;remember-me token-validity-seconds="31536000" data-source-ref="dataSource"/&gt;TokenBasedRememberMeServices bean 是在哪里定义的?使用这种机制,我是否不需要再搞乱 SecurityContextHolder 了?一般来说,以编程方式登录某人听起来是一个更好的解决方案。 我假设我需要PersistentTokenBasedRememberMeServices 而不是TokenBasedRememberMeServices。但无论哪种方式,如果我可以将它注入到我试图用记住我功能登录某人的控制器中,loginSuccess() 似乎不是我想要的。 Javadocs 说:“检查传入请求并检查是否存在配置的“记住我”参数。如果存在,或者如果 alwaysRemember 设置为 true,则调用 onLoginSucces。” 您使用的是哪个版本的 Spring Security?我指的是 org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices 3.0。 您可以对TokenBasedRememberMeServices 的永久或常规版本执行此操作。我刚刚检查了 3.1 的源(对不起,我错过了),它是一样的。 无需扩展类或使用包装器。只需在 PersistentTokenBasedRememberMeServices 上设置 alwaysRemember=true。不幸的是,它似乎无法通过 XML 元素完成,因此您需要定义 bean PersistentTokenBasedRememberMeServices 并将该属性设置为 true。之后正常的 rememberMeServices.loginSuccess(req, res, auth) 创建 cookie 和持久记录

以上是关于在 Spring 3.1 中使用记住我的功能登录用户的主要内容,如果未能解决你的问题,请参考以下文章

模拟淘宝登录和购物车功能:使用cookie记录登录名,下次登录时能够记得上次的登录名,使用cookie模拟购物车功能,使用session记住登录信息并验证是否登录,防止利用url打开网站,并实现退出登

Spring-security 记住我的功能不起作用

Spring Security---记住我功能详解

Spring Security LDAP 和记住我

Spring WebFlux + Security - 我们有“记住我”功能吗?

通过facebook登录后设置spring security记住我的cookie