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代码
Jul 01 Javascript
为指定元素增加样式的js代码
Dec 09 Javascript
简单的Jquery遮罩层代码实例
Nov 14 Javascript
JavaScript中的异常捕捉介绍
Dec 31 Javascript
浅析jquery如何判断滚动条滚到页面底部并执行事件
Apr 29 Javascript
noty ? jQuery通知插件全面解析
May 18 Javascript
jQuery简单实现tab选项卡切换效果
Jun 20 Javascript
AngularJs实现聊天列表实时刷新功能
Jun 15 Javascript
微信小程序picker组件简单用法示例【附demo源码下载】
Dec 05 Javascript
angular4 JavaScript内存溢出问题
Mar 06 Javascript
Layui多选只有最后一个值的解决方法
Sep 02 Javascript
JavaScript 中的六种循环方法
Jan 06 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
php配合jquery实现增删操作具体实例
2013/12/12 PHP
PHP三元运算的2种写法代码实例
2014/05/12 PHP
ThinkPHP的模版中调用session数据的方法
2014/07/01 PHP
四个PHP非常实用的功能
2015/09/29 PHP
PHP文件上传类实例详解
2016/04/08 PHP
PHP5.5.15+Apache2.4.10+MySQL5.6.20配置方法分享
2016/05/06 PHP
PHP会话控制实例分析
2016/12/24 PHP
php实现页面纯静态的实例代码
2017/06/21 PHP
PHP PDOStatement::setFetchMode讲解
2019/02/03 PHP
详解PHP中curl_multi并发的实现
2020/06/08 PHP
Web开发者必备的12款超赞jQuery插件
2010/12/03 Javascript
JQuery中SetTimeOut传参问题探讨
2013/05/10 Javascript
jquery插件冲突(jquery.noconflict)解决方法分享
2014/03/20 Javascript
JS+CSS实现自适应选项卡宽度的圆角滑动门效果
2015/09/15 Javascript
vue.js 表格分页ajax 异步加载数据
2016/10/18 Javascript
浅谈angularjs中响应回车事件
2017/04/24 Javascript
详解在Angular项目中添加插件ng-bootstrap
2017/07/04 Javascript
js+html5实现半透明遮罩层弹框效果
2020/08/24 Javascript
JS笛卡尔积算法与多重数组笛卡尔积实现方法示例
2017/12/01 Javascript
Cocos2d实现刮刮卡效果
2018/12/20 Javascript
使用jQuery实现掷骰子游戏
2019/10/24 jQuery
js实现登录拖拽窗口
2020/02/10 Javascript
autojs 蚂蚁森林能量自动拾取即给指定好友浇水的实现方法
2020/05/03 Javascript
Javascript新手入门之字符串拼接与变量的应用
2020/12/03 Javascript
django Serializer序列化使用方法详解
2018/10/16 Python
python通过txt文件批量安装依赖包的实现步骤
2019/08/13 Python
如何理解transaction事务的概念
2015/05/27 面试题
园林资料员岗位职责
2013/12/30 职场文书
新闻专业毕业生英文求职信
2014/03/19 职场文书
党的群众路线教育实践活动个人承诺书
2014/05/22 职场文书
简历中自我评价范文
2015/03/11 职场文书
2015银行年终工作总结范文
2015/05/26 职场文书
导游词之云南-元阳梯田
2019/10/08 职场文书
《帝国时代4》赛季预告 新增内容编译器可创造地图
2022/04/03 其他游戏
Ruby使用Mysql2连接操作MySQL
2022/04/19 Ruby
Nginx 配置 HTTPS的详细过程
2022/05/30 Servers