我可以提供一种不同的方法来刷新 jwt 令牌。
我在服务器端使用 Angular 和 Satellizer 和 Spring Boot。
这是客户端的代码:
var app = angular.module('MyApp',[....]);
app.factory('jwtRefreshTokenInterceptor', ['$rootScope', '$q', '$timeout', '$injector', function($rootScope, $q, $timeout, $injector) {
const REQUEST_BUFFER_TIME = 10 * 1000; // 10 seconds
const SESSION_EXPIRY_TIME = 3600 * 1000; // 60 minutes
const REFRESH_TOKEN_URL = '/auth/refresh/';
var global_request_identifier = 0;
var requestInterceptor = {
request: function(config) {
var authService = $injector.get('$auth');
// No need to call the refresh_token api if we don't have a token.
if(config.url.indexOf(REFRESH_TOKEN_URL) == -1 && authService.isAuthenticated()) {
config.global_request_identifier = $rootScope.global_request_identifier = global_request_identifier;
var deferred = $q.defer();
if(!$rootScope.lastTokenUpdateTime) {
$rootScope.lastTokenUpdateTime = new Date();
}
if((new Date() - $rootScope.lastTokenUpdateTime) >= SESSION_EXPIRY_TIME - REQUEST_BUFFER_TIME) {
// We resolve immediately with 0, because the token is close to expiration.
// That's why we cannot afford a timer with REQUEST_BUFFER_TIME seconds delay.
deferred.resolve(0);
} else {
$timeout(function() {
// We update the token if we get to the last buffered request.
if($rootScope.global_request_identifier == config.global_request_identifier) {
deferred.resolve(REQUEST_BUFFER_TIME);
} else {
deferred.reject('This is not the last request in the queue!');
}
}, REQUEST_BUFFER_TIME);
}
var promise = deferred.promise;
promise.then(function(result){
$rootScope.lastTokenUpdateTime = new Date();
// we use $injector, because the $http creates a circular dependency.
var httpService = $injector.get('$http');
httpService.get(REFRESH_TOKEN_URL + result).success(function(data, status, headers, config) {
authService.setToken(data.token);
});
});
}
return config;
}
};
return requestInterceptor;
}]);
app.config(function($stateProvider, $urlRouterProvider, $httpProvider, $authProvider) {
.............
.............
$httpProvider.interceptors.push('jwtRefreshTokenInterceptor');
});
让我解释一下它的作用。
假设我们希望“会话超时”(令牌到期)为 1 小时。
服务器创建具有 1 小时到期日期的令牌。
上面的代码创建了一个 http 拦截器,它拦截每个请求并设置一个请求标识符。然后我们创建一个未来的 Promise,它将在 2 种情况下解决:
1) 例如,如果我们创建了 3 个请求,并且在 10 秒内没有发出其他请求,那么只有最后一个请求会触发令牌刷新 GET 请求。
2) 如果我们被请求“轰炸”,因此没有“最后一个请求”,我们检查我们是否接近 SESSION_EXPIRY_TIME,在这种情况下,我们会立即开始令牌刷新。
最后但并非最不重要的一点是,我们使用一个参数来解决承诺。这是以秒为单位的增量,因此当我们在服务器端创建新令牌时,我们应该使用过期时间(60 分钟 - 10 秒)创建它。我们减去 10 秒,因为 $timeout 有 10 秒的延迟。
服务器端代码如下所示:
@RequestMapping(value = "auth/refresh/{delta}", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<?> refreshAuthenticationToken(HttpServletRequest request, @PathVariable("delta") Long delta, Device device) {
String authToken = request.getHeader(tokenHeader);
if(authToken != null && authToken.startsWith("Bearer ")) {
authToken = authToken.substring(7);
}
String username = jwtTokenUtil.getUsernameFromToken(authToken);
boolean isOk = true;
if(username == null) {
isOk = false;
} else {
final UserDetails userDetails = userDetailsService.loadUserByUsername(username);
isOk = jwtTokenUtil.validateToken(authToken, userDetails);
}
if(!isOk) {
Map<String, String> errorMap = new HashMap<>();
errorMap.put("message", "You are not authorized");
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(errorMap);
}
// renew the token
final String token = jwtTokenUtil.generateToken(username, device, delta);
return ResponseEntity.ok(new JwtAuthenticationResponse(token));
}
希望对某人有所帮助。