我正在使用Spring 4.3.8.RELEASE
和spring-boot 1.5.3.RELEASE
。我想用适当的凭据为oauth访问令牌提供应用程序。我正在使用org.springframework.security.oauth2.provider.token.store.JdbcTokenStore
类来执行此操作。但是,我注意到每次我将客户端连接到我使用OAuth设置的服务器时,服务器会重复返回相同的访问令牌,即使服务器重新启动也是如此。我的OAuth服务器配置在下面
<?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-2.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.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 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" />
<!-- include this only if you need to authenticate clients via request parameters -->
<custom-filter ref="clientCredentialsTokenEndpointFilter" after="BASIC_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
<!-- The OAuth2 protected resources are separated out into their own block
so we can deal with authorization and error handling separately. This isn't
mandatory, but it makes it easier to control the behaviour. -->
<http pattern="/oauth/(users|clients)/.*" request-matcher="regex"
create-session="stateless" entry-point-ref="oauthAuthenticationEntryPoint"
use-expressions="true" xmlns="http://www.springframework.org/schema/security">
<anonymous enabled="false" />
<intercept-url pattern="/oauth/users/([^/].*?)/tokens/.*"
access="#oauth2.clientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or #oauth2.isClient()) and #oauth2.hasScope('write')"
method="DELETE" />
<intercept-url pattern="/oauth/users/.*"
access="#oauth2.clientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or #oauth2.isClient()) and #oauth2.hasScope('read')"
method="GET" />
<intercept-url pattern="/oauth/clients/.*"
access="#oauth2.clientHasRole('ROLE_CLIENT') and #oauth2.isClient() and #oauth2.hasScope('read')"
method="GET" />
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
<expression-handler ref="oauthWebExpressionHandler" />
</http>
<!-- The OAuth2 protected resources are separated out into their own block
so we can deal with authorization and error handling separately. This isn't
mandatory, but it makes it easier to control the behaviour. -->
<http pattern="/me/**" 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="/me" access="ROLE_USER,SCOPE_READ" />
<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="sparklr2" />
</bean>
<bean id="clientAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="sparklr2/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>
<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>
<user-service id="userDetailsService">
<user name="marissa" password="koala" authorities="ROLE_USER" />
<user name="paul" password="emu" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</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.store.JdbcTokenStore">
<constructor-arg ref="dataSource" />
<property name="authenticationKeyGenerator">
<bean class="org.springframework.security.oauth2.UniqueAuthenticationKeyGenerator" />
</property>
</bean>
<bean id="tokenServices"
class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="tokenEnhancer" ref="tokenEnhancer" />
<property name="supportRefreshToken" value="true" />
<property name="clientDetailsService" ref="clientDetails" />
</bean>
<bean id="tokenEnhancer"
class="org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter" />
<bean id="requestFactory"
class="org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory">
<constructor-arg name="clientDetailsService" ref="clientDetails" />
</bean>
<bean id="approvalStore"
class="org.springframework.security.oauth2.provider.approval.TokenApprovalStore">
<property name="tokenStore" ref="tokenStore" />
</bean>
<oauth:authorization-server
client-details-service-ref="clientDetails" token-services-ref="tokenServices">
<oauth:client-credentials />
</oauth:authorization-server>
<oauth:resource-server id="resourceServerFilter" entry-point-ref="entry"
resource-id="myclientAssignment" token-services-ref="tokenServices" />
<bean id="entry" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<constructor-arg value="/assignment" />
</bean>
<context:property-placeholder location="classpath:application.properties"/>
<oauth:client-details-service id="clientDetails">
<oauth:client client-id="${myclient.client.id}"
authorized-grant-types="client_credentials" authorities="ROLE_CLIENT"
access-token-validity="30"
scope="read,write" secret="${myclient.client.secret}" />
</oauth:client-details-service>
<mvc:default-servlet-handler />
<oauth:expression-handler id="oauthExpressionHandler" />
<oauth:web-expression-handler id="oauthWebExpressionHandler" />
<http pattern="/api/**"
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="/**"
access="IS_AUTHENTICATED_FULLY"/>
<custom-filter ref="resourceServerFilter"
before="PRE_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
</beans>
这是我用来连接到我的本地服务器以检索令牌的shell脚本......
#!/bin/bash
ret=$(curl http://localhost:8080/myproject/oauth/token \
-u "myclientid:mysecret" \
-d "grant_type=client_credentials")
echo $ret > /tmp/out
cat /tmp/out
access_token=$( sed -e 's/^.*"access_token":"\([^"]*\)".*$/\1/' /tmp/out )
echo $access_token
修改
Per Hans的请求,这是一个示例访问令牌
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNDk2ODQ4NDAzLCJhdXRob3JpdGllcyI6WyJST0xFX0NMSUVOVCJdLCJqdGkiOiI4OGMxZjkzZC0wNmRhLTRmYTAtOTM1OS0yZWMxYzU5MWJlMGIiLCJjbGllbnRfaWQiOiJ6aW5jbGVhcm5pbmcifQ.Pf-rjPDj0ZhrNOYuhA0tK8lPLLCzlkqUuFFjb48xskA
这是第二个访问令牌
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNDk2OTUzNTg0LCJhdXRob3JpdGllcyI6WyJST0xFX0NMSUVOVCJdLCJqdGkiOiIxM2I5M2M4Ni05MmIwLTQyY2UtYjFkNS1lZjRiNmZhNzJkMzgiLCJjbGllbnRfaWQiOiJ6aW5jbGVhcm5pbmcifQ.GfSHA_JcQg2WHYCI81lunMFIhxdX6REc4goshB2Lck0
答案 0 :(得分:1)
为什么我为多个登录请求获取相同的访问令牌?
Ashwani在this post
中给出了如下答案访问令牌具有固定的生命周期。
在该期限尚未到期之前,访问令牌保持不变。 在此期间,如果您请求访问令牌,您将收到相同的字符串,但会重置过期。它只会发生 位置,密码等与之前的请求完全相同。 否则生成新令牌。有时Salesforce会重置 最后到期令牌的到期时间,并在响应中返回。
没有办法直接使这些令牌过期,因此令牌的发布时间很短,因此应用程序被迫不断刷新它们,从而使服务有机会在需要时撤销应用程序的访问权。 / p>
@Dave, As you have told that, there is no expiry field, so it is non-expiring access token
。
非过期访问令牌是开发人员最简单的方法。如果您选择此选项,请务必考虑您正在进行的权衡。
如果您想要能够任意撤销它们,那么使用自编码令牌是不切实际的。因此,您需要将这些令牌存储在某种数据库中,因此可以根据需要删除它们或将其标记为无效。
请注意,即使服务打算发布非过期访问令牌以供正常使用,您仍需要提供一种机制,以便在特殊情况下使其过期,例如,如果用户明确要撤消应用程序的访问权限,或者如果用户帐户被删除。
开发人员测试自己的应用程序时,非过期访问令牌更容易。您甚至可以为开发人员预生成一个或多个非过期访问令牌,并在应用程序详细信息屏幕上显示给他们。通过这种方式,他们可以立即开始使用令牌发出API请求,而不必担心设置OAuth流程以开始测试您的API。
总之,在以下情况下使用非过期访问令牌:
资源链接: Acess token lifetime
如何刷新访问令牌?这里给出了规则:https://www.oauth.com/oauth2-servers/access-tokens/refreshing-access-tokens/
如何发出访问令牌:
+----------+
| Resource |
| Owner |
| |
+----------+
v
| Resource Owner
(A) Password Credentials
|
v
+---------+ +---------------+
| |>--(B)---- Resource Owner ------->| |
| | Password Credentials | Authorization |
| Client | | Server |
| |<--(C)---- Access Token ---------<| |
| | (w/ Optional Refresh Token) | |
+---------+ +---------------+
Figure 5: Resource Owner Password Credentials Flow
图5中所示的流程包括以下步骤:
(A)资源所有者向客户提供其用户名和 密码。
(B)客户端从授权请求访问令牌 服务器的令牌端点,包括收到的凭据 来自资源所有者。在提出请求时,客户端 使用授权服务器进行身份验证。
(C)授权服务器验证客户端并验证 资源所有者凭据,如果有效,则发出访问权限 令牌。
授权服务器必须遵守以下规则:
o要求对机密客户端或任何客户端进行身份验证 已发布客户端凭据的客户端(或与其他客户端 身份验证要求),
o如果包含客户端身份验证,则验证客户端,
使用其验证资源所有者密码凭据 现有的密码验证算法。
答案 1 :(得分:1)
这是示例常规访问令牌,在发出时,您的配置将有30
秒到期。
在发出新令牌时,您应该会看到信息消息(无法找到令牌的访问令牌)。
{
"access_token": "e057a4f3-9872-4b97-803e-938ad6ef32db",
"token_type": "bearer",
"refresh_token": "f9a7ea9a-0fd1-45a9-b8ae-de87eaf61787",
"expires_in": 30,
"scope": "read write"
}
这是预期的行为,因为spring会查看数据库以验证是否已发出身份验证的访问令牌。
在令牌(30s
)到期之前的后续请求将不会显示该消息。旧访问令牌过期后,当新令牌发出并继续时,此消息将再次显示。
以下行是从
显示该信息消息的位置默认情况下,刷新令牌设置为在30天后过期。当刷新令牌到期时,类似的日志消息将再次出现。
它也应该与JWT访问令牌类似。
当您在DEBUG
日志记录级别运行应用程序时发出新令牌时,这是日志消息的序列。
2017-06-06 14:06:51.339 DEBUG 28732 --- [nio-9191-exec-1] o.s.s.o.p.token.store.JdbcTokenStore : Failed to find access token for authentication org.springframework.security.oauth2.provider.OAuth2Authentication@8e69d9c0: Principal: org.springframework.security.core.userdetails.User@586034f: Username: admin; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_ADMIN, ROLE_USER
2017-06-06 14:06:51.346 DEBUG 28732 --- [nio-9191-exec-1] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL query
2017-06-06 14:06:51.346 DEBUG 28732 --- [nio-9191-exec-1] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement [select token_id, token from oauth_access_token where token_id = ?]
2017-06-06 14:06:51.347 INFO 28732 --- [nio-9191-exec-1] o.s.s.o.p.token.store.JdbcTokenStore : Failed to find access token for token e057a4f3-9872-4b97-803e-938ad6ef32db
2017-06-06 14:06:51.442 DEBUG 28732 --- [nio-9191-exec-1] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL update
2017-06-06 14:06:51.443 DEBUG 28732 --- [nio-9191-exec-1] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement [insert into oauth_access_token (token_id, token, authentication_id, user_name, client_id, authentication, refresh_token) values (?, ?, ?, ?, ?, ?, ?)]
2017-06-06 14:06:51.450 DEBUG 28732 --- [nio-9191-exec-1] o.s.jdbc.core.JdbcTemplate : SQL update affected 1 rows
2017-06-06 14:06:51.452 DEBUG 28732 --- [nio-9191-exec-1] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL update
2017-06-06 14:06:51.452 DEBUG 28732 --- [nio-9191-exec-1] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement [insert into oauth_refresh_token (token_id, token, authentication) values (?, ?, ?)]
2017-06-06 14:06:51.455 DEBUG 28732 --- [nio-9191-exec-1] o.s.jdbc.core.JdbcTemplate : SQL update affected 1 rows
答案 2 :(得分:0)
在您的配置中,您使用JdbcTokenStore:
因此,您必须检查表oauth_client_details:您必须有一行列为access_token_validity,您可以在其中更改持续时间(以秒为单位)。
另一种解决方案是删除该行并重新部署您的应用程序。您的配置将使用您在配置中指定的值以及您在对话中谈论的值再次创建行。
答案 3 :(得分:0)
我知道发布这个问题的答案已经很晚了,但是我认为这是应该考虑的。
配置授权服务器时,参数之一是 accessTokenValiditySeconds
。
这里的要点是它是您的解决方案:
以下是配置授权服务器的示例。
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient(ParamsConfig.TRUSTED_CLIENT_ID)
.secret(passwordEncoder.encode(ParamsConfig.TRUSTED_CLIENT_SECRET))
.accessTokenValiditySeconds(10)
.scopes(ParamsConfig.OAUTH_SECURITY_SCOPE)
.authorizedGrantTypes(ParamsConfig.OAUTH_AUTHORIZATION_GRANT_TYPE)
.authorities(ParamsConfig.OAUTH_SECURITY_AUTHORITIES)
.resourceIds(ParamsConfig.OAUTH_SECURITY_RESOURCE_ID);
}
请参见accessTokenValiditySeconds
,其中描述了两件事:
1-直到该访问令牌在什么时候有效。
2-如果您请求访问令牌,直到什么时候您将获得相同的访问令牌。
注意: 此配置使用Java而不是xml。
在您的情况下,应为access-token-validity
。