Spring Oauth2无效的访问令牌

Posted

技术标签:

【中文标题】Spring Oauth2无效的访问令牌【英文标题】:Spring Oauth2 Invalid access token 【发布时间】:2019-03-13 14:58:30 【问题描述】:

我有 spring 和 oauth2 的项目,我可以获得令牌,但是当我尝试在受保护的服务中使用它时,我得到响应“无效的访问令牌”。 缺什么?

这是我的调度器 servlet:

    <?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
  xmlns:sec="http://www.springframework.org/schema/security"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 
http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd
  http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
  http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
  <context:component-scan base-package="com.mx.testing.mem.back" /> 
  <sec:http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="authenticationManager">
        <sec:intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
        <sec:anonymous enabled="false" />
        <sec:http-basic entry-point-ref="clientAuthenticationEntryPoint" />
        <sec:custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" />
        <sec:access-denied-handler ref="oauthAccessDeniedHandler" />
  </sec:http>
  <sec:http pattern="/protected/**" create-session="never" entry-point-ref="oauthAuthenticationEntryPoint">
        <sec:anonymous enabled="false" />
        <sec:intercept-url pattern="/protected/**" method="GET" access="IS_AUTHENTICATED_FULLY" />
        <sec:custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
        <sec:access-denied-handler ref="oauthAccessDeniedHandler" />
  </sec:http>
  <bean id="oauthAuthenticationEntryPoint"
        class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
  </bean>
  <bean id="clientAuthenticationEntryPoint"
        class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
        <property name="realmName" value="springsec/client" />
        <property name="typeName" value="Basic" />
  </bean>
  <bean id="oauthAccessDeniedHandler"
        class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler">
  </bean>
  <bean id="clientCredentialsTokenEndpointFilter"
        class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
        <property name="authenticationManager" ref="authenticationManager" />
  </bean>
  <sec:authentication-manager alias="authenticationManager">
        <sec:authentication-provider user-service-ref="clientDetailsUserService" />
  </sec:authentication-manager>
  <bean id="clientDetailsUserService"
        class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
        <constructor-arg ref="clientDetails" />
  </bean>
  <bean id="clientDetails" class="com.mx.testing.mem.back.controller.GuestServiceImpl">
        <property name="id" value="testingTrustedClient" />
        <property name="secretKey" value="testingKeyTrusted" />
  </bean>
  <sec:authentication-manager id="userAuthenticationManager">
        <sec:authentication-provider ref="customUserAuthenticationProvider" />
  </sec:authentication-manager>
  <bean id="customUserAuthenticationProvider"
        class="com.mx.testing.mem.back.controller.UserAuthenticationProvider">
  </bean>
  <oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices">
  <oauth:authorization-code />
  <oauth:implicit/>
  <oauth:refresh-token/>
  <oauth:client-credentials />
  <oauth:password authentication-manager-ref="userAuthenticationManager"/>
  </oauth:authorization-server>
  <oauth:resource-server id="resourceServerFilter" resource-id="springsec" token-services-ref="tokenServices" />
  <bean id="tokenStore"  class="org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore" />
  <bean id="tokenServices"
        class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
        <property name="tokenStore" ref="tokenStore" />
        <property name="supportRefreshToken" value="true" />
        <property name="accessTokenValiditySeconds" value="12000"></property>
        <property name="clientDetailsService" ref="clientDetails" />
  </bean>
   <!-- Enable @Controller annotation support -->
    <mvc:annotation-driven />
    <mvc:resources mapping="/assets/**" location="/assets/" />
    <mvc:resources mapping="/druid/**" location="/WEB-INF/support/http/resources" />
   <!-- <bean id="dataSource"
        class="com.alibaba.druid.pool.DruidDataSource"
        init-method="init" destroy-method="close">
        <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
       <property name="url" value="jdbc:sqlserver://captrade.database.windows.net;database=AgileTrade;" />
    <property name="username" value="testing" />
    <property name="password" value="C4pgemini2017" />
        <property name="validationQuery" value="SELECT 1" />
    </bean> -->
      <bean id="dataSource" class="oracle.jdbc.pool.OracleDataSource">
    <property name="dataSourceName" value="ds"/>
    <property name="URL" value="jdbc:oracle:thin:@127.0.0.1:1521:cdb1"/>
    <property name="user" value="C##testing_SCHEME"/>
    <property name="password" value="testing"/>
