2016-09-30 17 views
3

我使用了Spring啓動,OAuth2用戶,智威湯遜custome令牌和MySQL。春天引導+ OAuth的+智威湯遜+ MySQL的刷新令牌不工作的第二次

問題:我能夠獲得令牌和刷新令牌,使用刷新令牌我能夠按時獲得新令牌而已,如果我再次嘗試使用新的刷新令牌來獲取新的令牌意味着我得到以下錯誤。

錯誤消息

{ 
"error": "invalid_grant", 
"error_description": "Invalid refresh token: eyJhbGciOiJSUzI1NiJ9.eyJsb2dpbklkIjoibmF2ZWVuIiwidXNlcl9uYW1lIjoibmF2ZWVuIiwic2NvcGUiOlsiY2l0eSIsInJlYWQiLCJ3cml0ZSJdLCJhdGkiOiI4YmVlMzZhZi1lZWM1LTQzODItYjNkZi1jYTU3Mjc0NjQ5N2MiLCJleHAiOjE0NzUyMjc4NzUsImF1dGhvcml0aWVzIjpbIlRlc3RlciIsIlVzZXIiLCJBZG1pbiIsImNhbXBhaWduLWNhbmFsbCIsIm9yZy1jYW5hbGwiXSwianRpIjoiNjE1OWM4NTYtYTZmNi00Njg3LTg3OTMtMTA1NDdkODE4YmVhIiwiY2xpZW50X2lkIjoiY2l0eUNsaWVudElkIn0.FvA821Hv0ZzA6mdwNp-XlcHAy6tCncP8snkQDlmDWulFE-BIe-KxTT0ugjoK2l1ncAQugtyfXCnS_a0bgAPcu1HKmYgIvj4f3XBj1WLRagiDfJqjZAwZhDPvrwks7W1IsvWrzy5k-pmoO7373C5DU0jbFsanzkvMQ6LQAwb_bFfOB3GYH5BSIW4rcbe8AH1B3QKxn9J26Jj1yQWnkY8HnUqnxN5C-3jBwr8pvqPmX2AjOVeAnkoGfY6B3Dq1vz8EE17I8GG2uqGgUsaTiVqP3Lka__ue00MjajxcpVHeh7t1Qs0IbTa2oeuahAwcYOC_ik_Rplhn3w-LHpyhPBrTHA" 
} 

請找到驗證服務器和資源服務器配置文件。 我無法找到我犯的錯誤。

授權服務器

@Configuration 
@EnableAuthorizationServer 
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { 

@Autowired 
private DataSource dataSource; 

@Autowired 
@Qualifier("authenticationManagerBean") 
private AuthenticationManager authenticationManager; 

@Autowired 
@Qualifier("userDetailsService") 
UserDetailsService userDetailsService; 

@Override 
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception { 
    oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()"); 
      //.passwordEncoder(passwordEncoder()); 
} 

@Bean 
public PasswordEncoder passwordEncoder() { 
    CustomPasswordEncoder encoder = new CustomPasswordEncoder(); 
    return encoder; 
} 

@Override 
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception { 
    clients.jdbc(dataSource); 
} 

@Override 
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception { 
    final TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); 
    tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter())); 
    endpoints.tokenStore(tokenStore()) 
      // .approvalStore(approvalStore()) 
      .userDetailsService(userDetailsService).tokenEnhancer(tokenEnhancerChain) 
      .authenticationManager(authenticationManager); 
} 

@Bean 
public JwtAccessTokenConverter accessTokenConverter() { 
    final JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); 
    final KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("mytest.jks"), 
      "mypass".toCharArray()); 
    converter.setKeyPair(keyStoreKeyFactory.getKeyPair("mytest")); 
    return converter; 
} 

@Bean 
public TokenEnhancer tokenEnhancer() { 
    return new CustomTokenEnhancer(); 
} 

