安全建议请参考此总结开发安全API的checklist安装gitclonehttps://github.com/Nicksapp/nAuth-restful-api。gitrunnpminstallconfig.js中具体的数据库配置信息,搭建整体框架,然后再开发我们设计的概念路由设计POST/api/signup:用户注册POST/api/user/accesstoken:账户验证,获取tokenGET/api/users/info:获取用户信息,需要验证用户Modeldesignname:用户名password:密码token:验证相关token关于RESTfulAPI网上已经有很多介绍,这里不再赘述。我想说的是它的主要作用,就是对于今天的网络应用来说,它分为前端和后端两部分。但目前的发展趋势是应用平台需求的扩展(IOS、Android、Webapp等)。因此,需要一种统一的机制来方便前端设备与不同应用平台的后端之间的通信,即前后端分离。这导致了API架构的流行,甚至出现了“APIFirst”的设计思想。RESTfulAPI是一套比较成熟的面向互联网应用的API设计理论。技术栈我们的路由设计使用Node.js上的Express框架,Mongoose连接Mongodb数据库并交互,使用Postman调试我们设计的Api。让我们开始吧!API设计中token的思想在API设计中,TOKEN用于判断用户是否有访问API的权限。TOKEN不需要先编解码。一般来说,TOKEN是对一些用户名+时间等内容进行MD5不可逆加密。然后通过一张USER_TOKEN表来判断用户请求中包含的TOKEN是否与USER_TOKEN表中的TOKEN一致。具体的实践过程主要是:设置一个key比如key='2323dsfadfewrasa3434'。此密钥只有发送方和接收方知道。调用时,发送方结合各种参数,使用key按照一定的规则(各种sorts,MD5,ip等)生成一个access_key。将帖子一起提交到API接口。接收者从post和access_key中获取参数。和发送一样,使用keykey对每个参数(各种sorts,MD5,ip等)进行相同的规则,生成一个access_key2。比较access_key和access_key2。相同的。如果不相同,则报错并返回或加入黑名单。token设计的具体做法废话不多说,先来看看我们的干货吧。这次我们选择Node.js+experss和Mongoose进入RESTtoken实践项目地址:GitHub地址或者gitclonehttps://github.com/Nicksapp/nAuth-restful-api.git新建一个项目先看看我们的项目文件夹-routes/----index.js----users.js-models/---user.js-config.js-package.json-passport.js-index.jsnpminit创建我们的包.json然后在项目根文件夹下安装我们需要的依赖npminstallexpressbody-parsermorganmongoosejsonwebtokenbcryptpassportpassport-http-bearer--saveexpress:我们主要的开发框架mongoose:用来和MongoDB数据库交互的框架,请提前在PC上安装MongoDBmorgan:会在Terminal中显示程序请求过程的信息,方便我们调试代码我们的tokenpassport:一个非常流行的权限验证库bcrypt:用户密码的hash加密--savewrite将我们安装的库文件放到package.json的dependencies里面,这样其他人在打开项目的时候就可以正确安装需要的依赖了。用户模型定义了我们需要的用户模型,对于moogoose,新建models/user.jsconstmongoose=require('mongoose');constSchema=mongoose.Schema;constbcrypt=require('bcrypt');constUserSchema=newSchema({name:{type:String,unique:true,//不可重复约束require:true//不可空约束},password:{type:String,require:true},token:{type:String}});//添加用户保存时,中间件用bcrypt加密密码,这样用户密码只有自己知道UserSchema.pre('save',function(next){varuser=this;if(this.isModified('password')||this.isNew){bcrypt.genSalt(10,function(err,salt){if(err){returnnext(err);}bcrypt.hash(user.password,salt,function(err,hash){if(err){returnnext(err);}user.password=hash;next();});});}else{returnnext();}});//检查用户输入的密码是否正确UserSchema.methods.comparePassword=function(passw,cb){bcrypt.compare(passw,this.password,(err,isMatch)=>{if(err){returncb(err);}cb(null,isMatch);});};module.exports=mongoose.model('User',UserSchema);配置文件./config.js用于配置我们的MongoDB数据库连接和令牌密钥module.exports={'secret':'learnRestApiwithNickjs',//在我们创建和验证JSONWebTokens时使用'database':'mongodb://localhost:27017/test'//填写本地mongodb连接地址,xxx是数据表名};本地服务器配置./index.js服务器配置文件也是程序的入口。这里我们主要用它来包含我们程序需要加载的库文件和调用初始化程序需要的依赖。constexpress=require('express');constapp=express();constbodyParser=require('body-parser');//解析body字段模块constmorgan=require('morgan');//命令行日志显示constmongoose=require('mongoose');constpassport=require('passport');//用户认证模块passportconstStrategy=require('passport-http-bearer').Strategy;//token验证模块constroutes=require('./routes');constconfig=require('./config');让port=process.env.PORT||8080;app.use(passport.initialize());//初始化passport模块app.use(morgan('dev'));//命令行显示程序运行日志,方便app调试bug.use(bodyParser.urlencoded({extended:false}));app.use(bodyParser.json());//调用bodyParser模块,使程序能够正确解析body的传入值routes(app);//导入mongoose.Promise=global.Promise;mongoose.connect(config.database);//连接数据库app.listen(port,()=>{console.log('listeningonport:'+port);})路由配置./routes主要存放路由相关的文件./routes/index.js通用路由入口,导入使用的路由module.exports=(app)=>{app.get('/',(req,res)=>{res.json({message:'helloindex!'});});app.use('/api',require('./users'));//在所有用户路由之前添加/api};./routes/users.jsconstexpress=require('express');constUser=require('../models/user');constjwt=require('jsonwebtoken');constconfig=require('../config');constpassport=require('passport');constrouter=express.Router();require('../passport')(passport);//注册账号router.post('/signup',(req,res)=>{if(!req.body.name||!req.body.password){res.json({success:false,message:'请输入您的账户密码'});}else{varnewUser=newUser({//在库中创建一个新用户用户名:req.body.name,密码:req.body.password});//保存用户账号newUser.save((err)=>{if(err){returnres.json({success:false,message:'Registrationfailed!'});}res.json({success:true,message:'Newusersuccessfulcreated!'});});}});//检查用户名和密码,如果验证则生成accesstoken通过router.post('/user/accesstoken',(req,res)=>{User.findOne({//根据用户名:req.body.name查找用户名是否存在},(err,user)=>{如果(呃r){抛出错误;}if(!user){res.json({success:false,message:'认证失败,用户不存在!'});}elseif(user){//检查密码是否正确user.comparePassword(req.body.password,(err,isMatch)=>{if(isMatch&&!err){vartoken=jwt.sign({name:user.name},config.secret,{expiresIn:10080//token过期销毁时间设置});user.token=token;user.save(function(err){if(err){res.send(err);}});res.json({success:true,message:'认证成功!',token:'Bearer'+token,name:user.name});}else{res.send({success:false,message:'认证失败,密码错误!'});}});}});});//passport-http-bearertoken中间件验证//通过header->Bearer+token发送授权//或者传递?access_token=tokenrouter.get('/user/user_info',passport.authenticate('承载',{会话:假}),function(req,res){res.json({username:req.user.name});});module.exports=router;passportconfiguration./passport.jsconfigurationpermissionmodulerequiredfunctionsconstpassport=require('护照');constStrategy=require('passport-http-bearer').Strategy;constUser=require('./models/user');constconfig=require('./config');module.exports=function(passport){passport.use(newStrategy(function(token,done){User.findOne({token:token},function(err,user){if(err){returndone(err);}if(!user){returndone(null,false);}returndone(null,user);});}));};主要验证发送的token值是否与用户服务器端的token值匹配,并进行信息验证Debug现在我们可以运行我们的代码,看看具体的操作过程!为了方便调试和收发参数,我们使用postman(可以安装在Chrome或Mac上)进行操作。nodeindex运行我们本地服务器,访问[localhost:8080/]()应该可以看到我们返回的初始json值设置好了,我们继续深入测试。POST访问[localhost:8080/api/signup](),我们来注册一个新用户,注意将body的Content-Type设置为x-www-form-urlencoded,这样我们的body-parser才能正确解析,好的,我们成功模拟了新用户的创建。连接数据库看看我们的用户信息是否也正确存储了(注意:我使用的是MongoChef,一款非常强大的MongoDB数据库管理软件),可以看到我的密码也被正确加密保存了。然后POST访问[localhost:8080/api/user/accesstoken]()为我的用户获取专用令牌。POST过程跟注册相关,可以看到我们的token值也正确生成了。查看我们数据库中的用户信息,token值也已经存进去了,方便我们后面验证权限。GET访问[localhost:8080/api/user/user_info](),同时在Header中传入我们的token值作为Authorization:token,如果用户名获取正确,则说明我们的访问请求通过了验证。如果token值不正确,返回HTTP状态码401Unauthorized,拒绝访问请求。至此,我们的权限验证功能就基本实现了。总结希望看完本教程后,能对大家在RESTfulApi的开发中有所启发。小学生一知半解,过程中有什么不足之处欢迎指正。
