Reputation: 739
I've got one problem with Spring Security OAuth 2.0.
There is basic configuration of spring-security, taken from sample:
<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" />
It works, when in my web.xml file looks like this:
...
<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>
But in this case, the "old part" of system doesn't work. So I have to change Dispatcher Servlet mapping:
<servlet-mapping>
<servlet-name>Dispatcher Servlet</servlet-name>
<url-pattern>/somepath/*</url-pattern>
</servlet-mapping>
Now the old part works, but not Oauth Spring security. I've tried with two url-patterns:
<servlet-mapping>
<servlet-name>Dispatcher Servlet</servlet-name>
<url-pattern>/somepath/*</url-pattern>
<url-pattern>/*</url-pattern>
</servlet-mapping>
Then oauth works, old part doesn't.
I did one more try and changed:
<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>
And now it works like this:
I'm accessing address (which worked before):
Web browser asks for login and password (Basic access authentication).
I need to enter client-id in login field and secret in password field.
Request to oauth is generated and then authentication method in my.package.OAuthAuthenticationProvider is called.
What should I change to avoid this Basic auth prompt?
Upvotes: 3
Views: 7839
Reputation: 4432
You can avoid the login page using a configuration like this:
@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
}
...
}
By default this method include a login page. With this new implementation in case you try to do a request without token a 403 error page will be displayed (you also can manage this error in order to redirect to a customized page)
Upvotes: 0
Reputation: 58134
Your problems with the servlet and filter mappings stem from the fact that Spring OAuth needs a DispatcherServlet for its OAuth related endpoints. I recommend you put those in the default servlet mapping (/), but it's up to you. In any case your one-stop-shop security configuration is a servlet configuration file, not a root context (so you need to load it with a DispatcherServlet
not a ContextLoaderListener
). This would be the same as in the sample (you need a servlet init param in web.xml), but there is nothing to stop you adding your own additional servlets (as long as the mappings don't clash of course).
The authentication question is different. The OAuth2 spec recommends that the /token endpoint is secured with Basic Authentication, so the prompt is a good thing in principle, although I can see that you tried to avoid it. Putting secrets in form fields like you are doing is not really secure enough to be used in a production system, but if for some reason you need it you can use the ClientCredentialsTokenEndpointFilter
(as you did). The reason that filter is not working might have been because you are sending a GET (not a POST), which is an even worse idea for security purposes than using the filter in the first place, but I don't see the flag being set anywhere that switches on POST-only validation in the filter (although I recommend you set it if you use this thing in anger anywhere).
You haven't shown 2 Spring XML files but your web.xml refers to 2. I think the problem ultimately will be traceable to the fact that your have them mixed up or bean definitions duplicated across the two. Maybe you can clarify that (or switch to loading a single file from the DispatcherServlet
as recommended above)?
Upvotes: 2