// JDBC token store configuration 
@Bean 
public TokenStore tokenStore() { 
    return new JdbcTokenStore(dataSource); 
} 

@Bean 
@Primary 
public ResourceServerTokenServices tokenServices() { 
    final DefaultTokenServices defaultTokenServices = new DefaultTokenServices(); 
    defaultTokenServices.setTokenStore(tokenStore()); 
    defaultTokenServices.setReuseRefreshToken(true); 
    return defaultTokenServices; 
} 

的UserDetailsS​​ervice

@Service("userDetailsService") 
public class CustomUserDetailsService implements UserDetailsService { 

@Autowired 
private AppUserService appUserService; 

@Autowired 
private AppUserRepo appUserRepo; 

@Transactional(readOnly = true) 
@Override 
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException { 
    // TODO: clean the server methods 
    final AppUser appUser = appUserRepo.findExceptDeletedByAppUserName(username); 
    final AppUserCreateDto appUserCreateDto = appUserService.getAppUserCreateByAppUserId(appUser.getAppUserId()); 
    return buildUserForAuthentication(appUser, buildUserAuthority(appUserCreateDto)); 
} 

// Converts com.mkyong.users.model.User user to 
// org.springframework.security.core.userdetails.User 
private User buildUserForAuthentication(AppUser appUser, List<GrantedAuthority> authorities) { 
    return new User(appUser.getLoginId(), appUser.getPasswordHash(), appUser.isActive(), true, true, true, 
      authorities); 
} 

private List<GrantedAuthority> buildUserAuthority(AppUserCreateDto appUserCreateDto) { 
    Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>(); 
    if (appUserCreateDto.isPrimary()) { 
     setAuths.add(new SimpleGrantedAuthority("SuperAdmin")); 
    } else { 
     for (AppUserRoleDto userRole : appUserCreateDto.getAppUserRoleDtos()) { 
      setAuths.add(new SimpleGrantedAuthority(userRole.getAppRoleDto().getName())); 
     } 
     for (AppUserClaimDto userClaim : appUserCreateDto.getAppUserClaimDtos()) { 
      setAuths.add(new SimpleGrantedAuthority(
        userClaim.getAppClaimDto().getClaimType() + "-" + userClaim.getAppClaimDto().getClaimValue())); 
     } 
    } 
    List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths); 
    return Result; 
} 

CustomToken

public class CustomTokenEnhancer implements TokenEnhancer { 
@Override 
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { 
    final Map<String, Object> additionalInfo = new HashMap<>(); 
    additionalInfo.put("loginId", authentication.getName()); 
    ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo); 
    return accessToken; 
} 

WebSecurity

@Configuration 
@EnableWebSecurity 
public class AuthorizationServerWebSecurityConfig extends WebSecurityConfigurerAdapter { 

@Autowired 
@Qualifier("userDetailsService") 
UserDetailsService userDetailsService; 

@Autowired 
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { 
    auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); 
} 

@Bean 
public PasswordEncoder passwordEncoder() { 
    CustomPasswordEncoder encoder = new CustomPasswordEncoder(); 
    return encoder; 
} 

@Override 
@Bean 
public AuthenticationManager authenticationManagerBean() throws Exception { 
    return super.authenticationManagerBean(); 
} 

ResourceServerConfig

@Configuration 
@EnableResourceServer 
public class ResourceServerConfig extends ResourceServerConfigurerAdapter { 

@Override 
public void configure(final HttpSecurity http) throws Exception { 
    // @formatter:off 
    http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED).and().anonymous().and() 
      .authorizeRequests().antMatchers("/api/p/**").permitAll().antMatchers("/api/ping").permitAll() 
      .antMatchers("/api/**").authenticated(); 
    // @formatter:on 
} 

@Override 
public void configure(final ResourceServerSecurityConfigurer config) { 
    config.tokenServices(tokenServices()); 
} 

