Implementing a Remember me for Spring-Social

别等时光非礼了梦想. 提交于 2019-12-04 09:09:45

I have some similar requirement.

I tried to have the url for authentication as /auth/facebook?_spring_security_remember_me=true (refer AbstractRememberMeServices.rememberMeRequested). But then, org.springframework.social.security.SocialAuthenticationFilter.detectRejection wouldn't not let this pass. It is coded like this:

protected boolean detectRejection(HttpServletRequest request) {
    Set<?> parameterKeys = request.getParameterMap().keySet();
    return parameterKeys.size() > 0 
            && !parameterKeys.contains("oauth_token") 
            && !parameterKeys.contains("code") 
            && !parameterKeys.contains("scope");
}

I think if we can add another clause there, it could work. Then I thought to override it by subclassing SocialAuthenticationFilter. But then in SpringSocialConfigurer it's not injected, but instanciated using the new keyword. Subclassing SpringSocialConfigurer also doesn't seem like a solution, because there are many useful private fields inside. So, I think a solution would be to just copy SpringSocialConfigurer into another class, and use that along with a subclassed SocialAuthenticationFilter. If I have understood all this correctly, all this seems like hacking, and I think we should create a ticket for proper remember me support.

Of course, if we want remember me to be always on, that's easy by setting the alwaysRemember field of the RememberMeServices, and I am doing it this way:

@Bean
public RememberMeServices rememberMeServices() {

    TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices(rememberMeKey, userService);
    rememberMeServices.setAlwaysRemember(true);
    return rememberMeServices;

}

So here's what I did to integrate RememberMeServices with Spring social. Just like @Sanjay says, its based on AlwaysRemember=true logic.

Instead of default TokenBasedRememberMeServices, we customize a little:

public class DynamicRememberMeServices extends TokenBasedRememberMeServices {

public final static String PARAM_BASED_KEY = "remember-me-param-based"; 
public final static String REMEMBER_ME_KEY = "remember-me";

private Logger logger = LoggerFactory.getLogger(getClass());

public DynamicRememberMeServices(String key, UserDetailsService userDetailsService){
    super(key, userDetailsService);
    super.setParameter(REMEMBER_ME_KEY);
    super.setCookieName(REMEMBER_ME_KEY);
}

@Override
protected boolean rememberMeRequested(HttpServletRequest request, String parameter) {
    if("on".equalsIgnoreCase(request.getParameter(PARAM_BASED_KEY)) ){
        logger.debug("param based request");
        return super.rememberMeRequested(request, parameter);
    }
    logger.debug("always remember me");
    return true;
}

}

and on the login.html:

<form th:action="@{/login}" method="post">
   User Name : <input type="text" name="username"/> 
   Password: <input type="password" name="password"/>
   <input type="hidden" name="remember-me-param-based" value="on" /> 
   Remember me: <input type='checkbox' name='remember-me' checked="checked"/>
   <input type="hidden" name="_csrf" th:value="${_csrf.token}"/>
   <input type="submit" value="Sign In"/>
</form>

<a th:href="@{/auth/facebook}">Or via facebook</a>

So in case of facebook login(via /auth/facebook), it will use AlwaysRememberMe strategy by returning true, but for traditional form-login, it depends:

  • If remember-me-param-based=on from the request body, it will check continue as traditional TokenBasedRememberMeServices based on the given Parameter(remember-me).
  • If remember-me-param-based is missing, it works as AlwaysRememberMe.

IMHO my implementation at least gives the choice for "traditional login" users.

We switched to Java config and created the remember me service:

@Bean
public MyRememberMeServices myRememberMeServices(){
        MyRememberMeServices service = new MyRememberMeServices (REMEMBERME_KEY, formUserDetailsService);
        service.setAlwaysRemember(true);
        service.setCookieName("xxxx");
        service.setParameter("_spring_security_remember_me");
        service.setTokenValiditySeconds(123);
        return service;
    };

and then on the SignInAdapter implementation for the social login:

@Override
public String signIn(String userId, Connection<?> connection, NativeWebRequest request) {

    // load the user based on the account id

    // create an authentication object to store in context


    // set remember-me cookie
    myRememberMeServices.onLoginSuccess(
        (HttpServletRequest) request.getNativeRequest(),
        (HttpServletResponse) request.getNativeResponse(),
        authentication);

    // forward to the original URL
    return extractOriginalUrl(request);
}

To answer this question, the original way it was asked (with XML config), Spring Security 3.2+ supports the services-ref attribute on the remember-me element. So in your security config you would have:

<security:http xmlns="http://www.springframework.org/schema/security">
  ...
  <remember-me services-ref="rememberMeServices" key="secret-key">
</security:http>

<bean id="rememberMeServices" class="com.example.MyRememberMeServices">
  <constructor-arg index="0" value="secret-key" />
  <constructor-arg index="1" ref="userDetailsService" />
  <property name="tokenValiditySeconds" value="1000000" />
</bean>

where MyRememberMeServices can be checklist's version or beku8's DynamicRememberMeServices. I prefer beku8's version since it leaves the choice of enabling remember-me for normal logins.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!