node.js+captchapng+jsonwebtoken实现登录验证示例


Posted in Javascript onAugust 17, 2017

提到登录验证,大家肯定能想到的就是12306的验证码了吧。12306为了防止刷票,可以说是煞费苦心,验证码也越来越难识别,最终即使是人也可能识别不了了。

今天,小编就给大家说一下node如何实现图片验证码,以及使用token验证登录。学习本文你将学到:

1.使用captchapng生成图片验证码

2.使用jsonwebtoken实现登录验证

一、图片验证码生成(最后有全部代码)

首先,我们理一下流程,第一步服务器要随机生成一组四位数。

第二步,将这四位数用canvas绘图生成图片。

第三步,我们要将这四位数存起来,以便用户返回数据时做对比。

那么存到哪里呢?很明显为了区分用户,存到session最为稳妥。

第一步,先要有一个登录页面。在这里我们依然用react,

login.tsx

import * as React from 'react'
import * as ReactDom from 'react-dom'
import {Link, browserHistory} from 'react-router';
import * as axios from 'axios';
export default class Login extends React.Component<any,any>{
  constructor(props){
    super(props)
    this.state = {
      userName : '',
      password : '',
      yzNoId  : '',
      hash : Math.random()
    }
  }
  handleUserName(e) : any {
    this.setState({
      userName : e.target.value
    })
  }
  handlePassword(e) : any {
    this.setState({
      password : e.target.value
    })
  }
  handleYzId(e) : any {
    this.setState({
      yzNoId : e.target.value
    })
  }
  render(){
    const { userName, password, yzNoId } = this.state;
    return(
      <div>
        <div className="nav-wrap">
          <ul className="nav">
            <li><Link to="/home">首页</Link></li>
            <li><Link to="/imgLoad">上传</Link></li>
            <li><Link to="/login">登陆</Link></li>
          </ul>
        </div>
        <div className="content">
          <div className="login-warp">
            <p>
              <input type="text" className="username" value={userName} onChange={this.handleUserName.bind(this)} placeholder="用户名"/>
            </p>
            <p>
              <input type="text" className="password" value={password} onChange={this.handlePassword.bind(this)} placeholder="密码"/>  
            </p>  
            <p>
              <input type="text" className="yz" value={yzNoId} onChange={this.handleYzId.bind(this)} placeholder="验证码"/>
              <img src={"http://localhost:3000/captcha"} className="yz-img" />  
            </p>
            <p>
              <input type="button" className="submit" value="登陆" onClick={this.sbumit.bind(this,{userName:userName,password:password,captcha:yzNoId})} /> 
            </p>  
          </div>
        </div>
      </div>
    )
  }
}

页面是这样的

node.js+captchapng+jsonwebtoken实现登录验证示例

我们需要通过服务器给一张验证图片。

router/index.js 添加如下代码

var Login = require('./controller/login');
var login = new Login;
router.get('/captcha', login.captcha);
router.post('/login',login.loginer);
login是定义在控制器的一个类的实例,captcha,loginer是它的方法。分别是返回验证图片、登录验证。
controller/login.js

var rf = require('fs');
var captchapng = require('captchapng');
class Login {
  constructor(){}
  captcha(req, res, next) {
    var str = parseInt(Math.random()*9000+1000);  //随机生成数字
    req.session.captcha = str;  // 存入session
    var p = new captchapng(80, 30, str); //生成图片
    p.color(0, 0, 0, 0);
    p.color(80, 80, 80, 255);
    var img = p.getBase64();
    var imgbase64 = new Buffer(img, 'base64');
    res.writeHead(200, {
      'Content-Type': 'image/png'
    });
    res.end(imgbase64);
  }
  loginer(req, res, next) {
    let captcha = req.body.captcha;
    let userName = req.body.userName;
    let password = req.body.password;
    if (captcha != req.session.captcha) {
      res.status(400).send({
        message: '验证码错误'
      });
    }else if(userName == "chenxuehui" && password == "123321"){
      res.json({"code":100,"verson":true,"msg":"登陆成功","token":token});
    }else{
      res.json({"code":0,"verson":false,"msg":"密码错误"});
    }
  }
}
module.exports = Login

captcha方法是生成一张含四位数字的图片,然后将图片保存到session中。

将此方法在 router/index.js 中引用

router.get('/captcha', login.captcha);

也就是说我们访问localhost:3000/captcha就会返回一张图片。

node.js+captchapng+jsonwebtoken实现登录验证示例

