本地项目请求token的地址:http://localhost:8882/oauth/token 第一次正常获取token结果:
{ "access_token": "627afc96-ced5-4aab-8f92-a3830ad9eae0", "token_type": "bearer", "refresh_token": "4fa139f6-d0bf-443f-942d-f98cb4b7a74d", "expires_in": 43199, "scope": "read" }请求自动续期操作: 参数
参数是否必须相关解释grant_type必须授权类型,此处操作值应为“refresh_token”client_id必须分配的客户端idclient_secret必须分配的客户端密码refresh_token必须值为上一次请求返回值中"refresh_token“请求结果如下: 调用refresh_token有以下几种情况:
调用时access_token,refresh_token均未过期调用时access_token过期,refresh_token未过期 此前两种情况,access_token会变,而且expires延长,refresh_token根据设定的过期时间,没有失效则不变。refresh_token过期 报错: {"error":"invalid_token","error_description":"Invalid refresh token (expired): 95844d87-f06e-4a4e-b76c-f16c5329e287"} 测试过程中遇到部分报错记录: <1 如果配置client 时授权类型 authorizedGrantTypes没有包含 “refresh_code”,会出现一下错误: Unauthorized grant type: refresh_token 以下响应结果经过自定义处理: { "success": false, "msg": "Unauthorized grant type: refresh_token", "errorCode": "400", "path": "/oauth/token", "timestamp": "1572507979741" }<2 报错message: UserDetailsService is required. 具体信息:
2019-10-31 15:53:12.715 INFO 15792 --- [nio-8882-exec-5] o.s.s.o.provider.endpoint.TokenEndpoint : Handling error: IllegalStateException, UserDetailsService is required.本来是以为在AuthorizationServerConfigurerAdapter配置是不用去进行用户验证的,所以只在WebSecurityConfigurerAdapter 中添加了UserDetailsService的配置,后面经过查询资料发现需要在AuthorizationServerEndpointsConfigurer中配上UserDetailsService。问题解决,原因大概是在进行refresh_token的时候,需要根据token值去重新进行一次用户验证。通过打断点的方式,发现调用了loadUserByUsername方法去进行re-authenticate。 相关源码列下:
@Transactional(noRollbackFor={InvalidTokenException.class, InvalidGrantException.class}) public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest) throws AuthenticationException { if (!supportRefreshToken) { throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue); } OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(refreshTokenValue); if (refreshToken == null) { throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue); } OAuth2Authentication authentication = tokenStore.readAuthenticationForRefreshToken(refreshToken); if (this.authenticationManager != null && !authentication.isClientOnly()) { // The client has already been authenticated, but the user authentication might be old now, so give it a // chance to re-authenticate. Authentication user = new PreAuthenticatedAuthenticationToken(authentication.getUserAuthentication(), "", authentication.getAuthorities()); user = authenticationManager.authenticate(user); Object details = authentication.getDetails(); authentication = new OAuth2Authentication(authentication.getOAuth2Request(), user); authentication.setDetails(details); } String clientId = authentication.getOAuth2Request().getClientId(); if (clientId == null || !clientId.equals(tokenRequest.getClientId())) { throw new InvalidGrantException("Wrong client for this refresh token: " + refreshTokenValue); } // clear out any access tokens already associated with the refresh // token. tokenStore.removeAccessTokenUsingRefreshToken(refreshToken); if (isExpired(refreshToken)) { tokenStore.removeRefreshToken(refreshToken); throw new InvalidTokenException("Invalid refresh token (expired): " + refreshToken); } authentication = createRefreshedAuthentication(authentication, tokenRequest); if (!reuseRefreshToken) { tokenStore.removeRefreshToken(refreshToken); refreshToken = createRefreshToken(authentication); } OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken); tokenStore.storeAccessToken(accessToken, authentication); if (!reuseRefreshToken) { tokenStore.storeRefreshToken(accessToken.getRefreshToken(), authentication); } return accessToken; }其中注释已经足以解释为什么需要在AuthorizationServerEndpointsConfigurer中配上UserDetailsService: // The client has already been authenticated, but the user authentication might be old now, so give it a // chance to re-authenticate.
修改配置如下:
@Autowired private UserServiceDetail userServiceDetail; @Override @Primary public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(new InMemoryTokenStore()) .authenticationManager(authenticationManager) .exceptionTranslator(customWebResponseExceptionTranslator) .userDetailsService(userServiceDetail) .allowedTokenEndpointRequestMethods(HttpMethod.POST, HttpMethod.GET); }