我正在使用
Spring启动,OAuth2,JWT客户令牌和
MySQL.
问题:我能够获取令牌和刷新令牌,使用刷新令牌我只能按时获取新令牌,如果我再次尝试使用新刷新令牌获取新令牌意味着我收到以下错误.
错误信息
{
"error": "invalid_grant",
"error_description": "Invalid refresh token: eyJhbGciOiJSUzI1NiJ9.eyJsb2dpbklkIjoibmF2ZWVuIiwidXNlcl9uYW1lIjoibmF2ZWVuIiwic2NvcGUiOlsiY2l0eSIsInJlYWQiLCJ3cml0ZSJdLCJhdGkiOiI4YmVlMzZhZi1lZWM1LTQzODItYjNkZi1jYTU3Mjc0NjQ5N2MiLCJleHAiOjE0NzUyMjc4NzUsImF1dGhvcml0aWVzIjpbIlRlc3RlciIsIlVzZXIiLCJBZG1pbiIsImNhbXBhaWduLWNhbmFsbCIsIm9yZy1jYW5hbGwiXSwianRpIjoiNjE1OWM4NTYtYTZmNi00Njg3LTg3OTMtMTA1NDdkODE4YmVhIiwiY2xpZW50X2lkIjoiY2l0eUNsaWVudElkIn0.FvA821Hv0ZzA6mdwNp-XlcHAy6tCncP8snkQDlmDWulFE-BIe-KxTT0ugjoK2l1ncAQugtyfXCnS_a0bgAPcu1HKmYgIvj4f3XBj1WLRagiDfJqjZAwZhDPvrwks7W1IsvWrzy5k-pmoO7373C5DU0jbFsanzkvMQ6LQAwb_bFfOB3GYH5BSIW4rcbe8AH1B3QKxn9J26Jj1yQWnkY8HnUqnxN5C-3jBwr8pvqPmX2AjOVeAnkoGfY6B3Dq1vz8EE17I8GG2uqGgUsaTiVqP3Lka__ue00MjajxcpVHeh7t1Qs0IbTa2oeuahAwcYOC_ik_Rplhn3w-LHpyhPBrTHA"
}
请找到Auth服务器和资源服务器配置文件.
我无法找到我错误的地方.
授权服务器
@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;
}
的UserDetailsService
@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;
}
第一次获得令牌
{
"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"
}
第一次获得刷新令牌
{
"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"
}
最佳答案 JdbcTokenStore与JWT不兼容.
不要设置任何tokenStore.
“由于JWT令牌是自包含的,因此持久的JWT令牌无关紧要”
资源:
https://github.com/spring-projects/spring-security-oauth/issues/687