【问题标题】:spring security and facebook authentication not working春季安全和Facebook身份验证不起作用
【发布时间】:2013-03-10 09:47:30
【问题描述】:

我想在使用 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);

FacebookHelper.java 中getLoggedInUserId 的详细信息:

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 传递到过滤器?

我无法在任何地方找到完整的教程,谁能指出正确的链接。

【问题讨论】:

    标签: facebook spring spring-security


    【解决方案1】:

    我在最近开发的一款应用中做了类似的事情。我研究过使用 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,并将这两个参数作为请求的一部分传递。

    UserBean.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 配置中

    【讨论】:

      猜你喜欢
      • 2015-12-16
      • 2017-08-29
      • 2013-02-24
      • 1970-01-01
      • 2022-07-22
      • 2019-03-31
      • 2016-08-20
      • 2017-03-08
      • 1970-01-01
      相关资源
      最近更新 更多