Spring Oauth2登录 - 不允许匿名?

时间:2015-10-13 18:55:11

标签: java spring-security-oauth2

遵循manual我实现连接到Facebook图形API并从用户检索信息。当我想通过Facebook访问(登录)时出现问题,我收到了这个错误:

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,但问题也应该出现在google,twitter等上。

2 个答案:

答案 0 :(得分:0)

您共享的代码段不会显示登录信息,只会在您尝试检索有关用户的信息时显示。

根据Spring文档,您需要调整客户端以使其能够使用FB。

  

某些外部OAuth2提供商(例如Facebook)并未完全实施   规范是正确的,否则他们只是坚持旧的   规范的版本比Spring Security OAuth。要使用这些提供商   在您的客户端应用程序中,您可能需要调整各个部分   客户端基础设施。

     

以Facebook为例,Facebook有一项功能   tonr2应用程序(您需要更改配置以添加您的   拥有,有效,客户端ID和秘密 - 它们很容易生成   Facebook网站)。

在另一个线程中有一个关于如何在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;
        }
    }

希望它有所帮助!

答案 1 :(得分: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;
    }

在OAuth2ProtectedResourceDetails上相应地添加facebook凭据。 然后,这个bean将由Spring Security OAuth AuthenticationProvider引用,当你试图从facebook获取数据时,一旦FacebookController尝试访问FB数据,就会自动检测到这些数据。 但你必须了解下面的配置:

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使用用户“marissa”和密码“wombat”提供对Spring安全性的身份验证。所以在你的ui中,首先尝试对Spring Security进行身份验证。 经过身份验证后,您将拥有“USER”角色。只有这次你才可以访问名为FaceBookController的资源,试图从facebook本身检索数据,参考这段代码,

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

所以只需相应调整代码即可。

<强>更新

请注意:给定的Spring Security示例应用程序尚未配置为具有动态客户端密钥,它仍然使用OAuth2ProtectedResourceDetails中的单个密钥进行硬编码,只是为了简单地演示它。从经过身份验证的Facebook用户检索动态Facebook客户端密钥需要更多步骤。或者,如果您只想测试facebook检索,请将上面的代码调整为类似的内容,

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

删除要求用户拥有角色USER的限制,并且只需允许所有请求,而无需首先在Spring Security上进行身份验证。