2 qq 35831147 qq_35831147 于 2016.09.06 16:47 提问

spring security oauth2 的一个问题 求大神指点

在oauth2根据授权码获取accsess token时,通关Debug发现在TokenEndpoint类下的getAccessToken方法内会根据principal去数据库获取client_id,但principal内存储的是当前用户的信息,然后就会报错:Given client ID does not match authenticated client

求大神解答怎么破.

security.xml文件如下:

 <?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:oauth2="http://www.springframework.org/schema/security/oauth2"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
                        http://www.springframework.org/schema/security/oauth2
                        http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd
                        http://www.springframework.org/schema/security
                        http://www.springframework.org/schema/security/spring-security-3.2.xsd">


    <!-- 指定不需要权限过滤的路径 -->
    <http pattern="/login.jsp" security="none" />

    <http pattern="/aouth/token" create-session="stateless"
        authentication-manager-ref="oauth2AuthenticationManager"
        entry-point-ref="oauth2AuthenticationEntryPoint">

        <!-- 设置拦截地址和规则 -->
        <intercept-url pattern="/aouth/token" access="IS_AUTHENTICATED_FULLY" />
        <!-- 非匿名 -->
        <anonymous enabled="false" />

        <http-basic entry-point-ref="oauth2AuthenticationEntryPoint" />
        <!-- 设置过滤链 -->
        <custom-filter ref="clientCredentialsTokenEndpointFilter"
            before="BASIC_AUTH_FILTER" />
        <!-- 拒绝访问处理 -->
        <access-denied-handler ref="oauthAccessDeniedHandler" />
    </http>
    <!--一个自定义的filter,必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性, 
        我们的所有控制将在这三个类中实现 -->
    <beans:bean id="myFilter"
        class="com.yichi.mvcm.filter.MyFilterSecurityInterceptor">
        <beans:property name="authenticationManager" ref="authenticationManager" />
        <beans:property name="accessDecisionManager" ref="myAccessDecisionManagerBean" />
        <beans:property name="securityMetadataSource"
            ref="myInvocationSecurityMetadataSource" />
    </beans:bean>
    <!-- 认证客户端的manager -->
    <authentication-manager id="oauth2AuthenticationManager">
        <authentication-provider user-service-ref="oauth2ClientDetailsUserService" />
    </authentication-manager>

    <!-- spring security提供的用户登录验证 ,alias的值对应上面的ref="authenticationManager" -->
    <authentication-manager alias="authenticationManager">
        <!--userDetailServiceImpl 获取登录的用户、用户权限 -->
        <authentication-provider user-service-ref="myUserDetailService" />
        <!-- 缓存 -->
        <!-- <authentication-provider user-service-ref="cachingUserDetailsService" 
            /> -->
    </authentication-manager>
    <!-- 缓存 -->
    <!-- <beans:bean id="cachingUserDetailsService" class="org.springframework.security.config.authentication.CachingUserDetailsService"> 
        <beans:constructor-arg ref="myUserDetailService" /> 缓存UserDetails的UserCache 
        <beans:property name="userCache" ref="userCache"/> </beans:bean> <beans:bean 
        id="userCache" class="org.springframework.security.core.userdetails.cache.EhCacheBasedUserCache"> 
        用于真正缓存的Ehcache对象 <beans:property name="cache" ref="ehcache4UserDetails"></beans:property> 
        </beans:bean> <beans:bean id="ehcache4UserDetails" class="org.springframework.cache.ehcache.EhCacheFactoryBean" 
        /> -->

    <!--在这个类中,你就可以从数据库中读入用户的密码,角色信息,是否锁定,账号是否过期等 -->
    <beans:bean id="myUserDetailService" class="com.yichi.mvcm.filter.MyUserDetailService">
        <!-- 配置成员变量 -->
        <beans:property name="sysUserService" ref="sysUserService" />
    </beans:bean>
    <!--访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 -->
    <beans:bean id="myAccessDecisionManagerBean"
        class="com.yichi.mvcm.filter.MyAccessDecisionManager">
    </beans:bean>
    <beans:bean id="sysResService" class="com.yichi.mvcm.service.sys.SysResService"></beans:bean>
    <!--资源源数据定义,将所有的资源和权限对应关系建立起来,即定义某一资源可以被哪些角色访问 -->
    <beans:bean id="myInvocationSecurityMetadataSource"
        class="com.yichi.mvcm.filter.MyInvocationSecurityMetadataSource">
        <!-- 配置成员变量 -->
        <beans:constructor-arg>
            <beans:ref bean="sysResService" />
        </beans:constructor-arg>
    </beans:bean>

    <!-- 配置client客户端信息 -->
    <authentication-manager id="clientAuthenticationManager">

        <authentication-provider user-service-ref="oauth2ClientDetailsUserService" />
    </authentication-manager>
    <beans:bean id="oauth2ClientDetailsUserService"
        class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
        <beans:constructor-arg ref="clientDetailsService" />
    </beans:bean>


    <!-- 数据库读取客户端信息 -->
     <beans:bean id="clientDetailsService" class="com.yichi.mvcm.filter.CustomJdbcClientDetailsService"> 
        <beans:constructor-arg index="0" ref="dataSource" /> </beans:bean> 

    <!-- 资源filter配置 -->
    <oauth2:resource-server id="picResourceServer"
        resource-id="pic-resource" token-services-ref="tokenServices" />

    <!-- 配token service用于生成访问令牌token -->
    <beans:bean id="tokenServices"
        class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
        <beans:property name="tokenStore" ref="tokenStore" />
        <!-- 是否生成更新令牌 默认为false -->
        <beans:property name="supportRefreshToken" value="true" />
        <beans:property name="clientDetailsService" ref="clientDetailsService" />
    </beans:bean>

    <!-- token仓库 可以选择存储在内存或者数据库 -->
    <!-- <beans:bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore"> 
        </beans:bean> -->
    <beans:bean id="tokenStore"
        class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore">
        <beans:constructor-arg index="0" ref="dataSource" />
    </beans:bean>

    <!-- 资源 -->
    <http pattern="/mvcm/**" create-session="never"
        entry-point-ref="oauth2AuthenticationEntryPoint"
        access-decision-manager-ref="oauth2AccessDecisionManager">
        <anonymous enabled="false" />
        <intercept-url pattern="/mvcm/**" access="ROLE_index,SCOPE_READ" />
        <custom-filter ref="picResourceServer" before="PRE_AUTH_FILTER" />
        <access-denied-handler ref="oauthAccessDeniedHandler" />
    </http>

    <beans:bean id="oauth2AuthenticationEntryPoint"
        class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint" />

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

    <beans:bean id="oauth2AccessDecisionManager"
        class="org.springframework.security.access.vote.UnanimousBased">
        <beans:constructor-arg>
            <beans:list>
                <beans:bean
                    class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
                <beans:bean class="org.springframework.security.access.vote.RoleVoter" />
                <beans:bean
                    class="org.springframework.security.access.vote.AuthenticatedVoter" />
            </beans:list>
        </beans:constructor-arg>
    </beans:bean>
    <!-- user-approval-page:用户第三方登录认证以后的授权确认页面 error-page : 客户端申请授权的数据与服务端的不相同时跳转的页面 -->
    <oauth2:authorization-server 
        client-details-service-ref="clientDetailsService" token-services-ref="tokenServices"
        user-approval-handler-ref="oauthUserApprovalHandler"
        user-approval-page="oauth_approval" error-page="oauth_error">
        <oauth2:authorization-code 
            authorization-code-services-ref="authorizationCodeServices" />
        <oauth2:implicit />
        <oauth2:refresh-token />
        <oauth2:client-credentials />
        <oauth2:password />
    </oauth2:authorization-server>

    <!-- 授权码存储到数据库 -->
    <beans:bean id="authorizationCodeServices"
        class="org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices">
        <beans:constructor-arg index="0" ref="dataSource" />
    </beans:bean>


    <beans:bean id="oauthUserApprovalHandler"
        class="org.springframework.security.oauth2.provider.approval.DefaultUserApprovalHandler" />


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

    <http auto-config="true" use-expressions="true">
        <!-- 没有权限时跳转的页面 -->
        <access-denied-handler error-page="/accessDenied.jsp" />
        <!-- 使用自定义的登录页面 login_page:指定登录页面 authentication-failure-url:登录失败跳转到/login.jsp 
            login-processing-url进行认证拦截的请求路径 username-parameter和 password-parameter指定属性name字段 
            default-target-url认证成功默认跳转页面 -->
        <form-login login-page="/login.jsp"
            authentication-failure-url="/login.jsp?error=1" login-processing-url="/springSecurity"
            username-parameter="account" password-parameter="password"
            default-target-url="/index.jsp" />

            <intercept-url pattern="/oauth/**" access="hasRole('ROLE_userOrg')" />
        <!-- 设置单用户登录 -->
        <session-management>
            <!-- max-sessions表示最多允许多少次重复登录。如果没有配置error-if-maximum-exceeded, 那么用户账号的第二次登录会使第一次登录失效,而配置了的话,那么第二次登录会被阻止。 
                通常的做法是阻止第二次登录。 -->
            <concurrency-control max-sessions="1"
                error-if-maximum-exceeded="false" />
        </session-management>

        <custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR" />

        <!-- 检测失效的sessionId,session超时时,定位到另外一个URL -->
        <session-management invalid-session-url="/login.jsp" />

        <logout invalidate-session="true" logout-success-url="/"
            logout-url="/logout" />

    </http>