@Bean 
@Primary 
public ResourceServerTokenServices tokenServices() { 
    final DefaultTokenServices defaultTokenServices = new DefaultTokenServices(); 
    defaultTokenServices.setTokenStore(tokenStore()); 
    defaultTokenServices.setReuseRefreshToken(true); 
    return defaultTokenServices; 
} 

@Bean 
public TokenStore tokenStore() { 
    return new JwtTokenStore(accessTokenConverter()); 
} 

@Bean 
public JwtAccessTokenConverter accessTokenConverter() { 
    final JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); 
    final Resource resource = new ClassPathResource("public.txt"); 
    String publicKey = null; 
    try { 
     publicKey = IOUtils.toString(resource.getInputStream()); 
    } catch (final IOException e) { 
     throw new RuntimeException(e); 
    } 
    converter.setVerifierKey(publicKey); 
    return converter; 
} 

獲得令牌第1次

{ 
"access_token":"eyJhbGciOiJSUzI1NiJ9.eyJsb2dpbklkIjoibmF2ZWVuIiwidXNlcl9uYW1lIjoibmF2ZWVuIiwic2NvcGUiOlsiY2l0eSIsInJlYWQiLCJ3cml0ZSJdLCJleHAiOjE0NzUyMjUyMzAsImF1dGhvcml0aWVzIjpbIlRlc3RlciIsIlVzZXIiLCJBZG1pbiIsImNhbXBhaWduLWNhbmFsbCIsIm9yZy1jYW5hbGwiXSwianRpIjoiYWNlMmQwYzgtZjRhOS00NzY5LThhN2EtNDk1ZjY3ZDFhMjk4IiwiY2xpZW50X2lkIjoiY2l0eUNsaWVudElkIn0.cKZgk39yQ_tl7NW4OhUmSnhPSgvWD8UPp6RRfpc0hsW28ICVjIaCURRaC-eEs9J_YuC2X7NDTFFy3KknFka7rDV2JMCFSILivW13EFT2i0TkHUVFCBWk4MMlEKOXQOUVPiMZ3t3zD6_Tkmo_NneNPjouRVyFjCZ4WGqWPEzGpofExZWlBzoV7bDuF28fTQKqxIQM-ubwx-hKY_btlaXQpyJCuKn4QoCwvp2Bh5zqSEIk2RiGh0nsUyi_MZl3TQX2kYDv-SwWOxf3K9bibY9xPhzgLVIER39Ipo7FrUE9KsYoEkXM1-CghbADjIXu03xd7GZ2530fs-MHhr24YzVY1Q", 
"token_type": "bearer", 
"refresh_token":"eyJhbGciOiJSUzI1NiJ9.eyJsb2dpbklkIjoibmF2ZWVuIiwidXNlcl9uYW1lIjoibmF2ZWVuIiwic2NvcGUiOlsiY2l0eSIsInJlYWQiLCJ3cml0ZSJdLCJhdGkiOiJhY2UyZDBjOC1mNGE5LTQ3NjktOGE3YS00OTVmNjdkMWEyOTgiLCJleHAiOjE0NzUyMjc4NzUsImF1dGhvcml0aWVzIjpbIlRlc3RlciIsIlVzZXIiLCJBZG1pbiIsImNhbXBhaWduLWNhbmFsbCIsIm9yZy1jYW5hbGwiXSwianRpIjoiNjE1OWM4NTYtYTZmNi00Njg3LTg3OTMtMTA1NDdkODE4YmVhIiwiY2xpZW50X2lkIjoiY2l0eUNsaWVudElkIn0.S5pCyuaJVf6HnzLfct2HMQQdkcoZO0-FlgGIRJueAvMmVFRpiCqYCT7AniW8NUvltcMTkDXdZPJo21OmomdWUr3gO1BV3Ki9aJNuewXxsoymIy_L3xWbNb8k8hdrwhYZQufe1YnWLKgHpDUSc13cW-6SNQQwd9ugXkMIvp1qG7d9j6GCZrxOXj0HNLKR3CubfesweUb9GtG8D0XkEGc7O-xPSHZnJWX73sCT5Qi1fot1btTMoeCwp63r9Wa4TkESXrmXdSMzI0GUwc6x_7r3mv5gF34gzF8Z3fChSMglgxreRtF2PbTPGZKXQ3Dk-f0WcWOmbkpetg0n4Wo0dNujaw", 
"expires_in": 359, 
"scope": "city read write", 
"loginId": "naveen", 
"jti": "ace2d0c8-f4a9-4769-8a7a-495f67d1a298" 
} 

