2015-10-13 229 views
2

根據manual我實現了連接到Facebook圖形API並從用戶檢索信息。問題是當我想放棄與Facebook訪問(登錄),我得到這個錯誤:Spring Oauth2登錄 - 匿名不允許?

org.springframework.security.authentication.InsufficientAuthenticationException: Authentication is required to obtain an access token (anonymous not allowed) 
    at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:88) ~[spring-security-oauth2-2.0.7.RELEASE.jar:na] 
    at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221) ~[spring-security-oauth2-2.0.7.RELEASE.jar:na] 
    at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173) ~[spring-security-oauth2-2.0.7.RELEASE.jar:na] 
    at org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:105) ~[spring-security-oauth2-2.0.7.RELEASE.jar:na] 
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:565) ~[spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE] 
    at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:128) ~[spring-security-oauth2-2.0.7.RELEASE.jar:na] 
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:530) ~[spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE] 
    at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:237) ~[spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE] 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_25] 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_25] 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_25] 
    at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0_25] 
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317) ~[spring-aop-4.1.6.RELEASE.jar:4.1.6.RELEASE] 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.1.6.RELEASE.jar:4.1.6.RELEASE] 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.1.6.RELEASE.jar:4.1.6.RELEASE] 
    at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133) ~[spring-aop-4.1.6.RELEASE.jar:4.1.6.RELEASE] 
    at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121) ~[spring-aop-4.1.6.RELEASE.jar:4.1.6.RELEASE] 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.1.6.RELEASE.jar:4.1.6.RELEASE] 
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) ~[spring-aop-4.1.6.RELEASE.jar:4.1.6.RELEASE] 
    at com.sun.proxy.$Proxy79.getForObject(Unknown Source) ~[na:na] 
    at com.xxxx.xxxx.controllers.FacebookController.login(FacebookController.java:39) ~[classes/:na] 

錯誤明確表示「匿名用戶沒有允許獲得訪問令牌」。當然用戶是匿名的,並且正在嘗試進行身份驗證。那我該怎麼做呢?也許我在控制器上做的不是正確的登錄方式?

控制器:

@RequestMapping("/facebookLogin") 
@ResponseBody 
public User login() { 
    ObjectNode result = facebookRestTemplate 
      .getForObject(
        "https://graph.facebook.com/me/?fields=name,email,third_party_id", 
        ObjectNode.class); 
    ... 
} 

我使用spring-security:4.0.1.RELEASEspring-security-oauth2:2.0.7.RELEASE

注:在這種情況下我使用Facebook,但也應該出現與谷歌,Twitter等問題

+0

您好,請驗證我的答案,如果它可以幫助您 – vine

回答

0

的您共享的代碼段不會顯示登錄信息,只會在您嘗試檢索有關用戶的信息時顯示。

根據Spring文檔,您需要調整客戶端以使其能夠與FB一起工作。

Some external OAuth2 providers (e.g. Facebook) do not quite implement the specification correctly, or else they are just stuck on an older version of the spec than Spring Security OAuth. To use those providers in your client application you might need to adapt various parts of the client-side infrastructure.

To use Facebook as an example, there is a Facebook feature in the tonr2 application (you need to change the configuration to add your own, valid, client id and secret - they are easy to generate on the Facebook website).

還有就是關於如何做到這一點在Servlet頁Facebook Connect example in JSP (tomcat)

在這裏,另一個線程響應是另一種解決方案對如何做到這一點的一個MVC實現一個副本:

public ActionResult Authenticate() 
     { 
       var oauthFacebook = new FacebookOAuth(); 
       if (Request["code"] == null) 
       { 
        //Redirect the user to Facebook for authorization. 
        Response.Redirect(oauthFacebook.AuthorizationLinkGet()); 
       } 
       else 
       { 
        //Get the access token and secret. 
        oauthFacebook.AccessTokenGet(Request["code"]); 
        if (oauthFacebook.Token.Length > 0) 
        { 
         //We can now make our api calls 
         var user = oauthFacebook.GetAttributes(); 
        } 
       } 
     } 

