2014-02-20 35 views
3

Spring Security OAuth 2.0存在一個問題。Spring Security Oauth - 發送令牌請求時需要基本訪問認證

有彈簧安全的基本配置,從採樣:

<http pattern="/oauth/token" create-session="stateless" 
    authentication-manager-ref="clientAuthenticationManager" 
    xmlns="http://www.springframework.org/schema/security"> 
    <intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" /> 
    <anonymous enabled="false" /> 
    <http-basic entry-point-ref="clientAuthenticationEntryPoint" /> 
    <custom-filter ref="clientCredentialsTokenEndpointFilter" 
     after="BASIC_AUTH_FILTER" /> 
    <access-denied-handler ref="oauthAccessDeniedHandler" /> 
</http> 

<http pattern="/path/**" create-session="never" 
    entry-point-ref="oauthAuthenticationEntryPoint" 
    access-decision-manager-ref="accessDecisionManager" 
    xmlns="http://www.springframework.org/schema/security"> 
    <anonymous enabled="false" /> 
    <intercept-url pattern="/path/*" access="ROLE_USER" /> 
    <custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" /> 
    <access-denied-handler ref="oauthAccessDeniedHandler" /> 
</http> 

<bean id="oauthAuthenticationEntryPoint" 
    class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"> 
    <property name="realmName" value="path" /> 
</bean> 

<bean id="clientAuthenticationEntryPoint" 
    class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"> 
    <property name="realmName" value="path/client" /> 
    <property name="typeName" value="Basic" /> 
</bean> 

<bean id="oauthAccessDeniedHandler" 
    class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" /> 

<bean id="clientCredentialsTokenEndpointFilter" 
    class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter"> 
    <property name="authenticationManager" ref="clientAuthenticationManager" /> 
</bean> 

<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased" 
    xmlns="http://www.springframework.org/schema/beans"> 
    <constructor-arg> 
     <list> 
      <bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" /> 
      <bean class="org.springframework.security.access.vote.RoleVoter" /> 
      <bean class="org.springframework.security.access.vote.AuthenticatedVoter" /> 
     </list> 
    </constructor-arg> 
</bean> 

<bean id="oauthAuthenticationProvider" class="my.package.OAuthAuthenticationProvider" xmlns="http://www.springframework.org/schema/beans" /> 

<authentication-manager id="clientAuthenticationManager" 
    xmlns="http://www.springframework.org/schema/security"> 
    <authentication-provider user-service-ref="clientDetailsUserService" /> 
</authentication-manager> 

<authentication-manager alias="authenticationManager" 
    xmlns="http://www.springframework.org/schema/security"> 
    <authentication-provider ref="oauthAuthenticationProvider" /> 
</authentication-manager> 

<bean id="clientDetailsUserService" 
    class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService"> 
    <constructor-arg ref="clientDetails" /> 
</bean> 

<bean id="tokenStore" 
    class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore" /> 

<bean id="tokenServices" 
    class="org.springframework.security.oauth2.provider.token.DefaultTokenServices"> 
    <property name="tokenStore" ref="tokenStore" /> 
    <property name="supportRefreshToken" value="true" /> 
    <property name="clientDetailsService" ref="clientDetails" /> 
</bean> 

<bean id="userApprovalHandler" 
    class="org.springframework.security.oauth2.provider.approval.TokenServicesUserApprovalHandler"> 
    <property name="tokenServices" ref="tokenServices" /> 
</bean> 

<!-- authorization-server aka AuthorizationServerTokenServices is an interface 
    that defines everything necessary for token management --> 
<oauth:authorization-server 
    client-details-service-ref="clientDetails" token-services-ref="tokenServices" 
    user-approval-handler-ref="userApprovalHandler"> 
    <oauth:authorization-code /> 
    <oauth:implicit /> 
    <oauth:refresh-token /> 
    <oauth:client-credentials /> 
    <oauth:password /> 
</oauth:authorization-server> 

<oauth:resource-server id="resourceServerFilter" 
    resource-id="test" token-services-ref="tokenServices" /> 
<!-- ClientsDeailsService: Entry Point to clients database (given is in 
    memory implementation) --> 
<oauth:client-details-service id="clientDetails"> 
    <!-- client --> 
    <oauth:client client-id="the_client" 
     authorized-grant-types="authorization_code,client_credentials" 
     authorities="ROLE_USER" scope="read,write,trust" secret="secret" /> 

    <oauth:client client-id="my-trusted-client-with-secret" 
     authorized-grant-types="password,authorization_code,refresh_token,implicit" 
     secret="somesecret" authorities="ROLE_USER" /> 

</oauth:client-details-service> 

<sec:global-method-security 
    pre-post-annotations="enabled" proxy-target-class="true"> 
    <sec:expression-handler ref="oauthExpressionHandler" /> 
</sec:global-method-security> 

<oauth:expression-handler id="oauthExpressionHandler" /> 

<oauth:web-expression-handler id="oauthWebExpressionHandler" /> 

它的工作原理,在我的web.xml文件看起來是這樣的:

... 
<context-param> 
    <param-name>contextConfigLocation</param-name> 
    <param-value>/WEB-INF/spring-security.xml</param-value> 
