使用 angular 和 express-jwt 实现刷新令牌

Posted

技术标签:

【中文标题】使用 angular 和 express-jwt 实现刷新令牌【英文标题】:implementing refresh-tokens with angular and express-jwt 【发布时间】:2014-12-22 04:44:54 【问题描述】:

我想使用 angular、nodejs 和 express-jwt 实现带有 json web 令牌的滑动过期概念。我对如何做到这一点有点困惑,并且正在努力寻找刷新令牌的任何示例或与这些技术/框架的会话相关的其他材料。

我想到的几个选项是

在初始登录后为每个请求生成一个新令牌 在服务器端跟踪颁发的令牌

但我真的不确定,请帮忙

【问题讨论】:

我有基本相同的问题,并将我的第一次通过方法发布在:***.com/questions/27408762/… 你可以使用gist.github.com/Mirodil/952e5932c284a2d205db 【参考方案1】:

我设法实现了这个场景。

我做了什么……

在服务器上:

-启用用于登录的 API 端点。此端点将使用标头中的 Json Web 令牌进行响应。客户端必须捕获它(使用 $http 拦截器)并保存它(我使用本地存储)。客户端还将管理服务器发送的刷新令牌。

-在对服务器的每个请求中,配置一个中间件以快速验证令牌。起初我尝试了 express-jwt 模块,但 jsonwebtoken 对我来说是正确的。

对于特定路由,您可能需要禁用中间件。在这种情况下登录和注销。

var jwtCheck = auth.verifyJWT;
jwtCheck.unless = unless;
app.use('/api', jwtCheck.unless(path: [
    '/api/auth/signin',
    '/api/auth/signout'
]));

- 中间件 verifyJWT 总是在标头中使用令牌进行响应。如果令牌需要刷新,则调用 refreshed 函数。

jwtLib 是我自己的库,代码用于创建、刷新和获取 jwt 令牌。

function(req, res, next) 
    var newToken,
        token = jwtLib.fetch(req.headers);

    if(token) 
        jwt.verify(token, config.jwt.secret, 
            secret: config.jwt.secret
        , function(err, decoded) 
            if(err) 
                return res.status(401).send(
                    message: 'User token is not valid'
                );
            
            //Refresh: If the token needs to be refreshed gets the new refreshed token
            newToken = jwtLib.refreshToken(decoded);
            if(newToken) 
                // Set the JWT refreshed token in http header
                res.set('Authorization', 'Bearer ' + newToken);
                next();
             else 
                res.set('Authorization', 'Bearer ' + token);
                next();
            
        );
     else 
        return res.status(401).send(
            message: 'User token is not present'
        );
    
;

-刷新函数(jwtLib)。由于参数需要解码的令牌,请参见上面的 jsonwebtoken 在调用 jwt.verify() 时解析解码。

如果您在登录期间创建了一个有效期为 4 小时的令牌,并且刷新有效期为 1 小时(1 * 60 * 60 = 3600 秒),这意味着如果用户已不活动 3 年,令牌将被刷新小时或更长时间,但不超过 4 小时,因为在这种情况下验证过程将失败(刷新 1 小时窗口)。这样可以避免在每个请求上生成新的令牌,前提是令牌将在此时间窗口内过期。

module.exports.refreshToken = function(decoded) 
    var token_exp,
        now,
        newToken;

    token_exp = decoded.exp;
    now = moment().unix().valueOf();

    if((token_exp - now) < config.jwt.TOKEN_REFRESH_EXPIRATION) 
        newToken = this.createToken(decoded.user);
        if(newToken) 
            return newToken;
        
     else 
       return null;
    
;

在客户端(Angularjs)上:

-启用客户端登录。这将调用服务器端点。我使用用 base64 编码的 Http 基本身份验证。 您可以使用 base64 角度模块对电子邮件进行编码:密码 请注意,成功后我不会将令牌存储在 localStorage 或 Cookie 上。这将由 http 拦截器管理。