public class FacebookOAuth : Oauth 
    { 
     public FacebookOAuth() 
     { 
      Authorize = "https://graph.facebook.com/oauth/authorize"; 
      AccessToken = "https://graph.facebook.com/oauth/access_token"; 
      CallbackUrl = "http://<YourURLHere>/Authenticate"; 
      AttributesBaseUrl = "https://graph.facebook.com/me/?access_token="; 
      ConsumerKey = ConfigurationManager.AppSettings["FacebookConsumerKey"];//Ur Consumer Key goes here 
      ConsumerSecret = ConfigurationManager.AppSettings["FacebookConsumerSecret"];//Ur Consumer secret goes here 
      Provider = "Facebook"; 
     } 

     public override string AuthorizationLinkGet() 
     { 
      return 
       string.Format(
        "{0}?client_id={1}&redirect_uri={2}&scope=email,user_education_history,user_location,user_hometown", 
        Authorize, ConsumerKey, CallbackUrl); 
     } 

     public User GetAttributes() 
     { 
      string attributesUrl = string.Format("{0}{1}", AttributesBaseUrl, Token); 
      string attributes = WebRequest(Method.Get, attributesUrl, String.Empty); 
      var FacebookUser = new JavaScriptSerializer().Deserialize<FacebookUser>(attributes); 
      return new User() 
      { 
       FirstName = FacebookUser.first_name, 
       MiddleName = FacebookUser.middle_name, 
       LastName = FacebookUser.last_name, 
       Locale = FacebookUser.locale, 
       UserEmail = FacebookUser.email, 
       AuthProvider = Provider, 
       AuthToken=Token 
      }; 
     } 
    } 
public abstract class Oauth 
    { 
     #region Method enum 

     public enum Method 
     { 
      Get, 
      Post, 
      Delete 
     } ; 

     #endregion 

     protected string AccessToken; 
     protected string AttributesBaseUrl; 
     protected string Authorize; 
     protected string CallbackUrl; 
     protected string ConsumerKey; 
     protected string ConsumerSecret; 
     public string Provider { get; protected set; } 

     public string Token { get; set; } 

     public virtual string AuthorizationLinkGet() 
     { 
      return 
       string.Format(
        "{0}?client_id={1}&redirect_uri={2}&scope=publish_stream,email,user_education_history,user_location", 
        Authorize, ConsumerKey, CallbackUrl); 
     } 

     public void AccessTokenGet(string authToken) 
     { 
      Token = authToken; 
      string accessTokenUrl = string.Format("{0}?client_id={1}&redirect_uri={2}&client_secret={3}&code={4}", 
                AccessToken, ConsumerKey, CallbackUrl, ConsumerSecret, authToken); 
      string response = WebRequest(Method.Get, accessTokenUrl, String.Empty); 

      if (response.Length > 0) 
      { 
       //Store the returned access_token 
       NameValueCollection qs = HttpUtility.ParseQueryString(response); 

       if (qs["access_token"] != null) 
       { 
        Token = qs["access_token"]; 
       } 
      } 
     } 

     public string WebRequest(Method method, string url, string postData) 
     { 
      StreamWriter requestWriter; 
      string responseData = string.Empty; 

      var webRequest = System.Net.WebRequest.Create(url) as HttpWebRequest; 
      if (webRequest != null) 
      { 
       webRequest.Method = method.ToString(); 
       webRequest.ServicePoint.Expect100Continue = false; 
       webRequest.Timeout = 20000; 

       if (method == Method.Post) 
       { 
        webRequest.ContentType = "application/x-www-form-urlencoded"; 
        //POST the data. 
        requestWriter = new StreamWriter(webRequest.GetRequestStream()); 
        try 
        { 
         requestWriter.Write(postData); 
        } 

        finally 
        { 
         requestWriter.Close(); 
        } 
       } 
       responseData = WebResponseGet(webRequest); 
      } 
      return responseData; 
     } 

     public string WebResponseGet(HttpWebRequest webRequest) 
     { 
      StreamReader responseReader = null; 
      string responseData; 
      try 
      { 
       responseReader = new StreamReader(webRequest.GetResponse().GetResponseStream()); 
       responseData = responseReader.ReadToEnd(); 
      } 
      finally 
      { 
       if (webRequest != null) webRequest.GetResponse().GetResponseStream().Close(); 
       if (responseReader != null) responseReader.Close(); 
      } 
      return responseData; 
     } 
    } 

希望它有幫助!

+0