</context-param> 
... 
<listener> 
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
</listener> 
... 
<filter> 
    <filter-name>springSecurityFilterChain</filter-name> 
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 
</filter> 
... 
<filter-mapping> 
    <filter-name>springSecurityFilterChain</filter-name> 
    <url-pattern>/*</url-pattern> 
</filter-mapping> 
... 
<servlet> 
    <servlet-name>Dispatcher Servlet</servlet-name> 
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
    <init-param> 
     <param-name>contextConfigLocation</param-name> 
     <param-value>/WEB-INF/applicationContext.xml</param-value> 
    </init-param> 
    <load-on-startup>1</load-on-startup> 
</servlet> 
... 
<servlet-mapping> 
    <servlet-name>Dispatcher Servlet</servlet-name> 
    <url-pattern>/*</url-pattern> 
</servlet-mapping> 

但在這種情況下,系統的「舊部分」不起作用。 所以我必須要改變調度servlet映射:

<servlet-mapping> 
    <servlet-name>Dispatcher Servlet</servlet-name> 
    <url-pattern>/somepath/*</url-pattern> 
</servlet-mapping> 

現在舊的一部分作品,但不是的Oauth春季安全。我試過兩種網址模式:

<servlet-mapping> 
    <servlet-name>Dispatcher Servlet</servlet-name> 
    <url-pattern>/somepath/*</url-pattern> 
    <url-pattern>/*</url-pattern> 
</servlet-mapping> 

然後oauth的作品,舊的部分沒有。

我做了一個嘗試和改變:

<http pattern="/somepath/oauth/token" create-session="stateless" 
    authentication-manager-ref="clientAuthenticationManager" 
    xmlns="http://www.springframework.org/schema/security"> 
    <intercept-url pattern="/somepath/oauth/token" access="IS_AUTHENTICATED_FULLY" /> 
    <anonymous enabled="false" /> 
    <http-basic entry-point-ref="clientAuthenticationEntryPoint" /> 
    <custom-filter ref="clientCredentialsTokenEndpointFilter" 
     after="BASIC_AUTH_FILTER" /> 
    <access-denied-handler ref="oauthAccessDeniedHandler" /> 
</http> 

而現在它的工作原理是這樣的:

  1. 我訪問地址(工作前):

    https://localhost.server:8443/system/somepath/oauth/token?grant_type=password&client_id=my-trusted-client-with-secret&client_secret=somesecret&username=user&password=pass

  2. 網絡瀏覽器要求登錄和密碼(基本訪問認證)。

  3. 我需要在登錄字段和密碼字段中輸入client-id。

  4. 生成對oauth的請求,然後調用my.package.OAuthAuthenticationProvider中的身份驗證方法。

我應該更改哪些以避免此基本身份驗證提示?

回答

1

您對servlet和過濾器映射的問題源於Spring OAuth需要爲其OAuth相關端點提供DispatcherServlet。我建議你把它們放在默認的servlet映射(/)中,但這取決於你。在任何情況下,您的一站式安全配置都是servlet配置文件,而不是根環境(因此您需要使用DispatcherServlet而不是ContextLoaderListener加載它)。這與示例中的相同(在web.xml中需要servlet初始化參數),但沒有什麼可阻止您添加自己的附加servlet(只要映射不會發生衝突)。

驗證問題是不同的。 OAuth2規範建議使用基本身份驗證來保護/ token端點,因此原則上提示是件好事,儘管我可以看到您試圖避免它。把表單中的祕密放在你正在做的表單中並不足以在生產系統中使用,但如果由於某種原因需要它,你可以使用ClientCredentialsTokenEndpointFilter(如你所做的那樣)。過濾器不工作的原因可能是因爲你發送了一個GET(而不是POST),這對於安全目的來說比在第一個地方使用過濾器更糟糕,但是我沒有看到標記被設置任何地方都可以在過濾器中開啓POST-only驗證功能(儘管我建議您在任何地方使用此功能時設置它)。

您沒有顯示2個Spring XML文件,但您的web.xml引用了2.我認爲這個問題最終將會追溯到您將它們混合在一起或bean定義在兩者之間重複的事實。也許你可以澄清一點(或按照上面的建議從DispatcherServlet切換到加載單個文件)?

+0

感謝您的回覆。 我在另一個項目中使用spring oauth。我必須使用默認的servlet映射,但有可能(應用程序不能在生產環境中運行:))。沒有連接到ContextLoaderListener的spring-security配置文件,spring-security將不會啓動。 但在第二個應用程序的情況下,基本身份驗證沒有要求client_id和secret,並通過GET請求參數傳遞足以進行身份​​驗證...同樣的情況發生在這裏,當我將Dispatcher Servlet映射更改爲默認一。 –

+0

如果沒有作爲根環境加載Spring Security,「不會啓動」並不正確(請查看oauth示例中的反例)。我想我記得你沒有啓用的設置,你需要一個非默認的servlet映射 - auth和token endpoint urls必須被明確設置。我需要再看看更多的細節(明天),看看我能否用更多的信息更新答案,但也許這會讓你開始。 –

+0

再次感謝您的幫助。 現在我使用POST方法和其他方式發送基本身份驗證憑據,它似乎工作正常。 我應該如何設置這個「POST-only」驗證器? –

0

可以使用這樣的配置避免登錄頁面:

@Configuration 
@EnableWebSecurity 
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { 
    ... 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     // @formatter:off 
     http 
      .authorizeRequests() 
       .antMatchers("/").permitAll() 
      .and() 
       .authorizeRequests().anyRequest().hasRole("USER") 
      .and() 
       .csrf() 
       .requireCsrfProtectionMatcher(new AntPathRequestMatcher("/**")).disable(); 
     // @formatter:on 
    } 
    ... 
} 

默認情況下,該方法包括登錄頁面。如果您嘗試在沒有令牌的情況下執行請求,將會顯示403錯誤頁面(您也可以管理此錯誤以重定向到自定義頁面)

相關問題