作者: admin

  • node.js实现token身份验证

    node token node.js node

    《token理解、学习》

     

    安装必要模块:

    //koa-jwt 中间件,使用后为每个路由添加校验方法,就不需要一个个手动添加
    npm install -S koa-jwt
    
    
    //生成token
    npm install -S jsonwebtoken@9.0.0   //对应node版本v12.20.2至少要》12

     

     

    1,假设用户使用正确的帐号和密码登录,后台就会生成一个token,返回给浏览器,并保存在浏览器本地中,下次要进入后台就需要从本地获取到token传给后台

    const jwt = require('jsonwebtoken');
    //生成token
    
    router.get('/',async (ctx, next) => { //这里模拟,用户输入对的帐号密码时就会执行以下逻辑
        const token = jwt.sign({
           name: 123
        },'user_pass_xxx' //随便一点内容,撒盐:加密的时候混淆
        ,{
         expiresIn: 60 //60秒到期时间
        });
    
       //把token存放在本地
       ctx.cookies.set(
         'state',
          token,
          {
               domain: 'localhost', // 写cookie所在的域名
               path: '/', // 写cookie所在的路径
               maxAge: 10 * 60 * 1000, // cookie有效时长
               expires: new Date('2019-07-15'), // cookie失效时间
               httpOnly: false, // 是否只用于http请求中获取
               overwrite: false // 是否允许重写
          }
        )
        ctx.body='成功'
    })

     

    生成的token内容类似于:(以下代码是JWT数据格式,具体可以参考地址)

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoxMjMsImlhdCI6MTQ5MTQ3NTQyNCwiZXhwIjoxNDkxNDc1NDg0fQ.hYNC4qFAyhZClmPaLixfN137d41R2CFk1xPlfLK10JU

     

    //解密token
    jwt.verify(token, 'user_pass_xxx', function (err, decoded) {
      if (!err){
         console.log(decoded.name); //会输出123,如果过了60秒,则有错误。
      }
    })

     

     

    2,把token用cookies存储在本地里,每次请求需要用帐号和密码校验对了才能显示的页面时,就必须把token从cookies里提取出来把token值带入到request headers,之后服务器才能获取到token值。

    使用ajax把token值传入到request headers后在带给后台

     

     

    3,服务器获取到客服端从request headers带入的token值后,就可以进行逻辑判断了,如果有token值并没过期就显示出相应的页面或数据

     

    const app = new Koa()
    const koajwt = require('koa-jwt')
    
    app.use(async (ctx,next)=>{  //必须在koajwt验证之前把token传给服务器,不然根本访问不了路由
        console.log('app.js里面'+ctx.cookies.get('state'));
        ctx.request.header = {'authorization': "Bearer " + ctx.cookies.get('state')}
        await next();
    });
    app.use(koajwt({secret: 'user_pass_xxx'}).unless({path:[/^\/api\/login/, /^\/api\/register/]})) //usless排除进行jwt校验的路由
                                            //unless({ path: ['/public']}) ,每个路由都要校验token除了,访问/public路由时不需要
    
    
    //在路由中处理
    const router = require('koa-router')()
    router.post('/xxx', async ctx => {
      const token = ctx.state.user //从request headers带入token值,才能使用这种方法获取
      console.log(token.id)
      console.log(token.secret)
    }
    
    
    
    
    

     

    //单独为某个路由添加校验

    get('/helloworld', koajwt({
     secret: 'user_pass_xxx', // Should not be hardcoded
    }), async(ctx) => {
     ctx.body = 'helloworld page!' //通过了token才能显示内容
    })

     

     

    完成代码:

    编辑app.js

    const koajwt = require("koa-jwt");
    
    // 自定义接口错误信息
    app.use(async (ctx, next) => {
        return next().catch(err => {
            if (err.status === 401) {
                // 自定义返回结果
                ctx.status = 200;
                console.log(err.name);
                ctx.body = {
                    code: 401,
                    msg: '登录超时'//err.message
                };
            } else {
                throw err;
            }
        });
    });
    
    
    //以后每请求接口都会去拿cookie里面的token是否正常接口才能请求的通
    app.use(
        koajwt({
            secret:'user_pass_xxx',
            cookie: 'token', // 从 cookie 中获取token,cookie字段名是token
            debug: false // 开启debug可以看到准确的错误信息
        })
            .unless({ path:[/^\/login/, /^\/logout/, /^\/code_img/] })
    );
    
    
    
    
    
    // routes
    app.use(index.routes(), index.allowedMethods())
    app.use(users.routes(), users.allowedMethods())

     

    编辑路由的登录接口

    const jwt = require('jsonwebtoken');
    
    
    router.post('/login',async (ctx,next)=>{
    
       var obj = await new Promise((a,b)=>{
            db.get('username').find({"user":"admin"}).then((doc)=>{
    
                var pass = en_pass((ctx.request.body.password))
    
                if(doc[0].user===ctx.request.body.username && doc[0].pass===pass){ //客户端输入的账号密码和数据库匹配
    
                    //----------------生成token-----------------------------------
                    const token = jwt.sign({
                            "username":ctx.request.body.username,
                            "pass":pass
                        },'user_pass_xxx' //随便一点内容,撒盐:加密的时候混淆
                        ,{
                            expiresIn: 60 * 60 * 24 //token24小时后过期
                        });
                    //----------------生成token-----------------------------------
                    
                       a(token);
    
                }else{
                    a(false);
                }
    
            });
        })
    
        ctx.body={
            code:obj?200:401,
            data:obj?obj:null,
            msg:obj?'登录成功':'密码错误'
        }
    
    })

    只要登录成功,就要让后端或者前端去生成cookie,且字段名为token值是jwt.sign()生成的,以后每请求接口都会效验token是否正常接口才能请求的通

     

     

    参考文章有:

    https://www.dengxiangxing.com/post/42072

    http://yunzaifei.github.io/2017/09/22/koa2%E5%BC%80%E5%8F%91%E7%9A%84api%E6%9C%8D%E5%8A%A1%E5%8F%8Ajwt%E5%BA%94%E7%94%A8/

    https://www.jianshu.com/p/a7882080c541

    https://segmentfault.com/a/1190000009494020

    https://www.npmjs.com/package/jsonwebtoken

     

     

  • token理解、学习

    为了更好的理解token就使用,token、session 、cookie三者进行比较

    cookie:
    cookie数据存放在客户的浏览器上;
    cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗;
    适合保存一些不太重要的信息;

     

    session:
    session数据临时放在服务器上,至少这点安全性比cookie好;
    session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能;
    web服务器做了负载均衡,那么下一个操作请求到了另一台服务器的时候session会丢失,也就是说刚刚才输入了帐号密码,可能又再次输入。
    适合把帐号和加密的密码用session存储

     

    token:

    token用的最多场景就是用户输入帐号密码后,一段时间内就可以避免重复再次输入。
    cookie和session都可以实现类似的功能但是都没token安全,cookie和session都需要发出加密后的密码,想象一样虽然密码都密码了但是被公布出来始终还是觉得不安全。使用token认证不需要直接去操作用户的帐号和密码。
    作为身份认证 token安全性比session好,因为每个请求都有签名还能防止监听以及攻击。

     

    //用token作为身份认证最常见的工作流程:(密码模式)
    1. 登录时候,客户端通过用户名与密码请求登录。(用正确的帐号密码来跟服务器换一个token可以理解为令牌)
    2. 服务端收到客户端发来的帐号密码,并去验证用户名与密码
    3. 验证通过,服务端会签发一个Token,再把这个Token发给客户端.
    4. 客户端收到Token,存储到本地,如Cookie,SessionStorage,LocalStorage(如果涉及到跨域建议用cookie来暂时存储token).我们是存在SessionStorage
    5. 客户端每次像服务器请求API接口时候,都要带上Token,把token放入到Request Headers服务器那里就可以获取到。(带上的token其实就是从存储在本地的Cookie,SessionStorage,LocalStorage来获取到token)
    6. 服务端收到请求,验证Token,如果通过就返回数据,否则提示报错信息.

    http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html

    headers的一些理解:https://sdeno.com/?p=6375

    通过headers传参具体操作:https://sdeno.com/?p=6358

     

    jquery的ajax传递header参数:

    $.ajax({  
      beforeSend: function(xhr) {  //请求服务器前先执行
         xhr.setRequestHeader("Access-Toke");  
      },
      headers: {  //请求时,需要带的参数
        'Access-Token':$.cookie('access_token')
      },
      url: url,
      data: data
    });

     

    /—————————————————————————-

    ——————————————————————————/

    了解JWT,并使用

     

    认证成功后服务器一般会返回这么一些字符串类似于。

    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIyMzczIiwiaXNzIjoiaHR0cHM6Ly93d3ctcWEud2VpY2xpY2FpLmNvbS9hcGkvdmMvbG9naW4iL 
    CJpYXQiOjE1MzI0MTg3MTUsImV4cCI6MTU2Mzk1NDcxNSwibmJmIjoxNTMyNDE4NzE1LCJqdGkiOiJvRm5LcEkxMkZ3cUk2aGt5In0.rDagPOmG_IZ6p9zbAHo 
    BMNW0S56q30ScT-aug4l51rI

    以上这些字符串就是token值,也就是JWT。

     

     

    JWT适用于情景:
    1,需要认证用户。例如:需要账户和密码
    2,可能会存在跨域认证的问题。

    如果遇上以上两种问题,使用JWT是最佳的解决方案。

     

     

    JWT 的数据结构:

    eyxxxxxxx.eyxxxxxx.xxxxxxxx

    它是一个很长的字符串,中间用点(.)分隔成三个部分。

    这三部分,分别是:
    Header(头部).Payload(负载).Signature(签名)

     

     

    Header介绍

    Header 部分是一个 JSON 对象,通常是下面的样子。

    {
     "alg": "HS256",
     "typ": "JWT"
    }

    上面代码中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT。

    最后,将上面的 JSON 对象使用 Base64URL 算法(详见后文)转成字符串。

     

     

    Payload介绍

    Payload 部分也是一个 JSON 对象,参数都是可选

    {
     iss (issuer):签发人
     exp (expiration time):过期时间
     sub (subject):主题
     aud (audience):受众
     nbf (Not Before):生效时间
     iat (Issued At):签发时间
     jti (JWT ID):编号
    }
    
    例子:
    {
     "sub": "1234567890",
     "name": "John Doe",
     "admin": true
    }

    注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。

    这个 JSON 对象也要使用 Base64URL 算法转成字符串。

     

     

    Signature介绍

    Signature 作用是对前两部分的签名,防止数据篡改,但前提需要一个密钥(secret),这个密钥只有服务器才知道,不能泄露给用户。
    。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

     

     

    就这样JWT的结果就是由以下公式得出:

    HMACSHA256(
     base64UrlEncode(header) + "." +
     base64UrlEncode(payload),
     secret)

     

     

     

    Base64URL

    前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。

    JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+、/和=,在 URL 里
    面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。这就是 Base64URL 算法。

     

     

    最后,如何使用JWT

    既要有认证功能又要有跨域功能,最后的方法就是,放在 HTTP 请求的头信息Authorization字段里面。
    Authorization: Bearer eyxxxxxxx.eyxxxxxx.xxxxxxxx

    另一种做法是,跨域的时候,JWT 就放在 POST 请求的数据体里面。

     

     

    JWT 优缺点

    (1)JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。

    (2)JWT 不加密的情况下,不能将秘密数据写入 JWT。

    (3)JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。

    (4)JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。

    (5)JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。

    (6)为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。

     

    http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html

  • 手机滑动刻度功能

    手机 移动 滑动 刻度 刻度表

     

    kedu_diy

     

     

  • http headers 请求头/响应头

    请求头:就是客户端在访问url的时候也就是请求服务器时,会把一些request headers请求头信息发送给服务器,服务器就可以接收。

    (客户端发头部信息给服务器去接收)

     

     

    响应头:服务器收到了客户端提出的要求,现在要把数据返回给客户端,还有一些response Headers信息给客服端,所以客户端也能靠js获取到这些信息。

    (服务器返回头部信息给客户端接收)

     

    ——————————————————————————-

     

    Request Headers

    Accept:告诉服务器,客户机支持的数据类型

    Accept-Encoding:告诉服务器,客户机支持的数据压缩格式

    Cache-Control:缓存控制,服务器通过控制浏览器要不要缓存数据

    Connection:处理完这次请求,是断开连接还是保持连接

    Cookie:客户机通过这个可以向服务器带数据

    Host:访问的主机名

    Upgrade-Insecure-Requests:参考http://www.cnblogs.com/hustskyking/p/upgrade-insecure-requests.html

    User-Agent:告诉服务器,客户机的软件环境

     

     

    Response Headers响应头

    Connection:处理完这次请求后,是断开连接还是继续保持连接

    Content-Encoding:服务器通过这个头告诉浏览器数据的压缩格式

    Content-Length:服务器通过这个头告诉浏览器回送数据的长度

    Content-Type:服务器通过这个头告诉浏览器回送数据的类型

    Date:当前时间值

    Server:服务器通过这个头告诉浏览器服务器的类型

    Vary:Accept-Encoding ——明确告知缓存服务器按照 Accept-Encoding 字段的内容,分别缓存不同的版本;参考:https://imququ.com/post/vary-header-in-http.html

    X-Powered-By:服务器告知客户机网站是用何种语言或框架编写的。

     

    对SEO重要的字段:Rerer Cookie user-agent ,Web前端会影响SEO,我们经常看到的网页不抓取、不收录、没排名和没流量有些是因为Web前端影响的。

     

    https://www.cnblogs.com/good7758/articles/5635981.html

     

  • vue中使用ajax——vue-resource.js

    vue可以在全局环境中使用和局部环境使用

    // 基于全局Vue对象使用http
    Vue.http.get('/someUrl', [options]).then(function(){ console.log('成功回调') }, function(){ console.log('失败回调') });
    Vue.http.post('/someUrl', [body], [options]).then(successCallback, errorCallback);
    或者
    Vue.http({
      url: 'http://example.com/book',
      method: 'JSONP',
      jsonp: 'cb'
    });
    
    // 在一个Vue实例内使用$http
    this.$http.get('/someUrl', [options]).then(successCallback, errorCallback);
    this.$http.post('/someUrl', [body], [options]).then(successCallback, errorCallback);

     

    参数 类型 描述
    url string 请求的URL
    method string 请求的HTTP方法,例如:’GET’, ‘POST’或其他HTTP方法
    body ObjectFormData string request body
    params Object 请求的URL参数对象
    headers Object request header  ,涉及跨域时 无效,

    关于请求头部信息参考:https://sdeno.com/?p=6375

    timeout number 单位为毫秒的请求超时时间 (0 表示无超时时间)
    before function(request) 请求发送前的处理函数,类似于jQuery的beforeSend函数
    progress function(event) ProgressEvent回调处理函数
    credentials boolean 表示跨域请求时是否需要使用凭证
    emulateHTTP boolean 发送PUT, PATCH, DELETE请求时以HTTP POST的方式发送,并设置请求头的X-HTTP-Method-Override
    emulateJSON boolean 将request body以application/x-www-form-urlencoded content type发送

     

     

    emulateHTTP
    emulateHTTP(布尔值)。默认值为:false,设置值为true时,PUT, PATCH和DELETE等请求会以普通的POST方法发出,并且HTTP头信息的X-HTTP-Method-Override属性会设置为实际的HTTP方法。。

     

    emulateJSON
    emulateJSON(布尔值)。默认值为:false,设置值为true时,数据(无论是get还是post)都会以formdata(表单提交方式)的形式发送数据。所以我们在给后台传递参数的时候需要加JSON.stringify(参数)处理一下。
    如果服务器无法处理编码为application/json的请求,可以启用emulateJSON选项。启用之后,请求会以application/x-www-form-urlencoded为MIME type,就像普通的HTML表单一样。

     

    全局设置:

    Vue.http.options.emulateHTTP = true;
    Vue.http.options.emulateJSON = true;

     

     

    get:

    this.$http.get('get.php',{params:{a:1,b:2}}).then(function(res){
        alert(res.body); 
     },function(res){
        console.log(res.status);
    });

     

     

    post:

    客户端要发数据给后台时

    this.$http.post('post.php',{a:1,b:2}},{emulateJSON:true}).then(function(res){
        alert(res.body); 
     },function(res){
        console.log(res.status);
    });

     

     

    jsonp:

    url :https://sug.so.360.cn/suggest?callback=suggest_so&word=a
    
    new Vue({
      el:'#app',
      mounted:function () {
         this.$http.jsonp('https://sug.so.360.cn/suggest',{
            params:{word:'a'},  //传给服务器的参数
            jsonp:'callback',   //服务器那边接收的字段 $_POST['callback']
            headers: {"xxx": "xxxx"}  //客户端自定义请求头信息传给服务器,但是在跨域情况下无效。
         }).then(function(res){
            console.log(res.data);
         },function(res){
            console.log('失败');
         });
      }
    });
    
    
    //另一种格式
    
    this.$http({
       url: 'https://sug.so.360.cn/suggest',
       params: {
          word:'a'
       },
       method: 'JSONP',
       jsonp: 'callback'  //一般默认都是callback,如果有变化根据接口的规则来,例如/suggest?callback187=suggest_so,改成jsonp: 'callback187'
    }).then(function (res) {
        console.log(res.body);
    }, function (res) {
        console.log('错误回调');
    });
    
    

    跨域相关内容查看:https://sdeno.com/?p=3647

     

    interceptors数据劫持  类似before 、after

    Vue.http.interceptors.push(function(request, next) {
         // ...
         // 请求发送前的处理逻辑 (类似$.ajax的beforeSend,在请求服务器的url之前,做一些处理之后在请求)
         // ...
         request.params.c='sdf'; //例如:在请求服务器url前,突然又想额外加一个参数
         request.headers.set('Server', 'asfsdf222');  //这里设置请求头,也就是客户端发给服务器的Request headers,在服务器那里接收
      next(function(response) {
         // ...
         // 请求发送后的处理逻辑 (当客户端请求后台数据时,后台即将要发数据给客户端之前,这里又可以额外的做一些处理)
         // ...
         // 根据请求的状态,response参数会返回给successCallback或errorCallback
         response.body=response.body+'1231323';
         return response
      })
    });
    
    
    
    new Vue({
       el:'#app',
       mounted:function () {
           this.$http.get('http://www.vc.com/v3/blank',{params:{a:1,b:2}}).then(function(res){  //请求url前又额外添加了一个参数c=sdf
               // alert(res.body);
               console.log(res.body); //<---这里返回的数据内容多了'1231323' 
           },function(res){
               console.log(res.status);
           });
       }
    });

     

     

     

    http://www.jb51.net/article/103159.htm