獲取刷新令牌第1次

{ 
"access_token":"eyJhbGciOiJSUzI1NiJ9.eyJsb2dpbklkIjoibmF2ZWVuIiwidXNlcl9uYW1lIjoibmF2ZWVuIiwic2NvcGUiOlsiY2l0eSIsInJlYWQiLCJ3cml0ZSJdLCJleHAiOjE0NzUyMjUzNDcsImF1dGhvcml0aWVzIjpbIlRlc3RlciIsIlVzZXIiLCJBZG1pbiIsImNhbXBhaWduLWNhbmFsbCIsIm9yZy1jYW5hbGwiXSwianRpIjoiNzViYTA4OWYtZTczZC00MWIyLWEwM2ItMzBlZDRjNzIyMmM4IiwiY2xpZW50X2lkIjoiY2l0eUNsaWVudElkIn0.fhBqLGTyu4BaAv9zS9gGAgZYymhbBxgnIWBWedmmX1bHNGVWmGjkfxsOtMebVRzMx1WpQUreKSj4IO8bfSV6J9UXgJiq3bEP49gL_egvXIS0bmol35MaN1Kna1hod40RmhxEgfsScuP3Lf35eLX1cjqvpM_B6xtfjStf63AYZ0-e0_oigcXJkTU6QJmC2XFeeoaCHWEdWrWo6lIGMbriv2vlqIn81qENAZ_d9aNGpd-LtUqjJgD299xEOFhO6OCKfjx61gRLwB18daRI_lm_ns9mHUug3T87Ovq-axDYB4NaHB2LvMVi0pXYsdxjlRD-fQ--dNz4JcdTxbuhuxbr8Q", 
"token_type": "bearer", 
"refresh_token":"eyJhbGciOiJSUzI1NiJ9.eyJsb2dpbklkIjoibmF2ZWVuIiwidXNlcl9uYW1lIjoibmF2ZWVuIiwic2NvcGUiOlsiY2l0eSIsInJlYWQiLCJ3cml0ZSJdLCJhdGkiOiI3NWJhMDg5Zi1lNzNkLTQxYjItYTAzYi0zMGVkNGM3MjIyYzgiLCJleHAiOjE0NzUyMjc4NzUsImF1dGhvcml0aWVzIjpbIlRlc3RlciIsIlVzZXIiLCJBZG1pbiIsImNhbXBhaWduLWNhbmFsbCIsIm9yZy1jYW5hbGwiXSwianRpIjoiNjE1OWM4NTYtYTZmNi00Njg3LTg3OTMtMTA1NDdkODE4YmVhIiwiY2xpZW50X2lkIjoiY2l0eUNsaWVudElkIn0.fTJw2F3z-YZQOZ8gTLy9oZheIcZVP9UnbqhTFBG0kVuNojTO7NkzvrzdbG6CwHifolK4A31o2smmw5RHlx6224PgBnE-mCzI6lFG94O77IGvBfCNARJL_X6HbWm2wvtTNnz8k0UN_xPgqHtTpBcIUAHHxMG3TyFZFAoKWYbsQ6WL1mVNFwUxr2R60JYUlCPMB8Tl-2P9IEQr2FIH9amX80fsV23n8023quouwLOVmgUGyVzT1bJ1s2KtgQ51D3T6bvxR4IBlEhSYJ2hmt7DB1IbYQBkxWkd53BiMQQEPyFNgR_9JWFLH7Uq2TUOOb8xL_NnsoyAIO71IFxRPOOsN9w", 
"expires_in": 359, 
"scope": "city read write", 
"loginId": "naveen", 
"jti": "75ba089f-e73d-41b2-a03b-30ed4c7222c8" 
} 

