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。