Announcement Announcement Module
Collapse
No announcement yet.
ClassCastException when calling /oauth2/authorize Endpoint Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • ClassCastException when calling /oauth2/authorize Endpoint

    Hello Everyone,

    I have a RESTful web server that I'm using Spring Security OAuth2 on and I'm having trouble with the /authorize endpoint. The odd thing is, this all worked fine when I was testing it with mock data, but after I hooked it up, I started getting this error and it's quite baffling as to why. Here's the setup:

    web.xml
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    	
    	<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>
    	
    	<listener>
    		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    	</listener>
    
    	<listener>
    		<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    	</listener>
    	
    	<context-param>
        	<param-name>contextConfigLocation</param-name>
        	<param-value>WEB-INF/oauth2Context.xml</param-value>
      	</context-param>
    	
    	<servlet>
    		<servlet-name>API</servlet-name>
    		<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
    		<init-param>
    			<param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
    			<param-value>com.sun.jersey.api.container.filter.GZIPContentEncodingFilter</param-value>
    		</init-param>
    		<init-param>
    			<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
    			<param-value>com.sun.jersey.api.container.filter.GZIPContentEncodingFilter</param-value>
    		</init-param>
    		<init-param>
    			<param-name>com.sun.jersey.config.property.packages</param-name>
    			<param-value>com.mycompany</param-value>
    		</init-param>
    		<load-on-startup>1</load-on-startup>
    	</servlet>
    	<servlet-mapping>
    		<servlet-name>API</servlet-name>
    		<url-pattern>/*</url-pattern>
    	</servlet-mapping>
    	
    	<servlet>
            <servlet-name>spring</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>spring</servlet-name>
            <url-pattern>/oauth2/*</url-pattern>
        </servlet-mapping>
    
    </web-app>
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
    	xmlns:jee="http://www.springframework.org/schema/jee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns:oauth2="http://www.springframework.org/schema/security/oauth2"
    	xmlns:security="http://www.springframework.org/schema/security"
    	xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
    						http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
    						http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
    						http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd
    						http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
    						http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd">
    
    	<context:annotation-config />
    
    	<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
            <constructor-arg>
                <list>
                    <bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
                    <bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
                </list>
            </constructor-arg>
        </bean>
    
    	<security:http pattern="/oauth2/token" authentication-manager-ref="clientAuthenticationManager"
            entry-point-ref="oauth2EntryPoint" create-session="stateless">
            <security:intercept-url pattern="/oauth2/token" access="IS_AUTHENTICATED_FULLY" />
            <security:anonymous enabled="false" />
            <security:custom-filter ref="oauth2ProviderFilter" position="PRE_AUTH_FILTER" />
            <security:access-denied-handler ref="accessDeniedHandler" />
        </security:http>
        
        <security:http pattern="/oauth2/authorize" authentication-manager-ref="clientAuthenticationManager"
            entry-point-ref="oauth2EntryPoint" create-session="stateless">
            <security:intercept-url pattern="/oauth2/authorize" access="IS_AUTHENTICATED_FULLY" />
            <security:anonymous enabled="false" />
            <security:custom-filter ref="oauth2ProviderFilter" position="PRE_AUTH_FILTER" />
            <security:access-denied-handler ref="accessDeniedHandler" />
        </security:http>
        
    	<security:http pattern="^(?!/oauth2).*$" request-matcher="ciRegex" entry-point-ref="oauth2EntryPoint" access-decision-manager-ref="accessDecisionManager" 
    		authentication-manager-ref="clientAuthenticationManager" create-session="stateless">
    		<security:intercept-url pattern="^(?!/oauth2).*$" access="IS_AUTHENTICATED_FULLY" />
    		<security:anonymous enabled="false" />
    		<security:custom-filter ref="oauth2ProviderFilter" position="PRE_AUTH_FILTER" />
    		<security:access-denied-handler ref="accessDeniedHandler" />
    	</security:http>
    
    	<bean id="accessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />
    
    	<oauth2:resource-server id="oauth2ProviderFilter" token-services-ref="tokenServices" />
    	<oauth2:rest-template id="restTemplate" resource="authorizationCodeResourceDetails" />
    	<bean id="authorizationCodeResourceDetails" class="org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails">
    		<property name="preEstablishedRedirectUri" value="/oauth2/redirect" />
    	</bean>
    	 
    	<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
    		<property name="tokenStore">
    			<bean class="com.mycompany.oauth2.ApiOuth2TokenStore" />
    		</property>
    		<property name="supportRefreshToken" value="true" />
    	</bean> 
    	 
    	<oauth2:client-details-service id="clientDetailsService" />
    	
    	<bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
            <constructor-arg ref="clientDetailsService" />
        </bean>
    	 
    	<bean id="authenticationProcessingFilter" class="org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter">
    		<constructor-arg value="/oauth2/token" />
    		<property name="restTemplate" ref="restTemplate" />
    	</bean>
    	 
    	<oauth2:authorization-server client-details-service-ref="clientDetailsService" token-services-ref="tokenServices" 
    		authorization-endpoint-url="/authorize" token-endpoint-url="/not_in_use" error-page="forward:/oauth2/api/error" 
    		user-approval-page="forward:/oauth2/api/confirm_access">
    		<oauth2:authorization-code/>
    		<oauth2:refresh-token/>
    	</oauth2:authorization-server>
    
    	<security:authentication-manager id="clientAuthenticationManager">
    		<security:authentication-provider user-service-ref="clientDetailsUserService" />
    		<security:authentication-provider ref="oauth2Provider" />
    	</security:authentication-manager>
    
    	<bean id="apiTokenEndpoint" class="com.mycompany.oauth2.endpoint.ApiTokenEndpoint" />
    	<bean id="apiXmlApprovalEndpoint" class="com.mycompany.oauth2.endpoint.ApiXmlApprovalEndpoint" />
    	<bean id="oauth2EntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint" />
    	<bean id="clientDetailsService" class="com.mycompany.oauth2.ApiClientDetailsService" />
    	<bean id="oauth2Provider" class="com.mycompany.oauth2.OAuth2AuthenticationProvider" />
    </beans>
    Here's the url I'm using:
    https://localhost:7012/api/oauth2/au..._approval=true

    I'll post the exception in a reply.

  • #2
    Here's the exception:

    Code:
    java.lang.ClassCastException: java.lang.String cannot be cast to [Ljava.lang.String;
    	at org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver.resolveArgument(RequestParamMapMethodArgumentResolver.java:78)
    	at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:75)
    	at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:162)
    	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:123)
    	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:100)
    	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:604)
    	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:565)
    	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
    	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
    	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
    	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
    	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
    	at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    	at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
    	at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
    	at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)
    	at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:300)
    	at weblogic.servlet.internal.TailFilter.doFilter(TailFilter.java:26)
    	at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
    	at com.networkfleet.api.server.filter.UrlParameterFilter.doFilter(UrlParameterFilter.java:45)
    	at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
    	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
    	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
    	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    	at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
    	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    	at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54)
    	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    	at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter(OAuth2AuthenticationProcessingFilter.java:131)
    	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
    	at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
    	at weblogic.servlet.internal.RequestEventsFilter.doFilter(RequestEventsFilter.java:27)
    	at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
    	at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.wrapRun(WebAppServletContext.java:3715)
    	at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3681)
    	at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
    	at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:120)
    	at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2277)
    	at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2183)
    	at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1454)
    	at weblogic.work.ExecuteThread.execute(ExecuteThread.java:209)
    	at weblogic.work.ExecuteThread.run(ExecuteThread.java:178)
    Looking a the source, it's checking for a MultivaluedMap and if it's not it tries to cast it to an array. Possible bug?

    Thanks in advance for the help.
    Tim

    Comment


    • #3
      What version of Spring MVC do you have on your classpath (check carefully)? It works for me.

      By the way: why are you sending an access token to the /authorize endpoint (seems like a circular reference)?

      Comment


      • #4
        Originally posted by Dave Syer View Post
        What version of Spring MVC do you have on your classpath (check carefully)? It works for me.

        By the way: why are you sending an access token to the /authorize endpoint (seems like a circular reference)?
        I'm using spring mvc 3.1.4, as for the access token to /authorize, I'm going to rework that to use basic auth or something else.

        Comment


        • #5
          So here's the offending code in org.springframework.web.method.annotation.RequestP aramMapMethodArgumentResolver:

          Code:
          public Object resolveArgument(
          			MethodParameter parameter, ModelAndViewContainer mavContainer,
          			NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
          			throws Exception {
          
          		Class<?> paramType = parameter.getParameterType();
          
          		Map<String, String[]> parameterMap = webRequest.getParameterMap();
          		if (MultiValueMap.class.isAssignableFrom(paramType)) {
          			MultiValueMap<String, String> result = new LinkedMultiValueMap<String, String>(parameterMap.size());
          			for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
          				for (String value : entry.getValue()) {
          					result.add(entry.getKey(), value);
          				}
          			}
          			return result;
          		}
          		else {
          			Map<String, String> result = new LinkedHashMap<String, String>(parameterMap.size());
          			for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
          				if (entry.getValue().length > 0) {
          					result.put(entry.getKey(), entry.getValue()[0]);
          				}
          			}
          			return result;
          		}
          	}
          When it gets the paramType it's a java.util.Map and it's not assignable from MultiValueMap so it drops down to the else. My question is why is it automatically thinking it's an array of strings? As I'm debugging it, all the values in the map are just regular strings:

          Code:
          key=value //regular string
          rather than:
          Code:
          key=[value] //single-value string array
          So I'm not understanding why it's assuming it always going to be an array of strings instead of checking. It's just grabbing the first one in the array anyway. Anyone else run into this problem? BTW, I'm using WebLogic 10.3.5.0 if that makes any difference.

          And I'm getting this on any endpoint that uses @RequestParam. I created a TestEndpoint that just returns some XML and was able to reproduce it that way. What's strange is that it was working just fine, I haven't changed anything (version of Spring) so I'm not sure what's going on.

          Worse case scenario, I could just create my own resolver, but that's a lot of work especially if this is a potential bug.

          Thanks for all the help,
          Tim
          Last edited by tim273; Jun 14th, 2013, 01:27 PM.

          Comment


          • #6
            Figured it out, it was a custom servlet filter someone wrote, I commented that out and it works fine. I knew it would be something stupid.
            Last edited by tim273; Jun 14th, 2013, 05:09 PM.

            Comment

            Working...
            X