//Base64 encode Basic Authorization (email:password)
$http.defaults.headers.common.Authorization = 'Basic ' + base64.encode(credentials.email + ':' + credentials.password);
return $http.post('/api/auth/signin', skipAuthorization: true);

-配置 http 拦截器以在每次请求时将令牌发送到服务器并将令牌存储在响应中。如果收到刷新的令牌,则必须存储此令牌。

// Config HTTP Interceptors
angular.module('auth').config(['$httpProvider',
    function($httpProvider) 
        // Set the httpProvider interceptor
        $httpProvider.interceptors.push(['$q', '$location', 'localStorageService', 'jwtHelper', '$injector',
            function($q, $location, localStorageService, jwtHelper, $injector) 
                return 
                    request: function(config) 
                        var token = localStorageService.get('authToken');
                        config.headers = config.headers || ;

                        if (token && !jwtHelper.isTokenExpired(token)) 
                            config.headers.Authorization = 'Bearer ' + token;
                        
                        return config;
                    ,
                    requestError: function(rejection) 
                        return $q.reject(rejection);
                    ,
                    response: function(response) 
                        //JWT Token: If the token is a valid JWT token, new or refreshed, save it in the localStorage
                        var Authentication = $injector.get('Authentication'),
                            storagedToken = localStorageService.get('authToken'),
                            receivedToken = response.headers('Authorization');
                        if(receivedToken) 
                            receivedToken = Authentication.fetchJwt(receivedToken);
                        
                        if(receivedToken && !jwtHelper.isTokenExpired(receivedToken) && (storagedToken !== receivedToken)) 

                            //Save Auth token to local storage
                            localStorageService.set('authToken', receivedToken);
                        
                        return response;
                    ,
                    responseError: function(rejection) 
                        var Authentication = $injector.get('Authentication');
                        switch (rejection.status) 
                            case 401:
                                // Deauthenticate the global user
                                Authentication.signout();
                                break;
                            case 403:
                                // Add unauthorized behaviour
                                break;
                        

                        return $q.reject(rejection);
                    
                ;
            
        ]);
    
]);

【讨论】:

在这个解决方案中,如果旧的 JWT 已过期,您似乎只是在发布新的 JWT。这破坏了 JWT 的整体安全性,一旦过期就无法使用,甚至无法刷新和发布新令牌。服务器确实应该发出另一个称为刷新令牌的令牌,它将保存到数据库中并将其发送给客户端。刷新令牌将用于在前一个令牌过期后获取新的 JWT 访问令牌。 refreshToken() 仅在用户通过身份验证时调用,因此不存在安全问题。如果过期在 TOKEN_REFRESH_EXPIRATION 的时间窗口内,则 refreshToken() 生成一个新的令牌,否则返回 null。无论如何,这段代码是前一段时间写的。它仍然有效,但也许其他一些方法可能会更好。 @almoraleslopez 当有人偷了你的令牌时,黑客将通过身份验证并且可以无限制刷新 @AgBorkowski 如果刷新令牌由客户端处理,那么它很容易被窃取,因此不会提供额外的保护。我对刷新令牌的理解是,它们使您能够在一段时间后(比如 1 天,而 jwt 更经常地刷新)以及强制某人重新登录的能力。即您标记刷新令牌在数据库中无效。斯蒂芬是对的。我们应该回到数据库检查每次 JWT 过期时用户仍然被允许访问。否则还不如只拥有一个危险的长寿 JWT

以上是关于使用 angular 和 express-jwt 实现刷新令牌的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 angular2 设置 express-jwt 而不阻塞 node_modules?

尝试使用 express-jwt 阻止访问除登录页面以外的 Angular 应用程序时出现问题

无法使用带有 MEAN 堆栈的 express-jwt 在服务器端验证令牌

jsonwebtoken和express-jwt的使用

JWT 和 express-JWT 有问题

如何查看存储在 JWT 中的数据?使用 auth0 和 express-jwt