有了这个连接后我们就可以通过图片的src属性拿到该图片,但是当点击图片的时候要重新刷新,所以我们需要给图片添加一个点击刷新事件。将下面代码插入到login.tsx中

setHash() {
    this.setState({
      hash : Math.random()
    })
}

img标签也变成了

<img src={"http://localhost:3000/captcha?aaa="+this.state.hash}  className="yz-img" onClick={this.setHash.bind(this)} />  
 

此时login.tsx全部代码:

import * as React from 'react'
import * as ReactDom from 'react-dom'
import {Link, browserHistory} from 'react-router';
import * as axios from 'axios';
export default class Login extends React.Component<any,any>{
  constructor(props){
    super(props)
    this.state = {
      userName : '',
      password : '',
      yzNoId  : '',
      hash : Math.random()
    }
  }
  public async sbumit(params : any) : Promise<any>{
    let res = await axios.post('http://localhost:3000/login',params);
  }
  handleUserName(e) : any {
    this.setState({
      userName : e.target.value
    })
  }
  handlePassword(e) : any {
    this.setState({
      password : e.target.value
    })
  }
  handleYzId(e) : any {
    this.setState({
      yzNoId : e.target.value
    })
  }
  setHash() {
    this.setState({
      hash : Math.random()
    })
  }
  render(){
    const { userName, password, yzNoId } = this.state;
    return(
      <div>
        <div className="nav-wrap">
          <ul className="nav">
            <li><Link to="/home">首页</Link></li>
            <li><Link to="/imgLoad">上传</Link></li>
            <li><Link to="/login">登陆</Link></li>
          </ul>
        </div>
        <div className="content">
          <div className="login-warp">
            <p>
              <input type="text" className="username" value={userName} onChange={this.handleUserName.bind(this)} placeholder="用户名"/>
            </p>
            <p>
              <input type="text" className="password" value={password} onChange={this.handlePassword.bind(this)} placeholder="密码"/>  
            </p>  
            <p>
              <input type="text" className="yz" value={yzNoId} onChange={this.handleYzId.bind(this)} placeholder="验证码"/>
              <img src={"http://localhost:3000/captcha?aaa="+this.state.hash} className="yz-img" onClick={this.setHash.bind(this)} />  
            </p>
            <p>
              <input type="button" className="submit" value="登陆" onClick={this.sbumit.bind(this,{userName:userName,password:password,captcha:yzNoId})} /> 
            </p>  
          </div>
        </div>
      </div>
    )
  }
}

这样只要点击img,就会随机生成一个hash,然后就会调用新的图片出来。

接着我们进行登录验证。

loginer方法就是进行登录验证的。

拿到用户的用户名信息,密码以及验证码一次对比,最后返回登录是否成功数据。

当用户登陆成功以后,下次登录就不需要再次登录了,以往的方法可以选则session或者cookie的方式,在这里我们使用token。因为现在已经实现了前后端分离开发,我们更倾向于构建单页面配合ajax构建应用。而token最适合这种开发模式不过了。

token登录验证

token是一串经过加密的字符串,登录成功以后返回给用户保存,然后用户在请求接口时,都带这个token。所以我们需要对token进行加密。

Json Web Token就是专门解决这个问题的,原理就不做详解了,其实就是按照一定的方式得到一个字符串,然后在通过某种方式解开。

我们要做的第一步就是

当用户登录成功后,创建一个token返回给用户。

第二步:用户拿到token后应该把token存到本地。

第三步:需要写一个中间层,每次用户请求时我们验证用户携带的token是否正确。正确返回数据,不正确返回警告。
用户每次请求数据的时候要在header里把token带上。

第一步:还是controller/login.js

var rf = require('fs');
var jwt = require('jsonwebtoken');
var captchapng = require('captchapng');
var Tokens = require('../middleware/token')
var t = new Tokens;
class Login {
  constructor(){}
  captcha(req, res, next) {
    var str = parseInt(Math.random()*9000+1000);  //随机生成数字
    req.session.captcha = str;  // 存入session
    var p = new captchapng(80, 30, str); //生成图片
    p.color(0, 0, 0, 0);
    p.color(80, 80, 80, 255);
    var img = p.getBase64();
    var imgbase64 = new Buffer(img, 'base64');
    res.writeHead(200, {
      'Content-Type': 'image/png'
    });
    res.end(imgbase64);
  }
  loginer(req, res, next) {
    let captcha = req.body.captcha;
    let userName = req.body.userName;
    let password = req.body.password;
    if (captcha != req.session.captcha) {
      res.status(400).send({
        message: '验证码错误'
      });
    }else if(userName == "chenxuehui" && password == "123321"){
      // 设置token
      var datas = {userName:"chenxuehui"}
        //调用../middleware/token 下方法设置
      var token = t.setToken('cxh',300,datas)
      res.json({"code":100,"verson":true,"msg":"登陆成功","token":token});
    }else{
      res.json({"code":0,"verson":false,"msg":"密码错误"});
    }
  }
}

