1

我想在使用 Spring Security 的应用程序中使用 facebook 登录 + DB 身份验证。

所以我在 index.jsp 页面中添加了一个发送请求的 fb 按钮,例如,

function testAPI() {
    console.log('Welcome!  Fetching your information.... ');
    FB.api('/me', function(response) {
        console.log('Good to see you, ' + response.name + '.' + response.email);
        window.location = "<%=request.getContextPath()%>/j_spring_facebook_security_check";
    });
}

applicationContext-Security.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans">
    //spring security 3.
    <security:http auto-config="true" use-expressions="true" access-denied-page="/accessDenied.jsp">
        <security:form-login login-page="/index.jsp"
            default-target-url="/jsp/home.jsp"
            authentication-failure-handler-ref="authenticationFailureHandler"/>

        <security:intercept-url pattern="/jsp/listInBetweenPlaces.jsp"
            access="permitAll" />

        <security:intercept-url pattern="/jsp/home.jsp"
            access="permitAll" />

        <security:intercept-url pattern="/index.jsp"
            access="permitAll" />

        <security:intercept-url pattern="/jsp/*"
            access="isAuthenticated()" />

        <security:logout logout-url="/j_spring_security_logout" logout-success-url="/index.jsp?logout=success" 
            invalidate-session="true"  />

        <security:custom-filter before="FORM_LOGIN_FILTER"
            ref="facebookAuthenticationFilter" />
</security:http>

    <bean id="facebookAuthenticationFilter" class="org.springframework.security.facebook.FacebookAuthenticationFilter">
        <property name="authenticationManager" ref="authenticationManager"/>

        <property  name = "authenticationSuccessHandler">
            <bean  class = "org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler">
                <property  name = "defaultTargetUrl"  value = "/jsp/home.jsp"  />
                <property  name = "alwaysUseDefaultTargetUrl"  value = "true"  />
            </bean>
        </property>

        <property  name = "authenticationFailureHandler">
            <bean  class = "org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
                <property  name = "defaultFailureUrl"  value = "/fb/failure.jsp"  />
            </bean>
        </property>
    </bean>

    <bean id="authenticationProviderFacebook" class="org.springframework.security.facebook.FacebookAuthenticationProvider">
        <property name="roles" value="ROLE_FACEBOOK_USER" />
    </bean>

    <bean id="facebookHelper"  class="org.springframework.security.facebook.FacebookHelper">
        <property  name="apiKey"  value="my_api_key"  />
        <property  name="secret"  value="my_secret_key"  />
    </bean>

    <bean id="customUserDetailsService" class="com.onmobile.carpool.authentication.CustomUserDetailsService">
        <!--  <property name="carpoolService" ref="carpoolServiceImpl" /> -->
    </bean>

    <security:authentication-manager alias="authenticationManager">
        <security:authentication-provider user-service-ref="customUserDetailsService">
            <security:password-encoder hash="md5" />
        </security:authentication-provider>
        <security:authentication-provider ref="authenticationProviderFacebook">
        </security:authentication-provider>
    </security:authentication-manager>
</beans>

FacebookAuthenticationFilter.java:

public class FacebookAuthenticationFilter extends
                AbstractAuthenticationProcessingFilter implements
                ApplicationContextAware {

    public static final String DEFAULT_FILTER_PROCESS_URL = "/j_spring_facebook_security_check";
    private FacebookHelper facebookHelper = null ;
    private ApplicationContext ctx;
    protected FacebookAuthenticationFilter() {
        super(DEFAULT_FILTER_PROCESS_URL);
    }

    public Authentication attemptAuthentication(HttpServletRequest req,
                        HttpServletResponse res) throws AuthenticationException,
                        IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        if(facebookHelper == null){
            facebookHelper = (FacebookHelper) ctx
                    .getBean("facebookHelper");
        }

        Long uid = null;
        String sessionkey = null ;
        try {
            uid = facebookHelper.getLoggedInUserId(request, response);
            sessionkey = facebookHelper.lookupSessionKey(request);
        } catch (FacebookUserNotConnected e) {
            throw new AuthenticationCredentialsNotFoundException(
                                        "Facebook user not connected", e);
        }

        FacebookAuthenticationToken token = new FacebookAuthenticationToken(uid);
        token.setSessionkey(sessionkey);
        token.setDetails(authenticationDetailsSource.buildDetails(request));

        AuthenticationManager authenticationManager = getAuthenticationManager();
        Authentication authentication = authenticationManager
                                .authenticate(token);

        return authentication;
    }

    public void setApplicationContext(ApplicationContext ctx)
                        throws BeansException {
        this.ctx = ctx;
    }

    public FacebookHelper getFacebookHelper() {
        return facebookHelper;
    }

    public void setFacebookHelper(FacebookHelper facebookHelper) {
        this.facebookHelper = facebookHelper;
    }

}

我总是遇到这个电话的问题:

 uid = facebookHelper.getLoggedInUserId(request, response);

getLoggedInUserIdFacebookHelper.java 中的详细信息:

public Long getLoggedInUserId(HttpServletRequest request,
                    HttpServletResponse response) throws FacebookUserNotConnected {

    FacebookWebappHelper<Document> facebook = new FacebookWebappHelper<Document>(
                        request, response, apiKey, secret);
    Long uid = facebook.getUser();

    if (uid == null)
            throw new FacebookUserNotConnected(
                            "Facebook user id could not be obtained");
    return uid;
}

下面的行总是评估为 null 因为它抛出异常:

Long uid = facebook.getUser();

我从下面的链接中学习了 Facebook 相关课程,

