关于接口的安全验证
- 基于 Session 的安全验证
- Session 存储在服务器,用户用户比较少的话是一种简单的安全验证机制,但是涉及到跨域 的话需要进行一些配置
- 用户量非常非常大的话会耗费一定的服务器资源(中小项目不需要考虑)
- 对请求参数进行加密的签名验证
- 涉及公钥、私钥、签名等
- JWT
- JWT 全称 JSON Web Token,是目前比较流行的另一种跨域身份验证解决方案
- 也是被很多人用坏的一种安全验证机制,具体需要看项目是否适合
关于 JWT的验证的流程
- 客户端请求接口登录,登录成功后,服务端通过jwt生成token返回给客户端
- 客户端访问需要登录后才能访问的接口的时候,需要在请求接口的时候携带token,服务端获取token调用jwt验证token的合法性,验证不通过则拒绝访问
Nodejs 中使用 JWT 实现接口的安全验证
签发 token
安装 jsonwebtoken $ cnpm install jsonwebtoken --save
签发:
var jwt = require('jsonwebtoken');
router.get('/login', function (req, res, next) {
// 三个参数:用户对象,签名字符串(最好定义在配置中),过期时间
var token = jwt.sign({ name: '张三' }, 'this is sign',{ expiresIn:60*60*24});
res.send(token);
});
Axios 访问基于 Jwt 的接口
// 存储服务器签发的token ...
var token = ''; // 这是保存的服务器签发的token, 伪代码
// 带token请求接口
axios.get("http://localhost:3000/api/list", {
auth: {
username: token,
password:''
}
})
.then(function(response) {
// handle success
console.log(response); })
.catch(function(error) {
// handle error
console.log(error);
})
.finally(function() {
// always executed
});
备注:在不同的框架中如:react,vue,angular, 需要封装通用拦截器,在拦截器中使用,以上仅作为演示
获取请求头里面的 token
安装 $ cnpm install basic-auth --save
获取
router.get('/addressList', function (req, res, next) {
var token = auth(req); // 这里的auth就是 basic-auth
})
验证客户端传过来 token 的合法性
router.get('/addressList', function (req, res, next) {
var token = auth(req);
if(!token) {
res.send('没有权限');
}
try {
var v = jwt.verify(token.name, 'this is sign'); // 验证token
// console.log(v);
res.send(v ? "有权限" : "没有权限")
} catch (error) {
res.send(error);
}
});
备注:上面仅作为简单演示,最好做成一个中间件来处理权限验证
关于 Jwt 的一些问题
- JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。
- JWT 不加密的情况下,不能将秘密数据写入 JWT。
- JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询 数据库的次数。
- JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个token,或者更改 token 的权限。也就是说,一旦JWT签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。
- JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
- 为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。
关于jwt的二次签名验证
- 对于某一个接口,如: /api/address?uid=1&pid=2, 对请求的参数进行签名, “uid=1&pid=2”,通过算法处理 var sign = md5(“uid=1&pid=2” + 私钥) , 这个私钥可以是个md5加密后的登录密码
- 这个时候客户端会有这三个东西: token, 请求的url参数, sign (也就是 前面的password)
- 服务端获取客户端携带的token以及url上的参数, 然后比如根据uid查找用户密码, 计算出私钥(私钥=md5(用户密码)), 然后计算出sign
- 对比客户端传递过来的sign和服务端计算出的sign是否一致, 如果一致则验证通过
- 当另一个接口被请求的时候,如:/api/xxx?uid=1&tid=3 同样的道理,每次因为参数不同,计算出的sign都是不一样的,当然如果没有参数,接口本身其实也可以当做参数处理, 服务器可以通过相同的算法来进行对比,这样可以进一步阻止了客户端权限的盗用
- 当然道高一尺魔高一丈,没有绝对的安全,如果涉及到一些支付相关的业务, 尽量利用二次加密和https以及尽可能小的过期时间,以及做一些机制对token进行续签等