module.exports = Login

这次在loginer方法里面我们加入设置token,并返回给用户。setToken方法是设置token的方法。

第二步:用户拿到后保存。

在login.tsx就变成如下

import * as React from 'react'
import * as ReactDom from 'react-dom'
import {Link, browserHistory} from 'react-router';
import * as axios from 'axios';
export default class Login extends React.Component<any,any>{
  constructor(props){
    super(props)
    this.state = {
      userName : '',
      password : '',
      yzNoId  : '',
      hash : Math.random()
    }
  }
  public async sbumit(params : any) : Promise<any>{
    let res = await axios.post('http://localhost:3000/login',params);
    if(res.data.verson){
      sessionStorage.setItem('token',res.data.token);
      browserHistory.push("/home")
    }
  }
  handleUserName(e) : any {
    this.setState({
      userName : e.target.value
    })
  }
  handlePassword(e) : any {
    this.setState({
      password : e.target.value
    })
  }
  handleYzId(e) : any {
    this.setState({
      yzNoId : e.target.value
    })
  }
  setHash() {
    this.setState({
      hash : Math.random()
    })
  }
  render(){
    const { userName, password, yzNoId } = this.state;
    return(
      <div>
        <div className="nav-wrap">
          <ul className="nav">
            <li><Link to="/home">首页</Link></li>
            <li><Link to="/imgLoad">上传</Link></li>
            <li><Link to="/login">登陆</Link></li>
          </ul>
        </div>
        <div className="content">
          <div className="login-warp">
            <p>
              <input type="text" className="username" value={userName} onChange={this.handleUserName.bind(this)} placeholder="用户名"/>
            </p>
            <p>
              <input type="text" className="password" value={password} onChange={this.handlePassword.bind(this)} placeholder="密码"/>  
            </p>  
            <p>
              <input type="text" className="yz" value={yzNoId} onChange={this.handleYzId.bind(this)} placeholder="验证码"/>
              <img src={"http://localhost:3000/captcha?aaa="+this.state.hash} className="yz-img" onClick={this.setHash.bind(this)} />  
            </p>
            <p>
              <input type="button" className="submit" value="登陆" onClick={this.sbumit.bind(this,{userName:userName,password:password,captcha:yzNoId})} /> 
            </p>  
          </div>
        </div>
      </div>
    )
  }
}

在sbumit方法里我们将token放到了sessonstorage里面。

第三步:设置中间件每次请求接口时,验证token,如果解析成功加入到请求头里面。

./middleware/token.js

var jwt = require('jsonwebtoken');
class Tokens {
  constructor(){}
  testToken(req,res,next) {
    var token = req.body.token || req.query.token || req.headers['x-access-token'];
       
    if(token) {
            //存在token,解析token
      jwt.verify(token, 'cxh' , function(err,decoded) {
        if(err) {
                   // 解析失败直接返回失败警告
          return res.json({success:false,msg:'token错误'})
        }else {
                   //解析成功加入请求信息,继续调用后面方法
          req.userInfo = decoded;
          next()
        }
      })
    }else {
      return res.status(403).send({success:false,msg:"没有token"})
    }
  }
  setToken(name,time,data) {
    var jwtSecret = name;
    var token = jwt.sign(data, jwtSecret, {
      expiresIn: time
    })
    return token;
  }
}
module.exports = Tokens

testToken方法是验证token,setToken是设置token方法

假如没有登录请求是这样的

node.js+captchapng+jsonwebtoken实现登录验证示例

在 router/index.js

var express = require('express');
var router = express.Router();
var rf = require('fs');
var Login = require('./controller/login');
var Tokens = require('./middleware/token')
var t = new Tokens;
var login = new Login;
//主页
router.get('/', function(req, res, next) {
  res.render("wap/index")
});
//获取图片验证码
router.get('/captcha', login.captcha);
//登录验证
router.post('/login',login.loginer);
//请求数据时 t.testToken 验证token
router.post('/list',t.testToken,function(req, res, next){
  res.json({
    //在请求信息里面拿到数据
    username : req.userInfo.userName,
    success : true,
    result : [
      {
        name:'1111111'
      },
      {
        name :'22222'
      }
    ]
  })
})
module.exports = router;

