I have a spring security enabled project. I have implemented a remember me using the login form which works fine. But I also have on the login form social logins from google/ facebook which work fine. The problem is that they do not remember the user. Is there a way to set a similar "remember me" functionality?
My current spring config for the normal login page:
<http access-denied-page="/login?authorization_error=true"
disable-url-rewriting="true" authentication-manager-ref="formAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/account/**" access="ROLE_USER" />
<remember-me key="iRemember"
token-validity-seconds="1209600" user-service-ref="formClientDetailsUserService" />
<form-login authentication-failure-url="/login?authentication_error=true"
default-target-url="/account/" login-page="/login"
login-processing-url="/login.do" />
<logout logout-success-url="http://example.com" logout-url="/logout" />
<anonymous />
</http>
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 traditionalTokenBasedRememberMeServices
based on the given Parameter(remember-me
). - If
remember-me-param-based
is missing, it works asAlwaysRememberMe
.
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.
来源:https://stackoverflow.com/questions/25565809/implementing-a-remember-me-for-spring-social