我试图在Spring OAuth应用程序中使用刷新令牌但没有成功。系统将在密码授予上发出刷新令牌:
{
"access_token": "xxxxx",
"token_type": "bearer",
"refresh_token": "xxxxxx",
"expires_in": 21599,
"scope": "read write"
}
但是尝试使用刷新令牌会导致以下错误:
curl -u acme -d" grant_type = refresh_token& refresh_token = xxxxxx" http://localhost:9999/uaa/oauth/token
{
"error": "invalid_token",
"error_description": "Cannot convert access token to JSON"
}
我的auth服务器配置如下:
@Controller
@SessionAttributes("authorizationRequest")
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
@EnableResourceServer
@ImportResource("classpath:/spring/application-context.xml")
@Configuration
public class ApplicationConfiguration extends WebMvcConfigurerAdapter {
@RequestMapping("/user")
@ResponseBody
public Principal user(Principal user) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println(auth.toString());
return user;
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.addViewController("/oauth/confirm_access").setViewName("authorize");
}
@Configuration
@Order(-20)
protected static class LoginConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
// @formatter:off
http
.formLogin().loginPage("/login").permitAll()
.and()
.requestMatchers().antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access")
.and()
.authorizeRequests().anyRequest().authenticated();
// @formatter:on
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
@Configuration
public static class JwtConfiguration {
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
KeyPair keyPair = new KeyStoreKeyFactory(new ClassPathResource("keystore.jks"), "foobar".toCharArray())
.getKeyPair("test");
converter.setKeyPair(keyPair);
return converter;
}
@Bean
public JwtTokenStore jwtTokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
}
@Configuration
@EnableAuthorizationServer
protected static class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter implements
EnvironmentAware {
private static final String ENV_OAUTH = "authentication.oauth.";
private static final String PROP_CLIENTID = "clientid";
private static final String PROP_SECRET = "secret";
private static final String PROP_TOKEN_VALIDITY_SECONDS = "tokenValidityInSeconds";
private RelaxedPropertyResolver propertyResolver;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Autowired
private JwtTokenStore jwtTokenStore;
@Autowired
@Qualifier("myUserDetailsService")
private UserDetailsService userDetailsService;
@Autowired
private DataSource dataSource;
@Override
public void setEnvironment(Environment environment) {
this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_OAUTH);
}
@Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(jwtTokenStore);
tokenServices.setAuthenticationManager(authenticationManager);
tokenServices.setTokenEnhancer(tokenEnhancer());
return tokenServices;
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
// The order is important here - the custom enhancer must come before the jwtAccessTokenConverter.
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), jwtAccessTokenConverter));
endpoints
.authenticationManager(authenticationManager)
.tokenEnhancer(tokenEnhancerChain)
.tokenStore(jwtTokenStore)
.userDetailsService(userDetailsService);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
/*.withClient(propertyResolver.getProperty(PROP_CLIENTID))
.scopes("read", "write")
.autoApprove(true)
.authorities(ClientAuthoritiesConstants.CLIENT)
.authorizedGrantTypes("authorization_code", "refresh_token", "password")
.secret(propertyResolver.getProperty(PROP_SECRET))
.accessTokenValiditySeconds(propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer
.class, 1800));*/
}
}
/**
* Configures the global LDAP authentication
*/
@Configuration
protected static class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter implements EnvironmentAware {
private static final String ENV_LDAP = "authentication.ldap.";
private static final String PROP_SEARCH_BASE = "userSearchBase";
private static final String PROP_SEARCH_FILTER = "userSearchFilter";
private static final String PROP_GROUP_SEARCH_FILTER = "groupSearchFilter";
private static final String PROP_LDAP_URL = "url";
private static final String PROP_LDAP_USER = "userDn";
private static final String PROP_LDAP_PASS = "password";
private RelaxedPropertyResolver propertyResolver;
/**
* Maps the LDAP user to the Principle that we'll be using in the app
*/
public UserDetailsContextMapper userDetailsContextMapper() {
return new UserDetailsContextMapper() {
@Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username,
Collection<? extends GrantedAuthority> authorities) {
// Get the common name of the user
String commonName = ctx.getStringAttribute("cn");
// Get the users email address
String email = ctx.getStringAttribute("mail");
// Get the domino user UNID
String uId = ctx.getStringAttribute("uid");
return new CustomUserDetails(email, "", commonName, authorities);
}
@Override
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
throw new IllegalStateException("Only retrieving data from LDAP is currently supported");
}
};
}
@Override
public void setEnvironment(Environment environment) {
this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_LDAP);
}
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userSearchBase(propertyResolver.getProperty(PROP_SEARCH_BASE))
.groupSearchBase(propertyResolver.getProperty(PROP_SEARCH_BASE))
.userSearchFilter(propertyResolver.getProperty(PROP_SEARCH_FILTER))
.groupSearchFilter(propertyResolver.getProperty(PROP_GROUP_SEARCH_FILTER))
.userDetailsContextMapper(userDetailsContextMapper())
.contextSource()
.url(propertyResolver.getProperty(PROP_LDAP_URL))
.managerDn(propertyResolver.getProperty(PROP_LDAP_USER))
.managerPassword(propertyResolver.getProperty(PROP_LDAP_PASS));
}
}
}
任何人都知道为什么auth服务器在给定有效刷新令牌时不会发出新令牌?
答案 0 :(得分:5)
有这个问题。我发送了“Bearer xxxxxx ...”而TokenEnhancer只期待“xxxxx ...”没有“Bearer”前缀
答案 1 :(得分:4)
因此看起来问题是无效的refresh_token格式。由于我的配置,auth服务器期待的是一个有效的JWT,而我发送了一个普通的承载令牌。因此,错误消息无法将令牌转换为JSON&#39;。
顺便说一下,我发现这个文档对于理解Spring OAuth的所有部分如何组合在一起非常有用,这使我能够弄清楚这里发生了什么:
https://github.com/spring-projects/spring-security-oauth/blob/master/docs/oauth2.md
答案 2 :(得分:2)
我有同样的问题。经过一些调试后发现我的签名不匹配。
在我的情况下,我的设置键有点不同,并且存在签名和验证密钥未匹配的错误。
https://github.com/spring-projects/spring-security-oauth/issues/1144
答案 3 :(得分:1)
我已经两年了,如果它对任何人有帮助,但我的同一问题是由于我没有使用我在令牌服务提供程序DefaultTokenServices中的JwtTokenStore中使用的tokenEnhancer。
<!-- Access token converter -->
<bean id="jwtAccessTokenConverter"
class="org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter">
<property name="signingKey" value="${security.jwt.signing-key}"/>
</bean>
<!-- Token store -->
<bean id="jwtTokenStore"
class="org.springframework.security.oauth2.provider.token.store.JwtTokenStore">
<constructor-arg name="jwtTokenEnhancer" ref="jwtAccessTokenConverter"/>
</bean>
<!-- Creates token store services provider -->
<bean id="tokenServiceProvider"
class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore"
ref="jwtTokenStore"/>
<!--This must be set according to z docs -->
<property name="tokenEnhancer"
ref="jwtAccessTokenConverter"/>
<property name="supportRefreshToken"
value="true"/>
<property name="accessTokenValiditySeconds"
value="${security.jwt.access-token-validity-seconds}"/>
<property name="refreshTokenValiditySeconds"
value="${security.jwt.refresh-token-validity-seconds}"/>
</bean>
答案 4 :(得分:0)
Spring Boot 1.5.4也有同样的问题
jwtAccessTokenConverter.setVerifierKey(publicKey);
实际上并没有真正设置验证者(在调试值中为空)在 -
JwtAccessTokenConverter
...protected Map<String, Object> decode(String token) {
try {
Jwt jwt = JwtHelper.decodeAndVerify(token, verifier);
随着解决方法的帮助:
private JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new CustomTokenEnhancer();
jwtAccessTokenConverter.setSigningKey(jwtSigningKey);
jwtAccessTokenConverter.setVerifier(new RsaVerifier(jwtPublicKey));
log.info("Set JWT signing key to: {}", jwtAccessTokenConverter.getKey());
return jwtAccessTokenConverter;
}