我们在另一个页面调用list接口试一下

import * as axios from 'axios';
import { transToken } from '../decorator/index'


class Home extends React.Component<any,any>{
  constructor(props){
    super(props)
    this.state = {
      data : ''
    }
  }
  async getList(): Promise<any>{
    let token = sessionStorage.getItem('token');
    const config = {
     // 请求头信息
     headers: {'x-access-token': token}      
    }
    let res = await axios.post('http://localhost:3000/list',{},config);
    if(!res.data.success){
      browserHistory.push('/login');
      return;
    }
    this.setState({
      data : res.data
    })
  }
  render(){
    const { data } = this.state;
    return(
      <div>
        <div className="nav-wrap">
          <ul className="nav">
            <li><Link to="/home">首页</Link></li>
            <li><Link to="/imgLoad">上传</Link></li>
            <li><Link to="/login">登陆</Link></li>
          </ul>
        </div>
        <div className="content">
          Home
          <span onClick={this.getList.bind(this)}>获取数据</span>
          <div>{
            data ? data.result.map( (val,k) => {
              return <li key = {k}>{val.name}</li>
            }) : null
          }</div>
        </div>
      </div>
      
    )
  }
}
export default Home

当调用getList时,如果此时没有登录res.data.success就会为false,则跳到登录页。

全部代码

node.js

app.js

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require("express-session");
var ejs = require('ejs');

var index = require('./routes/index');


var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
// app.set('view engine', 'jade');
app.engine('html', ejs.__express);
app.set('view engine', 'html');
app.use(session({
  secret:"dabao",
  resave:false,
  saveUninitialized:true,
  cookie:{}
}));
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser({limit: 5000000}));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, '')));

app.use('/', index);


// catch 404 and forward to error handler
app.use(function(req, res, next) {
 var err = new Error('Not Found');
 err.status = 404;
 next(err);
});

// error handler
app.use(function(err, req, res, next) {
 // set locals, only providing error in development
 res.locals.message = err.message;
 res.locals.error = req.app.get('env') === 'development' ? err : {};

 // render the error page
 res.status(err.status || 500);
 res.render('error');
});

module.exports = app;

index.js

var express = require('express');
var router = express.Router();
var rf = require('fs');
var Login = require('./controller/login');
var Tokens = require('./middleware/token')
var t = new Tokens;
var login = new Login;
/* GET home page. */
router.get('/', function(req, res, next) {
  res.render("wap/index")
});
router.post('/upLoadImg',function(req,res,next){
  let imgData = req.body.imgData;
  console.log(imgData)
  let base64Data = imgData.replace(/^data:image\/\w+;base64,/, "");
  let dataBuffer = new Buffer(base64Data, 'base64');
  let timer = Number( new Date() );
  console.log(timer)
  rf.writeFile("views/images/artCover"+timer+".png",dataBuffer, function(err) {
    if(err) {
     res.json({"code":400,"verson":false,"msg":err});
    }else {
     res.json({"code":100,"verson":true,"url":"views/src/common/images/artCover/"+timer+".png"});
    }
  });
})
router.get('/captcha', login.captcha);
router.post('/login',login.loginer);
router.post('/list',t.testToken,function(req, res, next){
  // 先解析token 
  console.log(req.userInfo)
  res.json({
    username : req.userInfo.userName,
    success : true,
    result : [
      {
        name:'1111111'
      },
      {
        name :'22222'
      }
    ]
  })
})
module.exports = router;

controller/login.js