https://code.google.com/p/spring-security-facebook/source/browse/trunk/src/?r=4#src%2Fmain%2Fjava%2Forg%2Fspringframework%2Fsecurity%2Ffacebook

我的需要是让用户可以通过 DB 检查或通过 FB 身份验证登录到我的应用程序。

我无法继续这样做,有人可以指出我缺少什么。我总是被重定向到 failure.jsp。我需要在 FB 帐户中启用什么吗?或者我将一些不正确的数据从 UI 传递到过滤器?

我无法在任何地方找到完整的教程,有人可以指出正确的链接。

4

1 回答 1

0

我在最近开发的一个应用程序中做了类似的事情。我研究过使用 Spring-Oauth 项目来执行 oauth 身份验证,但后来意识到 spring-oauth 要求您已经在应用程序中进行了身份验证,而我在网上找到的所有其他解决方案都已过时。

经过几天的努力,这就是我最终要做的。我在我的应用程序中使用 JSF 和 PrimeFaces,但我希望它能给你一个想法。该方法是最初使用 Facebook Javascript API 为您执行 Facebook 身份验证。Facebook 页面上有很多关于如何执行此操作的示例:

login.xhtml - Facebook API 部分

...
FB.Event.subscribe('auth.authResponseChange', function(response) {
  // Here we specify what we do with the response anytime this event occurs. 
  if (response.status === 'connected') {
    //a user has logged in to the app
    var responseToken = response.authResponse.accessToken;
    var responseUserId = response.authResponse.accessToken;
    loginJS([{name:'token', value: responseToken}, {name:'name', value: responseUserId}]);                      
  } 
});
...

在页面正文的某处还添加以下内容:

<p:remoteCommand name="loginJS" action="#{userBean.facebookLogin}" immediate="true"  process="@this" />;

p:remoteCommand调用托管 bean (userBean) facebookLogin 函数。此函数从请求中检索两个参数(userId、token)。在您的情况下,您需要做的就是从 javascript 调用您的“登录”servlet,并将这两个参数作为请求的一部分传递。

用户Bean.java

public String facebookLogin(){
    FacesContext context = FacesContext.getCurrentInstance();
    Map<String, String> map = context.getExternalContext().getRequestParameterMap();
    String userToken = map.get("token");
    String name = map.get("name");

    if(logger.isLoggable(Level.FINEST)){
        logger.finest("Login attempt via facebook. Token: "+ userToken);
    }

    Authentication auth = new FacebookTokenAuthentication(userToken, name);

    SecurityContextHolder.getContext().setAuthentication(auth);

    return "home?faces-redirect=true";
}

FacebookTokenAuthentication.java

public class FacebookTokenAuthentication implements Authentication {

    /**
     * 
     */
    private static final long serialVersionUID = 4218179259794454698L;
    private String token;
    private String name;

    public FacebookTokenAuthentication(String token, String name) {
        this.token = token;
        this.name = name;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return new ArrayList<GrantedAuthority>(0);
    }

    @Override
    public Object getCredentials() {
        return token;
    }

    @Override
    public Object getDetails() {
        return null;
    }

    @Override
    public Object getPrincipal() {
        return null;
    }

    @Override
    public boolean isAuthenticated() {
        return false;
    }

    @Override
    public void setAuthenticated(boolean isAuthenticated)
            throws IllegalArgumentException {
    }

    @Override
    public String getName() {
        return name;
    }
}

FacebookAuthenticationProvider.java

最后一部分是创建一个 facebook 身份验证提供程序。此身份验证提供程序将调用 facebook API 以获取用户信息并在您的数据库中查找匹配的用户。您可以在数据库中映射用户的 facebook id,也可以使用电子邮件地址来查找用户。对于这一部分,我使用 spring-social FacebookTemplate 来获取 facebook 个人资料信息。

public class FacebookAuthenticationProvider implements AuthenticationProvider {
    private static final Logger logger = Logger
            .getLogger(FacebookAuthenticationProvider.class.getName());

    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.security.authentication.AuthenticationProvider#
     * authenticate(org.springframework.security.core.Authentication)
     */
    @Override
    public Authentication authenticate(Authentication auth)
            throws AuthenticationException {
        Authentication resultAuth = null;
        if (logger.isLoggable(Level.FINEST)) {
            logger.finest("Attempting to authenticate user via the FacebookAuthenticationProvider.");
        }

        if (auth.isAuthenticated()) {
            return auth;
        }

        if (auth instanceof FacebookTokenAuthentication) {

            FacebookTokenAuthentication fbAuth = (FacebookTokenAuthentication) auth;
            String token = (fbAuth.getCredentials() != null) ? fbAuth
                    .getCredentials().toString() : null;

            if (logger.isLoggable(Level.FINEST)) {
                logger.finest("Creating facebook template. Token:" + token);
            }

            FacebookTemplate fbTemplate = new FacebookTemplate(token);

            FacebookProfile userProfile = fbTemplate.userOperations()
                    .getUserProfile();

            // LOOKUP THE USER IN YOUR DATABASE
            User user = //ADD CUSTOM USER LOOKUP HERE;

            resultAuth = new PreAuthenticatedAuthenticationToken(user, token);
            resultAuth.setAuthenticated(true);
        } else {

            throw new BadCredentialsException("Invalid credentials.");
        }

        return resultAuth;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.springframework.security.authentication.AuthenticationProvider#supports
     * (java.lang.Class)
     */
    @Override
    public boolean supports(Class<?> auth) {
        return auth.equals(FacebookTokenAuthentication.class);
    }

} 

不要忘记将 AuthenticationProvider 添加到您的 Spring 配置中

于 2014-02-01T20:12:28.313 回答