關鍵是,我怎樣才能檢索「最小」的信息來驗證/認證用戶在我的webapp使用'spring-security-oauth2'庫,我做了什麼'Torn2'確實(取回信息),但我只允許,如果我通過'spring-security'進行身份驗證。正如我所說無論是Facebook還是其他提供者。如果我必須重新實現每一件事情才能登錄,那麼使用'spring-security-oauth2'是沒有意義的。如果我表達得很好,我不會。謝謝你的回答 – xedo

+0

我明白你在說什麼,但壞事是facebook不符合oauth2規範。這就是爲什麼你必須重新實現這一點,我知道你想要一個處理它的標準方式,但除非FB改變(我認爲他們不會),我們堅持這一點。 –

+0

與谷歌相同的情況給我同樣的異常'InsufficientAuthenticationException' – xedo

0

嗨檢查這個代碼在tonr2項目

https://github.com/spring-projects/spring-security-oauth/blob/master/samples/oauth2/tonr/src/main/java/org/springframework/security/oauth/examples/config/WebMvcConfig.java

@Bean 
    public OAuth2ProtectedResourceDetails facebook() { 
     AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails(); 
     details.setId("facebook"); 
     details.setClientId("233668646673605"); 
     details.setClientSecret("33b17e044ee6a4fa383f46ec6e28ea1d"); 
     details.setAccessTokenUri("https://graph.facebook.com/oauth/access_token"); 
     details.setUserAuthorizationUri("https://www.facebook.com/dialog/oauth"); 
     details.setTokenName("oauth_token"); 
     details.setAuthenticationScheme(AuthenticationScheme.query); 
     details.setClientAuthenticationScheme(AuthenticationScheme.form); 
     return details; 
    } 

添加你的Facebook相應的OAuth2ProtectedResourceDetails憑據。 然後,這個bean將被Spring Security OAuth AuthenticationProvider引用,當你試圖從Facebook獲取數據時,一旦FacebookController試圖訪問FB數據,Facebook就會自動檢測到這些數據。 但是你必須瞭解以下配置:

https://github.com/spring-projects/spring-security-oauth/blob/master/samples/oauth2/tonr/src/main/java/org/springframework/security/oauth/examples/config/SecurityConfig.java

@Configuration 
@EnableWebSecurity 
public class SecurityConfig extends WebSecurityConfigurerAdapter { 

    @Override 
    protected void configure(AuthenticationManagerBuilder auth) throws Exception { 
     auth.inMemoryAuthentication().withUser("marissa").password("wombat").roles("USER").and().withUser("sam") 
       .password("kangaroo").roles("USER"); 
    } 

    @Override 
    public void configure(WebSecurity web) throws Exception { 
     web.ignoring().antMatchers("/resources/**"); 
    } 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     // @formatter:off 
      http.authorizeRequests() 
       .antMatchers("/sparklr/**","/facebook/**").hasRole("USER") 
       .anyRequest().permitAll() 
       .and() 
      .logout() 
       .logoutSuccessUrl("/login.jsp") 
       .permitAll() 
       .and() 
      .formLogin() 
       .loginProcessingUrl("/login") 
       .loginPage("/login.jsp") 
       .failureUrl("/login.jsp?authentication_error=true") 
       .permitAll(); 
     // @formatter:on 
    } 

} 

比方說你將要使用的tonr2應用中,inMemoryAuthentication在春季安全使用用戶「瑪麗莎」和密碼「袋熊」提供認證。因此,在您的UI中,首先嚐試對Spring Security進行身份驗證。 驗證通過後,您將擁有「USER」角色。只有這個時候,你將被允許再訪問名爲FaceBookController試圖檢索從Facebook本身的數據,請參見該代碼段資源,

 http.authorizeRequests() 
      .antMatchers("/sparklr/**","/facebook/**").hasRole("USER") 
      .anyRequest().permitAll() 
      .and() 

因此,只要調整相應的代碼。

UPDATE

注意:給定的Spring Security示例應用程序還沒有被配置爲具有動態客戶端密鑰,它依然採用的OAuth2ProtectedResourceDetails一個密鑰只是簡單地證明它硬編碼。從經過身份驗證的Facebook用戶檢索動態Facebook客戶端密鑰需要更多步驟。或者,如果你想只是爲了測試facebook的檢索,調整上述對像這樣的代碼,

http.authorizeRequests() 
      .anyRequest().permitAll() 
      .and() 

刪除要求用戶有一定作用USER,只是允許無需驗證所有請求的限制首先在Spring Security上。