var rf = require('fs');
var jwt = require('jsonwebtoken');
var captchapng = require('captchapng');
var Tokens = require('../middleware/token')
var t = new Tokens;
class Login {
  constructor(){}
  captcha(req, res, next) {
    var str = parseInt(Math.random()*9000+1000);  //随机生成数字
    req.session.captcha = str;  // 存入session
    var p = new captchapng(80, 30, str); //生成图片
    p.color(0, 0, 0, 0);
    p.color(80, 80, 80, 255);
    var img = p.getBase64();
    var imgbase64 = new Buffer(img, 'base64');
    res.writeHead(200, {
      'Content-Type': 'image/png'
    });
    res.end(imgbase64);
  }
  loginer(req, res, next) {
    let captcha = req.body.captcha;
    let userName = req.body.userName;
    let password = req.body.password;
    if (captcha != req.session.captcha) {
      res.status(400).send({
        message: '验证码错误'
      });
    }else if(userName == "chenxuehui" && password == "123321"){
      // 设置token
      var datas = {userName:"chenxuehui"}
      var token = t.setToken('cxh',300,datas)
      res.json({"code":100,"verson":true,"msg":"登陆成功","token":token});
    }else{
      res.json({"code":0,"verson":false,"msg":"密码错误"});
    }
  }
}
module.exports = Login

middleware/token.js

var jwt = require('jsonwebtoken');

class Tokens {
  constructor(){}
  testToken(req,res,next) {
    var token = req.body.token || req.query.token || req.headers['x-access-token'];

    if(token) {
      jwt.verify(token, 'cxh' , function(err,decoded) {
        if(err) {
          return res.json({success:false,msg:'token错误'})
        }else {
          req.userInfo = decoded;
          next()
        }
      })
    }else {
      return res.status(403).send({success:false,msg:"没有token"})
    }
  }
  setToken(name,time,data) {
    var jwtSecret = name;
    var token = jwt.sign(data, jwtSecret, {
      expiresIn: time
    })
    return token;
  }
}
module.exports = Tokens

react部分

login.tsx

import * as React from 'react'
import * as ReactDom from 'react-dom'
import {Link, browserHistory} from 'react-router';
import * as axios from 'axios';
export default class Login extends React.Component<any,any>{
  constructor(props){
    super(props)
    this.state = {
      userName : '',
      password : '',
      yzNoId  : '',
      hash : Math.random()
    }
  }
  public async sbumit(params : any) : Promise<any>{
    let res = await axios.post('http://localhost:3000/login',params);
    if(res.data.verson){
      sessionStorage.setItem('token',res.data.token);
      browserHistory.push("/home")
    }
  }
  handleUserName(e) : any {
    this.setState({
      userName : e.target.value
    })
  }
  handlePassword(e) : any {
    this.setState({
      password : e.target.value
    })
  }
  handleYzId(e) : any {
    this.setState({
      yzNoId : e.target.value
    })
  }
  setHash() {
    this.setState({
      hash : Math.random()
    })
  }
  render(){
    const { userName, password, yzNoId } = this.state;
    return(
      <div>
        <div className="nav-wrap">
          <ul className="nav">
            <li><Link to="/home">首页</Link></li>
            <li><Link to="/imgLoad">上传</Link></li>
            <li><Link to="/login">登陆</Link></li>
          </ul>
        </div>
        <div className="content">
          <div className="login-warp">
            <p>
              <input type="text" className="username" value={userName} onChange={this.handleUserName.bind(this)} placeholder="用户名"/>
            </p>
            <p>
              <input type="text" className="password" value={password} onChange={this.handlePassword.bind(this)} placeholder="密码"/>  
            </p>  
            <p>
              <input type="text" className="yz" value={yzNoId} onChange={this.handleYzId.bind(this)} placeholder="验证码"/>
              <img src={"http://localhost:3000/captcha?aaa="+this.state.hash} className="yz-img" onClick={this.setHash.bind(this)} />  
            </p>
            <p>
              <input type="button" className="submit" value="登陆" onClick={this.sbumit.bind(this,{userName:userName,password:password,captcha:yzNoId})} /> 
            </p>  
          </div>
        </div>
      </div>
    )
  }
}

home.js 获取列表信息