</beans:beans>

1个回答

zealVampire
zealVampire   2016.09.13 10:47

You should provice more infomation or exception, make sure you post(not http get) the client id and password,
so that client_id wn't be empty, and make sure your ClientDetailService get load the client by id,
keep debugging and you will find the error.

TokenEndpoint.java

    @RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
    public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam
    Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {

        if (!(principal instanceof Authentication)) {
            throw new InsufficientAuthenticationException(
                    "There is no client authentication. Try adding an appropriate authentication filter.");
        }

        String clientId = getClientId(principal);
        ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);

        TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);

        if (clientId != null && !clientId.equals("")) {
            // Only validate the client details if a client authenticated during this
            // request.
            if (!clientId.equals(tokenRequest.getClientId())) {
                // double check to make sure that the client ID in the token request is the same as that in the
                // authenticated client
                throw new InvalidClientException("Given client ID does not match authenticated client");
            }
        } 

DefaultOAuth2RequestFactory.java

    public TokenRequest createTokenRequest(Map<String, String> requestParameters, ClientDetails authenticatedClient) {

        String clientId = requestParameters.get(OAuth2Utils.CLIENT_ID);
        if (clientId == null) {
            // if the clientId wasn't passed in in the map, we add pull it from the authenticated client object
            clientId = authenticatedClient.getClientId();
        }
        else {
            // otherwise, make sure that they match
            if (!clientId.equals(authenticatedClient.getClientId())) {
                throw new InvalidClientException("Given client ID does not match authenticated client");
            }
        }
        String grantType = requestParameters.get(OAuth2Utils.GRANT_TYPE);

        Set<String> scopes = extractScopes(requestParameters, clientId);
        TokenRequest tokenRequest = new TokenRequest(requestParameters, clientId, scopes, grantType);

        return tokenRequest;
    }
Csdn user default icon
上传中...
上传图片
插入图片
准确详细的回答,更有利于被提问者采纳,从而获得C币。复制、灌水、广告等回答会被删除,是时候展现真正的技术了!