Spring Facebook 登录:注册重定向
Spring facebook login: signup redirect
场景 1
当使用 facebook 登录到我的 webapp
如果浏览器已经登录Facebook,并且
如果用户的社交身份验证详细信息已在我的 webapp 中注册,
然后当用户在我的网络应用程序上点击 "Sign in with Facebook" 时,
he/she 已获得 Facebook 授权,并且
返回的授权对象
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
是指定用户。
一切顺利
情景 2
如果浏览器未登录 Facebook 但
用户的社交身份验证详细信息已在我的网络应用程序中注册,
然后当用户在我的网络应用程序上点击 "Sign in with Facebook" 时,
用户通过输入用户名和密码登录 Facebook 但是
返回的授权对象
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
被授权为 anonymous 并且名称为 anonymousUser.
另外,Facebook授权后调用的是/signup
但是,我希望注册用户与方案 1 中的注册用户相同
我的安全配置如下。谁能告诉我问题出在哪里?
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
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.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<security:http use-expressions="true" entry-point-ref="appAuthenticationEntryPoint">
<security:intercept-url pattern="/login" access="permitAll()" />
<security:intercept-url pattern="/flow-entry.html" access="permitAll()"/>
<security:intercept-url pattern="/flow-jobpostdata.html" access="permitAll()"/>
<security:intercept-url pattern="/flow-jobpostdata_anydegree.html" access="permitAll()"/>
<security:intercept-url pattern="/j_spring_security_check" access="permitAll()"/>
<!-- Adds social authentication filter to the Spring Security filter chain. -->
<security:custom-filter before="PRE_AUTH_FILTER" ref="socialAuthenticationFilter"/>
<security:custom-filter position="FORM_LOGIN_FILTER" ref="SecurityAuthFilter"/>
</security:http>
<!-- authentication manager and its provider( social provider deals with social login & local user provider deals with form login ) -->
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="socialAuthenticationProvider"/>
<security:authentication-provider user-service-ref="localUserDetailService"/>
</security:authentication-manager>
<bean id="customAuthenticationProvider" class="com.ikoda.service.loginservices.CustomAuthenticationProvider">
<property name="auService" ref="auService" />
</bean>
<bean id="socialAuthenticationProvider" class="org.springframework.social.security.SocialAuthenticationProvider">
<constructor-arg ref="inMemoryUsersConnectionRepository"/>
<constructor-arg ref="socialUserDetailService"/>
</bean>
<!-- form login beans -->
<bean id="successHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
</bean>
<bean id="appAuthenticationEntryPoint"
class="com.ikoda.service.loginservices.AppAuthenticationEntryPoint">
<constructor-arg name="loginFormUrl" value="/login"/>
</bean>
<bean id="rememberMeServices"
class="org.springframework.security.web.authentication.NullRememberMeServices"/>
<bean id="failureHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<constructor-arg name="defaultFailureUrl" value="/login?error=true"/>
</bean>
<bean class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
id="SecurityAuthFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="successHandler"/>
<property name="authenticationFailureHandler" ref="failureHandler"/>
<property name="filterProcessesUrl" value="/j_spring_security_check"/>
<property name="rememberMeServices" ref="rememberMeServices"/>
</bean>
<!-- social login filter which is a pre authentication filter and works for /auth service url -->
<bean id="socialAuthenticationFilter" class="org.springframework.social.security.SocialAuthenticationFilter">
<constructor-arg name="authManager" ref="authenticationManager"/>
<constructor-arg name="userIdSource" ref="userIdSource"/>
<constructor-arg name="usersConnectionRepository" ref="inMemoryUsersConnectionRepository"/>
<constructor-arg name="authServiceLocator" ref="appSocialAuthenticationServiceRegistry"/>
<property name="authenticationSuccessHandler" ref="successHandler"/>
</bean>
<!-- inmemory connection repository which holds connection repository per local user -->
<bean id="inMemoryUsersConnectionRepository"
class="org.springframework.social.connect.mem.InMemoryUsersConnectionRepository">
<constructor-arg name="connectionFactoryLocator" ref="appSocialAuthenticationServiceRegistry"/>
<property name="connectionSignUp" ref="connectionSignUp"/>
</bean>
<!-- service registry will holds connection factory of each social provider-->
<bean id="appSocialAuthenticationServiceRegistry"
class="com.ikoda.service.loginservices.AppSocialAuthenticationServiceRegistry">
<constructor-arg>
<list>
<ref bean="facebookAuthenticationService"/>
<ref bean="googleAuthenticationService"/>
</list>
</constructor-arg>
</bean>
<bean id="facebookAuthenticationService"
class="org.springframework.social.facebook.security.FacebookAuthenticationService">
<constructor-arg name="apiKey" value="11111"/>
<constructor-arg name="appSecret" value="11111"/>
</bean>
<bean id="googleAuthenticationService"
class="org.springframework.social.google.security.GoogleAuthenticationService">
<constructor-arg name="apiKey" value="111-lpmhcmuj1577bd6god0696g4u2g16c1i.apps.googleusercontent.com"/>
<constructor-arg name="appSecret" value="111-111-"/>
</bean>
<bean id="userIdSource" class="org.springframework.social.security.AuthenticationNameUserIdSource"/>
<!-- If no local user is associated to a social connection then connection sign up will create a new local user and map it to social user -->
<bean id="connectionSignUp" class="com.ikoda.service.loginservices.AppConnectionSignUp"/>
</beans>
重定向到 signUp 是 Spring Social 的预期行为。关键是如何管理行为。
解决问题的一种方法是在控制器注册方法中验证社交登录。为此,需要访问社交关系。您可以通过在您的控制器中自动装配 ProviderSignInUtils 来做到这一点。
注意 AppConnectionSignUP 是 org.springframework.social.connect.ConnectionSignUp
的一些实现
Connection<?> connection = providerSignInUtils.getConnectionFromSession(webRequest);
if (null != connection)
{
String userIdString = appConnectionSignUp.execute(connection);
}
另一种解决问题的方法是实现您自己的 ConnectionSignUp
<bean id="connectionSignUp" class="com.ikoda.service.loginservices.AppConnectionSignUp"/>
并在您的 UsersConnectionRepository
中引用它
<bean id="jdbcUsersConnectionRepository"
class="org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository">
<constructor-arg ref="dataSource" />
<constructor-arg ref="appSocialAuthenticationServiceRegistry" />
<constructor-arg ref="textEncryptor" />
<property name="connectionSignUp" ref="connectionSignUp"/>
</bean>
如果采用这种方式,那么社交登录会绕过Controller /signUp,直接调用connectionSignUP bean的execute方法。
同时,在上面的问题中,inMemoryUsersConnectionRepository 是不明智的。我切换到 jdbcUsersConnectionRepository。
场景 1 当使用 facebook 登录到我的 webapp
如果浏览器已经登录Facebook,并且
如果用户的社交身份验证详细信息已在我的 webapp 中注册,
然后当用户在我的网络应用程序上点击 "Sign in with Facebook" 时,
he/she 已获得 Facebook 授权,并且
返回的授权对象Authentication auth = SecurityContextHolder.getContext().getAuthentication();
是指定用户。
一切顺利
情景 2 如果浏览器未登录 Facebook 但
用户的社交身份验证详细信息已在我的网络应用程序中注册,
然后当用户在我的网络应用程序上点击 "Sign in with Facebook" 时,
用户通过输入用户名和密码登录 Facebook 但是
返回的授权对象Authentication auth = SecurityContextHolder.getContext().getAuthentication();
被授权为 anonymous 并且名称为 anonymousUser.
另外,Facebook授权后调用的是/signup
但是,我希望注册用户与方案 1 中的注册用户相同
我的安全配置如下。谁能告诉我问题出在哪里?
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
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.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<security:http use-expressions="true" entry-point-ref="appAuthenticationEntryPoint">
<security:intercept-url pattern="/login" access="permitAll()" />
<security:intercept-url pattern="/flow-entry.html" access="permitAll()"/>
<security:intercept-url pattern="/flow-jobpostdata.html" access="permitAll()"/>
<security:intercept-url pattern="/flow-jobpostdata_anydegree.html" access="permitAll()"/>
<security:intercept-url pattern="/j_spring_security_check" access="permitAll()"/>
<!-- Adds social authentication filter to the Spring Security filter chain. -->
<security:custom-filter before="PRE_AUTH_FILTER" ref="socialAuthenticationFilter"/>
<security:custom-filter position="FORM_LOGIN_FILTER" ref="SecurityAuthFilter"/>
</security:http>
<!-- authentication manager and its provider( social provider deals with social login & local user provider deals with form login ) -->
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="socialAuthenticationProvider"/>
<security:authentication-provider user-service-ref="localUserDetailService"/>
</security:authentication-manager>
<bean id="customAuthenticationProvider" class="com.ikoda.service.loginservices.CustomAuthenticationProvider">
<property name="auService" ref="auService" />
</bean>
<bean id="socialAuthenticationProvider" class="org.springframework.social.security.SocialAuthenticationProvider">
<constructor-arg ref="inMemoryUsersConnectionRepository"/>
<constructor-arg ref="socialUserDetailService"/>
</bean>
<!-- form login beans -->
<bean id="successHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
</bean>
<bean id="appAuthenticationEntryPoint"
class="com.ikoda.service.loginservices.AppAuthenticationEntryPoint">
<constructor-arg name="loginFormUrl" value="/login"/>
</bean>
<bean id="rememberMeServices"
class="org.springframework.security.web.authentication.NullRememberMeServices"/>
<bean id="failureHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<constructor-arg name="defaultFailureUrl" value="/login?error=true"/>
</bean>
<bean class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
id="SecurityAuthFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="successHandler"/>
<property name="authenticationFailureHandler" ref="failureHandler"/>
<property name="filterProcessesUrl" value="/j_spring_security_check"/>
<property name="rememberMeServices" ref="rememberMeServices"/>
</bean>
<!-- social login filter which is a pre authentication filter and works for /auth service url -->
<bean id="socialAuthenticationFilter" class="org.springframework.social.security.SocialAuthenticationFilter">
<constructor-arg name="authManager" ref="authenticationManager"/>
<constructor-arg name="userIdSource" ref="userIdSource"/>
<constructor-arg name="usersConnectionRepository" ref="inMemoryUsersConnectionRepository"/>
<constructor-arg name="authServiceLocator" ref="appSocialAuthenticationServiceRegistry"/>
<property name="authenticationSuccessHandler" ref="successHandler"/>
</bean>
<!-- inmemory connection repository which holds connection repository per local user -->
<bean id="inMemoryUsersConnectionRepository"
class="org.springframework.social.connect.mem.InMemoryUsersConnectionRepository">
<constructor-arg name="connectionFactoryLocator" ref="appSocialAuthenticationServiceRegistry"/>
<property name="connectionSignUp" ref="connectionSignUp"/>
</bean>
<!-- service registry will holds connection factory of each social provider-->
<bean id="appSocialAuthenticationServiceRegistry"
class="com.ikoda.service.loginservices.AppSocialAuthenticationServiceRegistry">
<constructor-arg>
<list>
<ref bean="facebookAuthenticationService"/>
<ref bean="googleAuthenticationService"/>
</list>
</constructor-arg>
</bean>
<bean id="facebookAuthenticationService"
class="org.springframework.social.facebook.security.FacebookAuthenticationService">
<constructor-arg name="apiKey" value="11111"/>
<constructor-arg name="appSecret" value="11111"/>
</bean>
<bean id="googleAuthenticationService"
class="org.springframework.social.google.security.GoogleAuthenticationService">
<constructor-arg name="apiKey" value="111-lpmhcmuj1577bd6god0696g4u2g16c1i.apps.googleusercontent.com"/>
<constructor-arg name="appSecret" value="111-111-"/>
</bean>
<bean id="userIdSource" class="org.springframework.social.security.AuthenticationNameUserIdSource"/>
<!-- If no local user is associated to a social connection then connection sign up will create a new local user and map it to social user -->
<bean id="connectionSignUp" class="com.ikoda.service.loginservices.AppConnectionSignUp"/>
</beans>
重定向到 signUp 是 Spring Social 的预期行为。关键是如何管理行为。
解决问题的一种方法是在控制器注册方法中验证社交登录。为此,需要访问社交关系。您可以通过在您的控制器中自动装配 ProviderSignInUtils 来做到这一点。
注意 AppConnectionSignUP 是 org.springframework.social.connect.ConnectionSignUp
的一些实现Connection<?> connection = providerSignInUtils.getConnectionFromSession(webRequest);
if (null != connection)
{
String userIdString = appConnectionSignUp.execute(connection);
}
另一种解决问题的方法是实现您自己的 ConnectionSignUp
<bean id="connectionSignUp" class="com.ikoda.service.loginservices.AppConnectionSignUp"/>
并在您的 UsersConnectionRepository
中引用它 <bean id="jdbcUsersConnectionRepository"
class="org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository">
<constructor-arg ref="dataSource" />
<constructor-arg ref="appSocialAuthenticationServiceRegistry" />
<constructor-arg ref="textEncryptor" />
<property name="connectionSignUp" ref="connectionSignUp"/>
</bean>
如果采用这种方式,那么社交登录会绕过Controller /signUp,直接调用connectionSignUP bean的execute方法。
同时,在上面的问题中,inMemoryUsersConnectionRepository 是不明智的。我切换到 jdbcUsersConnectionRepository。