import * as React from 'react'
import * as ReactDom from 'react-dom'
import {Link, browserHistory} from 'react-router';
import * as axios from 'axios';
class Home extends React.Component<any,any>{
  constructor(props){
    super(props)
    this.state = {
      data : ''
    }
  }
  async getList(): Promise<any>{
    let token = sessionStorage.getItem('token');
    const config = {
     // 请求头信息
     headers: {'x-access-token': token}      
    }
    let res = await axios.post('http://localhost:3000/list',{},config);
    if(!res.data.success){
      browserHistory.push('/login');
      return;
    }
    this.setState({
      data : res.data
    })
  }
  render(){
    const { data } = this.state;
    return(
      <div>
        <div className="nav-wrap">
          <ul className="nav">
            <li><Link to="/home">首页</Link></li>
            <li><Link to="/imgLoad">上传</Link></li>
            <li><Link to="/login">登陆</Link></li>
          </ul>
        </div>
        <div className="content">
          Home
          <span onClick={this.getList.bind(this)}>获取数据</span>
          <div>{
            data ? data.result.map( (val,k) => {
              return <li key = {k}>{val.name}</li>
            }) : null
          }</div>
        </div>
      </div>
      
    )
  }
}
export default Home

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js 利用image对象实现图片的预加载提高访问速度
Mar 29 Javascript
JS 面向对象之继承---多种组合继承详解
Jul 10 Javascript
JavaScript中闭包的详解
Apr 01 Javascript
angular2 ng build部署后base文件路径问题详细解答
Jul 15 Javascript
JavaScript实现离开页面前提示功能【附jQuery实现方法】
Sep 26 jQuery
vue使用rem实现 移动端屏幕适配
Sep 26 Javascript
JavaScript静态作用域和动态作用域实例详解
Jun 17 Javascript
新手快速入门JavaScript装饰者模式与AOP
Jun 24 Javascript
JS端基于download.js实现图片、视频时直接下载而不是打开预览
May 09 Javascript
谈一谈vue请求数据放在created好还是mounted里好
Jul 27 Javascript
一篇文章让你搞懂JavaScript 原型和原型链
Nov 23 Javascript
JavaScript实现消消乐的源代码
Jan 12 Javascript
javascript 日期相减-在线教程(附代码)
Aug 17 #Javascript
微信小程序引用公共js里的方法的实例详解
Aug 17 #Javascript
微信小程序中使用Promise进行异步流程处理的实例详解
Aug 17 #Javascript
史上最全JavaScript数组去重的十种方法(推荐)
Aug 17 #Javascript
AngularJS通过ng-Img-Crop实现头像截取的示例
Aug 17 #Javascript
Javascript中的getter和setter初识
Aug 17 #Javascript
简单实现js进度条加载效果
Mar 25 #Javascript
You might like
set_include_path在win和linux下的区别
2008/01/10 PHP
用PHP程序实现支持页面后退的两种方法
2008/06/30 PHP
PHP 错误之引号中使用变量
2009/05/04 PHP
php中通过虚代理实现延迟加载的实现代码
2011/06/10 PHP
php递归创建目录的方法
2015/02/02 PHP
PHP中把错误日志保存在系统日志中(Windows系统)
2015/06/23 PHP
46 个非常有用的 PHP 代码片段
2016/02/16 PHP
PHPCMS忘记后台密码的解决办法
2016/10/30 PHP
CCPry JS类库 代码
2009/10/30 Javascript
window.name代替cookie的实现代码
2010/11/28 Javascript
jQuery1.6 正式版发布并提供下载
2011/05/05 Javascript
JQuery datepicker 使用方法
2011/05/20 Javascript
javascript下拉列表菜单的实现方法
2015/11/18 Javascript
JS公共小方法之判断对象是否为domElement的实例
2016/11/25 Javascript
nodejs个人博客开发第一步 准备工作
2017/04/12 NodeJs
JS异步处理的进化史深入讲解
2019/08/25 Javascript
vue调用本地摄像头实现拍照功能
2020/08/14 Javascript
nginx部署多个vue项目的方法示例
2020/09/06 Javascript
angular共享依赖的解决方案分享
2020/10/15 Javascript
用pywin32实现windows模拟鼠标及键盘动作
2014/04/22 Python
朴素贝叶斯算法的python实现方法
2014/11/18 Python
Python中使用异常处理来判断运行的操作系统平台方法
2015/01/22 Python
python判断设备是否联网的方法
2018/06/29 Python
python爬取盘搜的有效链接实现代码
2019/07/20 Python
wxPython电子表格功能wx.grid实例教程
2019/11/19 Python
Python下划线5种含义代码实例解析
2020/07/10 Python
Python关于拓扑排序知识点讲解
2021/01/04 Python
HTML5的结构和语义(2):结构
2008/10/17 HTML / CSS
肯尼亚网上商城:Kilimall
2016/08/20 全球购物
管理学专业个人求职信范文
2013/12/13 职场文书
给物业的表扬信
2014/01/21 职场文书
大型晚会策划方案
2014/02/06 职场文书
旅游节目策划方案
2014/05/26 职场文书
武当山导游词
2015/02/03 职场文书
思品教学工作总结
2015/08/10 职场文书
小学英语教学经验交流材料
2015/11/02 职场文书