以上使用獲取刷新令牌第二次刷新令牌

{ 
"error": "invalid_grant", 
"error_description": "Invalid refresh token: eyJhbGciOiJSUzI1NiJ9.eyJsb2dpbklkIjoibmF2ZWVuIiwidXNlcl9uYW1lIjoibmF2ZWVuIiwic2NvcGUiOlsiY2l0eSIsInJlYWQiLCJ3cml0ZSJdLCJhdGkiOiI3NWJhMDg5Zi1lNzNkLTQxYjItYTAzYi0zMGVkNGM3MjIyYzgiLCJleHAiOjE0NzUyMjc4NzUsImF1dGhvcml0aWVzIjpbIlRlc3RlciIsIlVzZXIiLCJBZG1pbiIsImNhbXBhaWduLWNhbmFsbCIsIm9yZy1jYW5hbGwiXSwianRpIjoiNjE1OWM4NTYtYTZmNi00Njg3LTg3OTMtMTA1NDdkODE4YmVhIiwiY2xpZW50X2lkIjoiY2l0eUNsaWVudElkIn0.fTJw2F3z-YZQOZ8gTLy9oZheIcZVP9UnbqhTFBG0kVuNojTO7NkzvrzdbG6CwHifolK4A31o2smmw5RHlx6224PgBnE-mCzI6lFG94O77IGvBfCNARJL_X6HbWm2wvtTNnz8k0UN_xPgqHtTpBcIUAHHxMG3TyFZFAoKWYbsQ6WL1mVNFwUxr2R60JYUlCPMB8Tl-2P9IEQr2FIH9amX80fsV23n8023quouwLOVmgUGyVzT1bJ1s2KtgQ51D3T6bvxR4IBlEhSYJ2hmt7DB1IbYQBkxWkd53BiMQQEPyFNgR_9JWFLH7Uq2TUOOb8xL_NnsoyAIO71IFxRPOOsN9w" 
} 

回答

0

我遇到同樣的問題。

此問題是由JwtAccessTokenConverter引起的。

在它接收到一個UID刷新令牌,訪問令牌內的第一請求,但在所述第二請求接收一個刷新JWT令牌並再次對其進行編碼,從而產生一個「新的刷新令牌」。

我通過創建一個自定義JwtAccessTokenConverter,提取刷新令牌,並創建一個新的OAuth2AccessToken解決了這個問題。

我不知道這是否是最好的或正確的解決方案...

public class CustomJwtAccessToKenConverter extends JwtAccessTokenConverter { 

    @Override 
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { 
    OAuth2RefreshToken oAuth2RefreshToken = accessToken.getRefreshToken(); 

    String refreshToken = ""; 

    JsonParser objectMapper = JsonParserFactory.create(); 
    Map<String, Object> claims = objectMapper.parseMap(JwtHelper.decode(oAuth2RefreshToken.getValue().getClaims()); 
    if(claims.containsKey(TOKEN_ID)) { 
     refreshToken = claims.get(TOKEN_ID).toString(); 
    } 

    DefaultOAuth2RefreshToken defaultOAuth2RefreshToken = new DefaultOAuth2RefreshToken(refreshToken); 
    DefaultOAuth2AccessToken defaultOAuth2AccessToken = new DefaultOAuth2AccessToken(accessToken); 
    defaultOAuth2AccessToken.setRefreshToken(defaultOAuth2RefreshToken); 

    return super.enhance(defaultOAuth2AccessToken, authentication); 

}}