</bean>
    <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.mx.testing.mem.back.mapper" />
    </bean>
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

  <mvc:default-servlet-handler />
  <context:annotation-config/>
</beans>

我的 web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    id="verwandlung" version="3.0">
    <display-name>testing MX</display-name>
      <session-config>
        <session-timeout>
                 3000
        </session-timeout>
    </session-config>
    <!-- Spring MVC Configuration -->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
        <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/dispatcher-servlet.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>
<filter>
  <filter-name>CorsFilter</filter-name>
  <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
  <init-param>
    <param-name>cors.allowed.origins</param-name>
    <param-value>*</param-value>
  </init-param>
  <init-param>
    <param-name>cors.allowed.methods</param-name>
    <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
  </init-param>
  <init-param>
    <param-name>cors.allowed.headers</param-name>
    <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
  </init-param>
  <init-param>
    <param-name>cors.exposed.headers</param-name>
    <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
  </init-param>
  <init-param>
    <param-name>cors.support.credentials</param-name>
    <param-value>false</param-value>
  </init-param>
  <init-param>
    <param-name>cors.preflight.maxage</param-name>
    <param-value>100000</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>CorsFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
    <!-- Druid -->
    <servlet>
        <servlet-name>DruidStatView</servlet-name>
        <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>DruidStatView</servlet-name>
        <url-pattern>/druid/*</url-pattern>
    </servlet-mapping>

    <!-- Home Page -->
    <welcome-file-list>
        <welcome-file>/</welcome-file>
    </welcome-file-list>
</web-app>

我通过以下方式获得令牌:

http://localhost:8080/ServicesMEM/oauth/token?username=myuser&password=mypassword&grant_type=password&scope=read,write,trust

我得到:


    "access_token": "261c2808-9df1-43a6-a6bd-f389a4ed0178",
    "token_type": "bearer",
    "refresh_token": "e53212b5-3cd3-4286-8833-c78069473d74",
    "expires_in": 10673,
    "scope": "read,write,trust"

但是当我使用http://localhost:8080/ServicesMEM/protected/demo/hjhh/?access_token=261c2808-9df1-43a6-a6bd-f389a4ed0178

我明白了:


    "error": "invalid_token",
    "error_description": "Invalid access token: 261c2808-9df1-43a6-a6bd-f389a4ed0178"

我使用 Header : Authorization Bearer ACCESS_TOKEN 但我得到同样的错误。 我错过了什么?

【问题讨论】:

【参考方案1】:

答案:

我需要将 MemoryToken Store 更改为 JDBC 令牌存储:

只需将 inMemoryTokenStore 替换为 JDBCTokenStore:

 <bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore">
    <constructor-arg name="dataSource" ref="dataSource" />
</bean>

并在数据库中为 Oauth2 创建表:

create table oauth_access_token(
token_id varchar2(100), 
token BLOB,
authentication_id varchar2(100), 
user_name varchar2(100), 
client_id varchar2(100),
 authentication BLOB,
 refresh_token varchar2(4000),
primary key(token_id)
);

create table oauth_refresh_token(
token_id varchar2(100), 
token BLOB, 
authentication BLOB
);

【讨论】:

以上是关于Spring Oauth2无效的访问令牌的主要内容,如果未能解决你的问题,请参考以下文章

spring security oauth2 (2.0.8) 使用 InMemory tokenstore 获取无效访问令牌

尝试使用spring oauth2中的刷新令牌获取新的访问令牌时出现无效的客户端错误

Spring Security Oauth2 客户端获取访问令牌失败,请求代码无效=415,消息=不支持的媒体类型

oauth2 spring security with spring session 使那些不活动 30 分钟的用户无效

Spring Security Oauth2 removeAccessToken 不起作用

Azure API 管理:使用 Oauth2 401 的授权给出“未经授权。访问令牌丢失或无效。”