-
Notifications
You must be signed in to change notification settings - Fork 0
/
db.json
1 lines (1 loc) · 389 KB
/
db.json
1
{"meta":{"version":1,"warehouse":"2.2.0"},"models":{"Asset":[{"_id":"themes/yilia/source/slider.e37972.js","path":"slider.e37972.js","modified":0,"renderable":1},{"_id":"themes/yilia/source/main.0cf68a.css","path":"main.0cf68a.css","modified":0,"renderable":1},{"_id":"source/assets/image/favicon.ico","path":"assets/image/favicon.ico","modified":0,"renderable":0},{"_id":"themes/yilia/source/main.0cf68a.js","path":"main.0cf68a.js","modified":0,"renderable":1},{"_id":"themes/yilia/source/mobile.992cbe.js","path":"mobile.992cbe.js","modified":0,"renderable":1},{"_id":"themes/yilia/source/img/preloader.gif","path":"img/preloader.gif","modified":0,"renderable":1},{"_id":"themes/yilia/source/img/default-skin.png","path":"img/default-skin.png","modified":0,"renderable":1},{"_id":"themes/yilia/source/fonts/default-skin.b257fa.svg","path":"fonts/default-skin.b257fa.svg","modified":0,"renderable":1},{"_id":"themes/yilia/source/fonts/iconfont.16acc2.ttf","path":"fonts/iconfont.16acc2.ttf","modified":0,"renderable":1},{"_id":"themes/yilia/source/fonts/iconfont.45d7ee.svg","path":"fonts/iconfont.45d7ee.svg","modified":0,"renderable":1},{"_id":"themes/yilia/source/fonts/iconfont.8c627f.woff","path":"fonts/iconfont.8c627f.woff","modified":0,"renderable":1},{"_id":"source/assets/image/avator.jpeg","path":"assets/image/avator.jpeg","modified":0,"renderable":0},{"_id":"themes/yilia/source/fonts/iconfont.b322fa.eot","path":"fonts/iconfont.b322fa.eot","modified":0,"renderable":1},{"_id":"themes/yilia/source/fonts/tooltip.4004ff.svg","path":"fonts/tooltip.4004ff.svg","modified":0,"renderable":1},{"_id":"themes/yilia/source/img/scrollbar_arrow.png","path":"img/scrollbar_arrow.png","modified":0,"renderable":1},{"_id":"source/assets/blog/totoro.jpeg","path":"assets/blog/totoro.jpeg","modified":0,"renderable":0}],"Cache":[{"_id":"themes/yilia/.eslintrc.js","hash":"5696ae049de010ed3786768b0c359f14c05b5ec6","modified":1554618464000},{"_id":"themes/yilia/.eslintignore","hash":"df0a50b13cc00acb749226fee3cee6e0351fb1d9","modified":1554618464000},{"_id":"themes/yilia/.editorconfig","hash":"da6d022b8f4d9c961e2f8f80677e92af8de0db4d","modified":1554618464000},{"_id":"themes/yilia/.gitattributes","hash":"e0f24dceeb1e6878a1dd9b01a2b9df1bc037a867","modified":1554618464000},{"_id":"themes/yilia/README.md","hash":"1bf755806af9d8874bd22e1abbdaaa24328ef4dc","modified":1554618464000},{"_id":"themes/yilia/package.json","hash":"367cb9579d35968a942c243ab248a5f5ebfaf462","modified":1554618464000},{"_id":"themes/yilia/.babelrc","hash":"b1b76475ac17dc9e2fa50af96c9e31eea2d0f2b4","modified":1554618464000},{"_id":"themes/yilia/.gitignore","hash":"9c4b7d27a1e3e5efa0c8ed143a032a85d586b03b","modified":1554618464000},{"_id":"themes/yilia/webpack.config.js","hash":"05ba46a4ae744272f5312e684928910dccad3755","modified":1554618464000},{"_id":"source/_posts/demo.md","hash":"cc508572e2e07cb68e4e7e75e07c5d6e5130b3c1","modified":1554730898046},{"_id":"source/_posts/git-version-control-basics.md","hash":"a74745bb878f87494f7edd6aef548f393df3a913","modified":1560100872948},{"_id":"source/_posts/networkSecurity-web-ClickJacking.md","hash":"833355a6aadc3c37e260d9e35869fc068db0e2c2","modified":1560100872949},{"_id":"source/_posts/node-server-deploy.md","hash":"c3d972ec99a8ebc07e40bbe6ffb8a1c885f31f72","modified":1560100872949},{"_id":"source/_posts/python-health-check-util.md","hash":"64387d75f9f3de417a49ccda908a7d49a11144ed","modified":1560100872950},{"_id":"source/_posts/http-learn-notes.md","hash":"1b2eee9c2acbb2a645d51c6cde893b0148788269","modified":1560100872948},{"_id":"source/_posts/python-lib-unittest.md","hash":"46729cecb71d2a3989130c7cc7f4bd39e4901dae","modified":1560100872950},{"_id":"source/_posts/python-tutorial-record.md","hash":"743e6e01c874eb8120553897f33a76ae10393abe","modified":1560100872952},{"_id":"source/_posts/python-module-requests.md","hash":"ab203f17ecef78ea61e9497605cc2f5c7bfaf1e8","modified":1560100872952},{"_id":"source/_posts/ssh-generate-SSH-key-and-deploy-it.md","hash":"7c3c96b26a777dba66b8c6961e518a59b1685e86","modified":1560100872953},{"_id":"source/_posts/web-secure.md","hash":"09eca3782e9b70b49b373e2e08acb8fbbb6ab4b2","modified":1560209944560},{"_id":"themes/yilia/layout/archive.ejs","hash":"2703b07cc8ac64ae46d1d263f4653013c7e1666b","modified":1554618464000},{"_id":"themes/yilia/layout/category.ejs","hash":"765426a9c8236828dc34759e604cc2c52292835a","modified":1554618464000},{"_id":"themes/yilia/layout/index.ejs","hash":"a35dc900203f9d8dd863ea4c1722198d6d457ec8","modified":1554618464000},{"_id":"themes/yilia/_config.yml","hash":"886f646dfc0b7d16e99a0979e4d4f7ac865fbdfb","modified":1554697856000},{"_id":"themes/yilia/layout/page.ejs","hash":"7d80e4e36b14d30a7cd2ac1f61376d9ebf264e8b","modified":1554618464000},{"_id":"themes/yilia/layout/post.ejs","hash":"7d80e4e36b14d30a7cd2ac1f61376d9ebf264e8b","modified":1554618464000},{"_id":"themes/yilia/layout/tag.ejs","hash":"eaa7b4ccb2ca7befb90142e4e68995fb1ea68b2e","modified":1554618464000},{"_id":"themes/yilia/languages/nl.yml","hash":"12ed59faba1fc4e8cdd1d42ab55ef518dde8039c","modified":1554618464000},{"_id":"themes/yilia/languages/default.yml","hash":"3083f319b352d21d80fc5e20113ddf27889c9d11","modified":1554618464000},{"_id":"themes/yilia/languages/fr.yml","hash":"84ab164b37c6abf625473e9a0c18f6f815dd5fd9","modified":1554618464000},{"_id":"themes/yilia/languages/no.yml","hash":"965a171e70347215ec726952e63f5b47930931ef","modified":1554618464000},{"_id":"themes/yilia/languages/ru.yml","hash":"4fda301bbd8b39f2c714e2c934eccc4b27c0a2b0","modified":1554618464000},{"_id":"themes/yilia/layout/layout.ejs","hash":"0a332bdbd3b86c231d690614687f5b97186b85d5","modified":1554618464000},{"_id":"themes/yilia/languages/zh-tw.yml","hash":"53ce3000c5f767759c7d2c4efcaa9049788599c3","modified":1554618464000},{"_id":"themes/yilia/languages/zh-CN.yml","hash":"ca40697097ab0b3672a80b455d3f4081292d1eed","modified":1554618464000},{"_id":"themes/yilia/source/slider.e37972.js","hash":"6dec4e220c89049037eebc44404abd8455d22ad7","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/toc.ejs","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1554618464000},{"_id":"themes/yilia/source-src/css.ejs","hash":"94dbdb02ca11849e415d54fb28546a598f2cffb1","modified":1554618464000},{"_id":"themes/yilia/source/main.0cf68a.css","hash":"ddf6e2c6b953c2c59a3c271e6070010a4cc81cf9","modified":1554618464000},{"_id":"source/assets/image/favicon.ico","hash":"c7dcd652fa9ed927d0ec2e0580f5de1ae9ddbdff","modified":1554730898049},{"_id":"themes/yilia/source/main.0cf68a.js","hash":"5299bd43c93dabe6eacb96ae9f83271c49c5909d","modified":1554698378000},{"_id":"themes/yilia/source/mobile.992cbe.js","hash":"01b35e71e37aa2849664eb5daf26daede2278398","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/archive-post.ejs","hash":"1f7d4819b7f67602c4a1b99871808d2160b60978","modified":1554618464000},{"_id":"themes/yilia/source-src/script.ejs","hash":"c21381e1317db7bb157f1d182b8c088cb7cba411","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/after-footer.ejs","hash":"b86b248720ad415ec1b5fee53fb583776c776f83","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/aside.ejs","hash":"8edbd7993b9b061611a193533a664e2e85eae748","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/css.ejs","hash":"236f8a377b2e4e35754319c3029bcd4a4115431d","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/archive.ejs","hash":"a6e94061ac55b9eb55275f87b608d62f6ea35659","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/footer.ejs","hash":"f2994e0acd1d606ebf4680afc4fa652e148ccf4e","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/baidu-analytics.ejs","hash":"f0e6e88f9f7eb08b8fe51449a8a3016273507924","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/google-analytics.ejs","hash":"f921e7f9223d7c95165e0f835f353b2938e40c45","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/header.ejs","hash":"6387a93dad7c3d778eb91e3821852fbf6813880c","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/left-col.ejs","hash":"183d7ca4ba8e7c80694ffdc8cf39957092238346","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/head.ejs","hash":"64f092186b5a744aa1603ce22bb1d44a34446add","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/mobile-nav.ejs","hash":"7fbbfabf5e29525b24ada14613c21a26789132b4","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/mathjax.ejs","hash":"151a1ef2173ba7b6789d349f0f8da89616cc1394","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/article.ejs","hash":"630c6ec866d056657d3d91e34b4c64eb993c0654","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/viewer.ejs","hash":"e495790b2abe2290875817e42bd505bc611d3e26","modified":1554618464000},{"_id":"themes/yilia/source/img/preloader.gif","hash":"6342367c93c82da1b9c620e97c84a389cc43d96d","modified":1554618464000},{"_id":"themes/yilia/source/img/default-skin.png","hash":"ed95a8e40a2c3478c5915376acb8e5f33677f24d","modified":1554618464000},{"_id":"themes/yilia/source/fonts/default-skin.b257fa.svg","hash":"2ac727c9e092331d35cce95af209ccfac6d4c7c7","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/tools.ejs","hash":"c41341b9618e591538e1136a2d1637587c1bbd90","modified":1554618464000},{"_id":"themes/yilia/source/fonts/iconfont.16acc2.ttf","hash":"f342ac8bf4d937f42a7d6a0032ad267ab47eb7f2","modified":1554618464000},{"_id":"themes/yilia/source/fonts/iconfont.45d7ee.svg","hash":"f9304e5714d20861be7d8f4d36687e88e86b9e1b","modified":1554618464000},{"_id":"themes/yilia/source/fonts/iconfont.8c627f.woff","hash":"aa9672cb097f7fd73ae5a03bcd3d9d726935bc0a","modified":1554618464000},{"_id":"source/assets/image/avator.jpeg","hash":"614f22cdbeb97afe962d213915c1cf65e3a2c237","modified":1554730898049},{"_id":"themes/yilia/source/fonts/iconfont.b322fa.eot","hash":"bc8c5e88f4994a852041b4d83f126d9c4d419b4a","modified":1554618464000},{"_id":"themes/yilia/source/fonts/tooltip.4004ff.svg","hash":"397fe4b1093bf9b62457dac48aa15dac06b54a3c","modified":1554618464000},{"_id":"themes/yilia/source-src/css/_core.scss","hash":"24f347a2412abbf58318369152504da9538f8d3b","modified":1554618464000},{"_id":"themes/yilia/source-src/css/_function.scss","hash":"93a50dd19a93485712da1f8d0a1672482dd1eabc","modified":1554618464000},{"_id":"themes/yilia/source-src/css/archive.scss","hash":"7d27e22ac898e8fafec14549e940c73cbea1fba8","modified":1554618464000},{"_id":"themes/yilia/source/img/scrollbar_arrow.png","hash":"d64a33c4ddfbdb89deeb6f4e3d36eb84dc4777c0","modified":1554618464000},{"_id":"themes/yilia/source-src/css/article-inner.scss","hash":"d79f2d35a06de83a2a226ca790b7a0a34789c115","modified":1554618464000},{"_id":"themes/yilia/source-src/css/article-main.scss","hash":"3fad68bd74260326f83090b0974dd80707e7bac7","modified":1554618464000},{"_id":"themes/yilia/source-src/css/article-nav.scss","hash":"43e507f2a48504079afd9471353337e23ca47470","modified":1554618464000},{"_id":"themes/yilia/source-src/css/comment.scss","hash":"cafe3834017a3bf47420f37543725025225a2c89","modified":1554618464000},{"_id":"themes/yilia/source-src/css/article.scss","hash":"0f6d61af99ed4db87f8589db1feaea7747b55963","modified":1554618464000},{"_id":"themes/yilia/source-src/css/aside.scss","hash":"578a67464dd0f542197f7fcee158c991db058563","modified":1554618464000},{"_id":"themes/yilia/source-src/css/global.scss","hash":"b4cb4f45a55d4250cd9056f76dab2a3c0dabcec4","modified":1554618464000},{"_id":"themes/yilia/source-src/css/footer.scss","hash":"7c995410b25baaf61dfc5e148e22ca60330abcd3","modified":1554618464000},{"_id":"themes/yilia/source-src/css/fonts.scss","hash":"97b8fba41c914145710b90091f400b845879577f","modified":1554618464000},{"_id":"themes/yilia/source-src/css/left.scss","hash":"0d30c0e7cdb831c3881a017006c782f2214ac195","modified":1554618464000},{"_id":"themes/yilia/source-src/css/grid.scss","hash":"849a29fcd7150214fcf7b9715fa5dc71d1f9b896","modified":1554618464000},{"_id":"themes/yilia/source-src/css/highlight.scss","hash":"3719994c2c9393813cc1d42b657205c368a329cb","modified":1554618464000},{"_id":"themes/yilia/source-src/css/main.scss","hash":"2f86a014af93583caba78a563d9549826bf28294","modified":1554618464000},{"_id":"themes/yilia/source-src/css/mobile-slider.scss","hash":"f053c609d84df0dd9eee1d11ddf0c19163a456be","modified":1554618464000},{"_id":"themes/yilia/source-src/css/mobile.scss","hash":"ace041d72f95b419f6a5e443191703c2b62007f4","modified":1554618464000},{"_id":"themes/yilia/source-src/css/page.scss","hash":"bf206bb7f7d0967bc8b7fdf01b7ffc99aff9ba88","modified":1554618464000},{"_id":"themes/yilia/source-src/css/reward.scss","hash":"80a4fcf9171d4a33235da96ac8a2b7dcabc45dfb","modified":1554618464000},{"_id":"themes/yilia/source-src/css/scroll.scss","hash":"9c8dfd1c76854ef063494ca76fac6360b391ed6d","modified":1554618464000},{"_id":"themes/yilia/source-src/css/social.scss","hash":"724162ccf3977e70a45d189abfaa20b6e2fba87b","modified":1554618464000},{"_id":"themes/yilia/source-src/css/tags.scss","hash":"ac67a3c7097849206244db9b0ba91daaba017ef5","modified":1554618464000},{"_id":"themes/yilia/source-src/css/share.scss","hash":"150c6425f6582e7ec78a873256ce49c9930e8805","modified":1554618464000},{"_id":"themes/yilia/source-src/css/tags-cloud.scss","hash":"c8aa84fca93862d3caae77c552873b8610f33327","modified":1554618464000},{"_id":"themes/yilia/source-src/css/tools.scss","hash":"1b1aa0908e58cf942b28e3881d07c5573c4129e1","modified":1554618464000},{"_id":"themes/yilia/source-src/js/Q.js","hash":"d011af172064b6c6e0c7051d8f9879373ddac113","modified":1554618464000},{"_id":"themes/yilia/source-src/js/anm.js","hash":"4a4c5d82b09a3063f91a434388e6aa064fd7fd98","modified":1554618464000},{"_id":"themes/yilia/source-src/js/aside.js","hash":"754f771264548a6c5a8ad842908e59ae4e7ed099","modified":1554618464000},{"_id":"themes/yilia/source-src/js/browser.js","hash":"04095b38cfd4316a23f8eb14b1ffaf95f78a4260","modified":1554618464000},{"_id":"themes/yilia/source-src/js/main.js","hash":"3894e60827c817319e43c9ff3ed045fc3d7336ce","modified":1554618464000},{"_id":"themes/yilia/source-src/js/fix.js","hash":"d6782d53c992e712af39c84e804eccaf38830b94","modified":1554618464000},{"_id":"themes/yilia/source-src/js/mobile.js","hash":"4d823b039fd296d24a454eae5a798b93c44560cb","modified":1554618464000},{"_id":"themes/yilia/source-src/js/report.js","hash":"4f1d9a18a936ce40b037f636a39127dd19175b6e","modified":1554618464000},{"_id":"themes/yilia/source-src/js/share.js","hash":"b090f82cf80cba7da764753906d9e2cc2acdf30d","modified":1554618464000},{"_id":"themes/yilia/source-src/js/slider.js","hash":"e846bcc5aac9c68b93f7b8de353df54d8d29f666","modified":1554618464000},{"_id":"themes/yilia/source-src/js/util.js","hash":"8456e9d6b19532742582c99b2fb9d09e146e1c58","modified":1554618464000},{"_id":"themes/yilia/source-src/js/viewer.js","hash":"2577deb6a9fe4f5436360b2ce9afcc7f9a7f0116","modified":1554618464000},{"_id":"themes/yilia/source-src/css/tooltip.scss","hash":"53d5a554bc2f38e9bb3d26400a47767013c05216","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/post/date.ejs","hash":"ef71c4081e866a494367575c59610e7e6339ece0","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/post/changyan.ejs","hash":"5f99b55980da64a723a8e14d5a7daba0d6504647","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/post/category.ejs","hash":"0809a4829aabeb4e911a3ed04ec28db4df7dfe3f","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/post/nav.ejs","hash":"1036c8e4e1a7bc935ba173744da735a0d6ed09cd","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/post/duoshuo.ejs","hash":"e8399025ed3b980aedb821c92855889f5f12fd5b","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/post/gitment.ejs","hash":"e68bbac9ffb1ad27b56837c9abad6ed6bb7daa0c","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/post/share.ejs","hash":"5dccfbe165b23a101f1333cc65ed8efbd197453c","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/post/wangyiyun.ejs","hash":"ea41c462168d9697caef9485862e9cac718a12c1","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/post/tag.ejs","hash":"2e783e68755abb852760eb0e627a3fbb50a05a55","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/post/title.ejs","hash":"2f275739b6f1193c123646a5a31f37d48644c667","modified":1554618464000},{"_id":"themes/yilia/source-src/css/core/_animation.scss","hash":"63a37f26276f9207405afe0f2d65339ce295bbaf","modified":1554618464000},{"_id":"themes/yilia/source-src/css/core/_media-queries.scss","hash":"491ab3378d5c11005ba65c607608bb36b368a9d5","modified":1554618464000},{"_id":"themes/yilia/source-src/css/core/_reset.scss","hash":"fab871fa93bd542e76a71a56428f2994a4aaf443","modified":1554618464000},{"_id":"themes/yilia/source-src/css/core/_mixin.scss","hash":"3bba5c77bad5981eac859fe05c9561d580ba7fa9","modified":1554618464000},{"_id":"themes/yilia/source-src/css/fonts/iconfont.eot","hash":"bc8c5e88f4994a852041b4d83f126d9c4d419b4a","modified":1554618464000},{"_id":"themes/yilia/source-src/css/fonts/iconfont.svg","hash":"f9304e5714d20861be7d8f4d36687e88e86b9e1b","modified":1554618464000},{"_id":"themes/yilia/source-src/css/core/_variables.scss","hash":"fb511c505d1309249f21dc577d4ad2bad99a764f","modified":1554618464000},{"_id":"themes/yilia/source-src/css/img/checkered-pattern.png","hash":"049262fa0886989d750637b264bed34ab51c23c8","modified":1554618464000},{"_id":"themes/yilia/source-src/css/img/scrollbar_arrow.png","hash":"d64a33c4ddfbdb89deeb6f4e3d36eb84dc4777c0","modified":1554618464000},{"_id":"themes/yilia/layout/_partial/script.ejs","hash":"4cb685f07e89dd5175c2a576e73a1a957aec5637","modified":1554618464000},{"_id":"themes/yilia/source-src/css/img/tooltip.svg","hash":"397fe4b1093bf9b62457dac48aa15dac06b54a3c","modified":1554618464000},{"_id":"themes/yilia/source-src/css/fonts/iconfont.ttf","hash":"f342ac8bf4d937f42a7d6a0032ad267ab47eb7f2","modified":1554618464000},{"_id":"themes/yilia/source-src/css/fonts/iconfont.woff","hash":"aa9672cb097f7fd73ae5a03bcd3d9d726935bc0a","modified":1554618464000},{"_id":"source/assets/blog/totoro.jpeg","hash":"30bc504edb2e9bd4ef8a4e2321f0d0f4c8283a91","modified":1554730898048},{"_id":"public/atom.xml","hash":"7c2631cc424e86fe0887c18ad1409878125fe96d","modified":1560209953621},{"_id":"public/content.json","hash":"e35d866cfc112d687294ee51d5434f628f1e862e","modified":1560209748914},{"_id":"public/2019/06/10/web-secure/index.html","hash":"e8f3a2c80e6ea2075b7c1fe4916f7318e2e09382","modified":1560209954246},{"_id":"public/2019/06/06/networkSecurity-web-ClickJacking/index.html","hash":"bea08c07edee44d31c7ead18d0175fad5f312d5f","modified":1560209749753},{"_id":"public/2019/05/29/ssh-generate-SSH-key-and-deploy-it/index.html","hash":"b9ac37ec939945ebad2c35d27ac63c050948a279","modified":1560209749753},{"_id":"public/2019/06/09/python-health-check-util/index.html","hash":"956740a7959633e52a94e1972863752c729de10c","modified":1560209749753},{"_id":"public/2019/05/16/git-version-control-basics/index.html","hash":"fd0d0ff60466d0544bee25252a5feccc451ee4f3","modified":1560209749753},{"_id":"public/2019/04/17/python-lib-unittest/index.html","hash":"e1d8b0efaaa9c8cc9d2cc53575559b7467e6fa5a","modified":1560209749758},{"_id":"public/2019/04/23/python-tutorial-record/index.html","hash":"5ef02e68296495d4a1a1fcadc52974a6e2ce0b57","modified":1560209749758},{"_id":"public/2015/10/21/demo/index.html","hash":"fbebf0ce74eb7e760c7fbd77f0e505ca59970f9a","modified":1560209749758},{"_id":"public/2019/04/19/node-server-deploy/index.html","hash":"094cf325744ff279894b123c332c94d1df4c6db7","modified":1560209749758},{"_id":"public/archives/index.html","hash":"6db5cdbcccdc108a75e3a1c94a5ea1248cf8c13e","modified":1560209749758},{"_id":"public/archives/2015/index.html","hash":"cef80398363726025510bfa80d57bca372cba4f7","modified":1560209749758},{"_id":"public/archives/2015/10/index.html","hash":"b4d60a712a0190b54f0d126d8d6457a49c7d4486","modified":1560209749758},{"_id":"public/2019/04/09/http-learn-notes/index.html","hash":"47fed45330e76db568482bd046ad3797ba502fe3","modified":1560209749759},{"_id":"public/archives/2019/index.html","hash":"cec8b68b78d8fc20f8534385cee011f8f11e5eca","modified":1560209749759},{"_id":"public/archives/2019/04/index.html","hash":"c16c3ed377a788caafb3363ebcdecab5d7f0c534","modified":1560209749759},{"_id":"public/index.html","hash":"fd7f4154a53662a9047f2fdcbdf41cb4bbc9c410","modified":1560209749759},{"_id":"public/2019/04/17/python-module-requests/index.html","hash":"c371d00e334f5ee8ce9ad33d3939baecab6c821f","modified":1560209749759},{"_id":"public/page/2/index.html","hash":"23d32f39890a4af4aba526e5ef904d5f4ef94ff6","modified":1560209749759},{"_id":"public/archives/2019/06/index.html","hash":"1fec02f1135ff89018b3177a98247859f1258292","modified":1560209749759},{"_id":"public/tags/日记/index.html","hash":"01b72bd7e9d1d2060b9eb914065d889eb658e758","modified":1560209749760},{"_id":"public/archives/2019/05/index.html","hash":"c81c966679dd3506f7d1e9b2b0fa2e2e365b01a0","modified":1560209749760},{"_id":"public/tags/日记/page/2/index.html","hash":"1afa40ea913e8caba5d660d2c565d1362e642d15","modified":1560209749760},{"_id":"public/tags/git/index.html","hash":"b0f8af0854547108b85fb723a3392b1e10eee64e","modified":1560209749760},{"_id":"public/tags/Python/index.html","hash":"b6343776f7a3dc735405beccb3799a104f64fa62","modified":1560209749760},{"_id":"public/tags/版本控制/index.html","hash":"3e8d4be314195ef1f9036f327c276638da5292b8","modified":1560209749760},{"_id":"public/tags/jenkins/index.html","hash":"1dc22a8967ea7957a639773de38221e8859a94b9","modified":1560209749760},{"_id":"public/tags/Node/index.html","hash":"df0215ff483e0e66802c55e7d29fcd27f63bd54b","modified":1560209749760},{"_id":"public/tags/itchat/index.html","hash":"b4e99fc024b771e96e5d0c94ec6c5e5a854faa1f","modified":1560209749761},{"_id":"public/tags/Web安全/index.html","hash":"87bf2493893189ad54ea63eded92549f5d8b51e5","modified":1560209749761},{"_id":"public/tags/MongoDB/index.html","hash":"9a706fa2652905393d1bbeb9b5cc972a6a55dce0","modified":1560209749761},{"_id":"public/tags/unittest/index.html","hash":"4d6254bfa2c2de29dbe4544cd578761628c0eb90","modified":1560209749761},{"_id":"public/tags/服务部署/index.html","hash":"81ed3308716d409cb4173ca74886e5e6807f2dd1","modified":1560209749761},{"_id":"public/tags/基础知识/index.html","hash":"04fd5370c988770c2e12ec910ddcb18e20cecf5a","modified":1560209749761},{"_id":"public/tags/requests/index.html","hash":"492b9677eef479bc5e5ec20f151a3996c9231e2f","modified":1560209749761},{"_id":"public/tags/SSH/index.html","hash":"ab6fdc7472bafca2cf8b2c52634190c9d62bc595","modified":1560209749762},{"_id":"public/tags/学习Vlog/index.html","hash":"20e5c811c112f3e766b6e34c4d723265e0bf5b17","modified":1560209749762},{"_id":"public/tags/css/index.html","hash":"0fba1df3c29bef4bda3627cb219e98d808a2073d","modified":1560209749762},{"_id":"public/tags/HTTP/index.html","hash":"4b70677d1baf5d194b02bcaa7bfdd709e5f6ab61","modified":1560209749762},{"_id":"public/assets/image/favicon.ico","hash":"c7dcd652fa9ed927d0ec2e0580f5de1ae9ddbdff","modified":1560209749766},{"_id":"public/img/default-skin.png","hash":"ed95a8e40a2c3478c5915376acb8e5f33677f24d","modified":1560209749766},{"_id":"public/img/preloader.gif","hash":"6342367c93c82da1b9c620e97c84a389cc43d96d","modified":1560209749766},{"_id":"public/fonts/iconfont.16acc2.ttf","hash":"f342ac8bf4d937f42a7d6a0032ad267ab47eb7f2","modified":1560209749766},{"_id":"public/fonts/default-skin.b257fa.svg","hash":"2ac727c9e092331d35cce95af209ccfac6d4c7c7","modified":1560209749766},{"_id":"public/fonts/iconfont.45d7ee.svg","hash":"f9304e5714d20861be7d8f4d36687e88e86b9e1b","modified":1560209749766},{"_id":"public/fonts/iconfont.b322fa.eot","hash":"bc8c5e88f4994a852041b4d83f126d9c4d419b4a","modified":1560209749766},{"_id":"public/fonts/iconfont.8c627f.woff","hash":"aa9672cb097f7fd73ae5a03bcd3d9d726935bc0a","modified":1560209749766},{"_id":"public/fonts/tooltip.4004ff.svg","hash":"397fe4b1093bf9b62457dac48aa15dac06b54a3c","modified":1560209749766},{"_id":"public/img/scrollbar_arrow.png","hash":"d64a33c4ddfbdb89deeb6f4e3d36eb84dc4777c0","modified":1560209749766},{"_id":"public/assets/image/avator.jpeg","hash":"614f22cdbeb97afe962d213915c1cf65e3a2c237","modified":1560209749768},{"_id":"public/assets/blog/totoro.jpeg","hash":"30bc504edb2e9bd4ef8a4e2321f0d0f4c8283a91","modified":1560209749780},{"_id":"public/slider.e37972.js","hash":"6dec4e220c89049037eebc44404abd8455d22ad7","modified":1560209749788},{"_id":"public/main.0cf68a.css","hash":"ddf6e2c6b953c2c59a3c271e6070010a4cc81cf9","modified":1560209749788},{"_id":"public/main.0cf68a.js","hash":"5299bd43c93dabe6eacb96ae9f83271c49c5909d","modified":1560209749789},{"_id":"public/mobile.992cbe.js","hash":"01b35e71e37aa2849664eb5daf26daede2278398","modified":1560209749791}],"Category":[],"Data":[],"Page":[],"Post":[{"title":"Git版本控制基础应用","date":"2019-05-16T06:26:15.000Z","top":false,"reward":false,"comments":0,"_content":"\n过去的一系列产品开发过程中,版本控制大多集中于svn的应用,对于git相对较为生疏。现阶段在开发一款企业级多系统应用过程中,对产品的版本分割、迭代有较强的前置需求,需要我自行维护构建一整套多版本的开发源码,因此借助公司内部已有gitlab平台做出基本尝试。\n此文主要是用以记录采用git这一整套分布式版本控制系统过程中所归纳总结的一系列命令行、Repository、Branches等管理应用心得及踩坑之处。\n\n<!-- more -->\n#### 一、Git简介\n分布式版本控制系统和集中式版本控制系统对于数据存储、安全性、文件流管理等各方面各有优异,以下仅对分布式版本控制系统做基本的优劣性总结。\n\n> * git大部分系统以文件变更列表的方式存储信息,把存储数据看做对小型文件系统的一组快照流。\n> * git绝大多数的操作只需要访问本地文件和资源,本次磁盘即保存着项目的完整历史,因此执行速度非常快。\n> * git文件状态主要有:已提交(committed)、已修改(modified)、已暂存(staged)三种。对应的工作区域也有Git仓库、工作目录以及暂存区域三种。\n> * git的工作流程:在工作目录中修改文件 -> 暂存文件,将文件的快照放入暂存区域 -> 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录\n> * git存在较为明显的缺点,代码的保密性差,仓库保存着所有的代码和版本信息。\n\n\n#### 二、Git安装及配置\n基于Mac OS X的Git安装\n###### 1.使用homebrew安装git ([homebrew](https://brew.sh/))\nHomebrew安装\n````\nruby -e \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)\"\n````\n使用homebrew安装git\n````\nbrew install git\n````\n###### 2.基于OSX Git安装程序安装([https://git-scm.com/download/mac](https://git-scm.com/download/mac))\n###### 3.基于图形化工具GitHub for Mac工具安装([https://desktop.github.com/](https://desktop.github.com/))\n\n#### 三、Git基本配置\n检查配置信息\n````\ngit config --list\n````\n用户信息配置\n````\ngit config --global user.name \"bayuefen\"\ngit config --global user.email wobushifunv555@163.com\n````\n\n#### 四、命令使用\n\n###### 常用命令\n````\n初始化操作:git init\n克隆仓库:git clone [url] <name>\n查看文件状态:git status\n对比文件暂存:git diff\n移除文件:git rm [file]\n查看历史提交记录:git log\n取消暂存文件:git reset HEAD <file>\n查看远程仓库:git remote -v\n冲突合并:git mergetool (可以使用opendiff等三方合并工具)\n查看分支:git branch\n创建分支:git branch <name>\n切换分支:git checkout <name>\n创建并切换到该分支:git checkout -b <name>\n合并某分支到当前分支:git merge <name>\n删除当前分支:git branch -d <name>\n提交改动记录: git commit -m \"changed records\"\n查看标签:git tag\n添加标签:git tag -a [version] -m [version_name]\n信息查看:git show\n````\n###### 拉取已有分支、开发并提交的基本流程\n````\n克隆现有repository:git clone domain/Project.git\n切换至开发branch:git checkout <branch_name>\nblingbling的苦逼工作:coding work...\n分支整合:git pull\n文件版本控制新增:git add ./\n提交改动至分支:git commit -m <commit_record>\n推送改动至分支:git push\n````\n###### 合并分支到master\n````\n切换至master分支:git checkout master\n远程分支整合:git pull origin master\n合并分支至master:git merge <branch>\n查看状态:git status\n推送分支内容至master:git push origin master\n````\n\n#### 五、References\n1.[Git教程](https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000)\n2.[Pro Git](https://git-scm.com/book/zh/v2)\n\n\n\n\n\n\n\n\n\n","source":"_posts/git-version-control-basics.md","raw":"title: Git版本控制基础应用\ndate: 2019-05-16 14:26:15\ntop: false\nreward: false\ncomments: false\ntags:\n - git\n - 日记\n - 版本控制\n---\n\n过去的一系列产品开发过程中,版本控制大多集中于svn的应用,对于git相对较为生疏。现阶段在开发一款企业级多系统应用过程中,对产品的版本分割、迭代有较强的前置需求,需要我自行维护构建一整套多版本的开发源码,因此借助公司内部已有gitlab平台做出基本尝试。\n此文主要是用以记录采用git这一整套分布式版本控制系统过程中所归纳总结的一系列命令行、Repository、Branches等管理应用心得及踩坑之处。\n\n<!-- more -->\n#### 一、Git简介\n分布式版本控制系统和集中式版本控制系统对于数据存储、安全性、文件流管理等各方面各有优异,以下仅对分布式版本控制系统做基本的优劣性总结。\n\n> * git大部分系统以文件变更列表的方式存储信息,把存储数据看做对小型文件系统的一组快照流。\n> * git绝大多数的操作只需要访问本地文件和资源,本次磁盘即保存着项目的完整历史,因此执行速度非常快。\n> * git文件状态主要有:已提交(committed)、已修改(modified)、已暂存(staged)三种。对应的工作区域也有Git仓库、工作目录以及暂存区域三种。\n> * git的工作流程:在工作目录中修改文件 -> 暂存文件,将文件的快照放入暂存区域 -> 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录\n> * git存在较为明显的缺点,代码的保密性差,仓库保存着所有的代码和版本信息。\n\n\n#### 二、Git安装及配置\n基于Mac OS X的Git安装\n###### 1.使用homebrew安装git ([homebrew](https://brew.sh/))\nHomebrew安装\n````\nruby -e \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)\"\n````\n使用homebrew安装git\n````\nbrew install git\n````\n###### 2.基于OSX Git安装程序安装([https://git-scm.com/download/mac](https://git-scm.com/download/mac))\n###### 3.基于图形化工具GitHub for Mac工具安装([https://desktop.github.com/](https://desktop.github.com/))\n\n#### 三、Git基本配置\n检查配置信息\n````\ngit config --list\n````\n用户信息配置\n````\ngit config --global user.name \"bayuefen\"\ngit config --global user.email wobushifunv555@163.com\n````\n\n#### 四、命令使用\n\n###### 常用命令\n````\n初始化操作:git init\n克隆仓库:git clone [url] <name>\n查看文件状态:git status\n对比文件暂存:git diff\n移除文件:git rm [file]\n查看历史提交记录:git log\n取消暂存文件:git reset HEAD <file>\n查看远程仓库:git remote -v\n冲突合并:git mergetool (可以使用opendiff等三方合并工具)\n查看分支:git branch\n创建分支:git branch <name>\n切换分支:git checkout <name>\n创建并切换到该分支:git checkout -b <name>\n合并某分支到当前分支:git merge <name>\n删除当前分支:git branch -d <name>\n提交改动记录: git commit -m \"changed records\"\n查看标签:git tag\n添加标签:git tag -a [version] -m [version_name]\n信息查看:git show\n````\n###### 拉取已有分支、开发并提交的基本流程\n````\n克隆现有repository:git clone domain/Project.git\n切换至开发branch:git checkout <branch_name>\nblingbling的苦逼工作:coding work...\n分支整合:git pull\n文件版本控制新增:git add ./\n提交改动至分支:git commit -m <commit_record>\n推送改动至分支:git push\n````\n###### 合并分支到master\n````\n切换至master分支:git checkout master\n远程分支整合:git pull origin master\n合并分支至master:git merge <branch>\n查看状态:git status\n推送分支内容至master:git push origin master\n````\n\n#### 五、References\n1.[Git教程](https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000)\n2.[Pro Git](https://git-scm.com/book/zh/v2)\n\n\n\n\n\n\n\n\n\n","slug":"git-version-control-basics","published":1,"updated":"2019-06-09T17:21:12.948Z","layout":"post","photos":[],"link":"","_id":"cjwr0llvf0000bkvl7r7ggvwr","content":"<p>过去的一系列产品开发过程中,版本控制大多集中于svn的应用,对于git相对较为生疏。现阶段在开发一款企业级多系统应用过程中,对产品的版本分割、迭代有较强的前置需求,需要我自行维护构建一整套多版本的开发源码,因此借助公司内部已有gitlab平台做出基本尝试。<br>此文主要是用以记录采用git这一整套分布式版本控制系统过程中所归纳总结的一系列命令行、Repository、Branches等管理应用心得及踩坑之处。</p>\n<a id=\"more\"></a>\n<h4 id=\"一、Git简介\"><a href=\"#一、Git简介\" class=\"headerlink\" title=\"一、Git简介\"></a>一、Git简介</h4><p>分布式版本控制系统和集中式版本控制系统对于数据存储、安全性、文件流管理等各方面各有优异,以下仅对分布式版本控制系统做基本的优劣性总结。</p>\n<blockquote>\n<ul>\n<li>git大部分系统以文件变更列表的方式存储信息,把存储数据看做对小型文件系统的一组快照流。</li>\n<li>git绝大多数的操作只需要访问本地文件和资源,本次磁盘即保存着项目的完整历史,因此执行速度非常快。</li>\n<li>git文件状态主要有:已提交(committed)、已修改(modified)、已暂存(staged)三种。对应的工作区域也有Git仓库、工作目录以及暂存区域三种。</li>\n<li>git的工作流程:在工作目录中修改文件 -> 暂存文件,将文件的快照放入暂存区域 -> 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录</li>\n<li>git存在较为明显的缺点,代码的保密性差,仓库保存着所有的代码和版本信息。</li>\n</ul>\n</blockquote>\n<h4 id=\"二、Git安装及配置\"><a href=\"#二、Git安装及配置\" class=\"headerlink\" title=\"二、Git安装及配置\"></a>二、Git安装及配置</h4><p>基于Mac OS X的Git安装</p>\n<h6 id=\"1-使用homebrew安装git-homebrew\"><a href=\"#1-使用homebrew安装git-homebrew\" class=\"headerlink\" title=\"1.使用homebrew安装git (homebrew)\"></a>1.使用homebrew安装git (<a href=\"https://brew.sh/\" target=\"_blank\" rel=\"noopener\">homebrew</a>)</h6><p>Homebrew安装<br><figure class=\"highlight sql\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">ruby -e \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/<span class=\"keyword\">install</span>/<span class=\"keyword\">master</span>/<span class=\"keyword\">install</span>)<span class=\"string\">\"</span></span><br></pre></td></tr></table></figure></p>\n<p>使用homebrew安装git<br><figure class=\"highlight mipsasm\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">brew </span><span class=\"keyword\">install </span>git</span><br></pre></td></tr></table></figure></p>\n<h6 id=\"2-基于OSX-Git安装程序安装(https-git-scm-com-download-mac)\"><a href=\"#2-基于OSX-Git安装程序安装(https-git-scm-com-download-mac)\" class=\"headerlink\" title=\"2.基于OSX Git安装程序安装(https://git-scm.com/download/mac)\"></a>2.基于OSX Git安装程序安装(<a href=\"https://git-scm.com/download/mac\" target=\"_blank\" rel=\"noopener\">https://git-scm.com/download/mac</a>)</h6><h6 id=\"3-基于图形化工具GitHub-for-Mac工具安装(https-desktop-github-com-)\"><a href=\"#3-基于图形化工具GitHub-for-Mac工具安装(https-desktop-github-com-)\" class=\"headerlink\" title=\"3.基于图形化工具GitHub for Mac工具安装(https://desktop.github.com/)\"></a>3.基于图形化工具GitHub for Mac工具安装(<a href=\"https://desktop.github.com/\" target=\"_blank\" rel=\"noopener\">https://desktop.github.com/</a>)</h6><h4 id=\"三、Git基本配置\"><a href=\"#三、Git基本配置\" class=\"headerlink\" title=\"三、Git基本配置\"></a>三、Git基本配置</h4><p>检查配置信息<br><figure class=\"highlight routeros\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">git<span class=\"built_in\"> config </span>--list</span><br></pre></td></tr></table></figure></p>\n<p>用户信息配置<br><figure class=\"highlight routeros\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">git<span class=\"built_in\"> config </span>--global user.name <span class=\"string\">\"bayuefen\"</span></span><br><span class=\"line\">git<span class=\"built_in\"> config </span>--global user.email wobushifunv555@163.com</span><br></pre></td></tr></table></figure></p>\n<h4 id=\"四、命令使用\"><a href=\"#四、命令使用\" class=\"headerlink\" title=\"四、命令使用\"></a>四、命令使用</h4><h6 id=\"常用命令\"><a href=\"#常用命令\" class=\"headerlink\" title=\"常用命令\"></a>常用命令</h6><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">初始化操作:git init</span><br><span class=\"line\">克隆仓库:git clone [url] <span class=\"symbol\"><name></span></span><br><span class=\"line\">查看文件状态:git status</span><br><span class=\"line\">对比文件暂存:git diff</span><br><span class=\"line\">移除文件:git rm [<span class=\"keyword\">file</span>]</span><br><span class=\"line\">查看历史提交记录:git <span class=\"built_in\">log</span></span><br><span class=\"line\">取消暂存文件:git reset HEAD <span class=\"symbol\"><file></span></span><br><span class=\"line\">查看远程仓库:git remote -v</span><br><span class=\"line\">冲突合并:git mergetool (可以使用opendiff等三方合并工具)</span><br><span class=\"line\">查看分支:git branch</span><br><span class=\"line\">创建分支:git branch <span class=\"symbol\"><name></span></span><br><span class=\"line\">切换分支:git checkout <span class=\"symbol\"><name></span></span><br><span class=\"line\">创建并切换到该分支:git checkout -<span class=\"keyword\">b</span> <span class=\"symbol\"><name></span></span><br><span class=\"line\">合并某分支到当前分支:git merge <span class=\"symbol\"><name></span></span><br><span class=\"line\">删除当前分支:git branch -d <span class=\"symbol\"><name></span></span><br><span class=\"line\">提交改动记录: git commit -<span class=\"keyword\">m</span> <span class=\"string\">\"changed records\"</span></span><br><span class=\"line\">查看标签:git <span class=\"keyword\">tag</span></span><br><span class=\"line\">添加标签:git <span class=\"keyword\">tag</span> -<span class=\"keyword\">a</span> [<span class=\"keyword\">version</span>] -<span class=\"keyword\">m</span> [version_name]</span><br><span class=\"line\">信息查看:git show</span><br></pre></td></tr></table></figure>\n<h6 id=\"拉取已有分支、开发并提交的基本流程\"><a href=\"#拉取已有分支、开发并提交的基本流程\" class=\"headerlink\" title=\"拉取已有分支、开发并提交的基本流程\"></a>拉取已有分支、开发并提交的基本流程</h6><figure class=\"highlight armasm\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">克隆现有repository:git clone domain/Project.git</span><br><span class=\"line\">切换至开发<span class=\"keyword\">branch:git </span>checkout <<span class=\"keyword\">branch_name></span></span><br><span class=\"line\"><span class=\"keyword\">blingbling的苦逼工作:coding </span>work...</span><br><span class=\"line\">分支整合:git pull</span><br><span class=\"line\">文件版本控制新增:git <span class=\"keyword\">add </span>./</span><br><span class=\"line\">提交改动至分支:git commit -m <commit_record></span><br><span class=\"line\">推送改动至分支:git <span class=\"keyword\">push</span></span><br></pre></td></tr></table></figure>\n<h6 id=\"合并分支到master\"><a href=\"#合并分支到master\" class=\"headerlink\" title=\"合并分支到master\"></a>合并分支到master</h6><figure class=\"highlight crmsh\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">切换至<span class=\"literal\">master</span>分支:git checkout <span class=\"literal\">master</span></span><br><span class=\"line\">远程分支整合:git pull origin <span class=\"literal\">master</span></span><br><span class=\"line\">合并分支至<span class=\"literal\">master</span>:git merge <span class=\"tag\"><branch></span></span><br><span class=\"line\">查看状态:git status</span><br><span class=\"line\">推送分支内容至<span class=\"literal\">master</span>:git push origin <span class=\"literal\">master</span></span><br></pre></td></tr></table></figure>\n<h4 id=\"五、References\"><a href=\"#五、References\" class=\"headerlink\" title=\"五、References\"></a>五、References</h4><p>1.<a href=\"https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000\" target=\"_blank\" rel=\"noopener\">Git教程</a><br>2.<a href=\"https://git-scm.com/book/zh/v2\" target=\"_blank\" rel=\"noopener\">Pro Git</a></p>\n","site":{"data":{}},"excerpt":"<p>过去的一系列产品开发过程中,版本控制大多集中于svn的应用,对于git相对较为生疏。现阶段在开发一款企业级多系统应用过程中,对产品的版本分割、迭代有较强的前置需求,需要我自行维护构建一整套多版本的开发源码,因此借助公司内部已有gitlab平台做出基本尝试。<br>此文主要是用以记录采用git这一整套分布式版本控制系统过程中所归纳总结的一系列命令行、Repository、Branches等管理应用心得及踩坑之处。</p>","more":"<h4 id=\"一、Git简介\"><a href=\"#一、Git简介\" class=\"headerlink\" title=\"一、Git简介\"></a>一、Git简介</h4><p>分布式版本控制系统和集中式版本控制系统对于数据存储、安全性、文件流管理等各方面各有优异,以下仅对分布式版本控制系统做基本的优劣性总结。</p>\n<blockquote>\n<ul>\n<li>git大部分系统以文件变更列表的方式存储信息,把存储数据看做对小型文件系统的一组快照流。</li>\n<li>git绝大多数的操作只需要访问本地文件和资源,本次磁盘即保存着项目的完整历史,因此执行速度非常快。</li>\n<li>git文件状态主要有:已提交(committed)、已修改(modified)、已暂存(staged)三种。对应的工作区域也有Git仓库、工作目录以及暂存区域三种。</li>\n<li>git的工作流程:在工作目录中修改文件 -> 暂存文件,将文件的快照放入暂存区域 -> 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录</li>\n<li>git存在较为明显的缺点,代码的保密性差,仓库保存着所有的代码和版本信息。</li>\n</ul>\n</blockquote>\n<h4 id=\"二、Git安装及配置\"><a href=\"#二、Git安装及配置\" class=\"headerlink\" title=\"二、Git安装及配置\"></a>二、Git安装及配置</h4><p>基于Mac OS X的Git安装</p>\n<h6 id=\"1-使用homebrew安装git-homebrew\"><a href=\"#1-使用homebrew安装git-homebrew\" class=\"headerlink\" title=\"1.使用homebrew安装git (homebrew)\"></a>1.使用homebrew安装git (<a href=\"https://brew.sh/\" target=\"_blank\" rel=\"noopener\">homebrew</a>)</h6><p>Homebrew安装<br><figure class=\"highlight sql\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">ruby -e \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/<span class=\"keyword\">install</span>/<span class=\"keyword\">master</span>/<span class=\"keyword\">install</span>)<span class=\"string\">\"</span></span><br></pre></td></tr></table></figure></p>\n<p>使用homebrew安装git<br><figure class=\"highlight mipsasm\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">brew </span><span class=\"keyword\">install </span>git</span><br></pre></td></tr></table></figure></p>\n<h6 id=\"2-基于OSX-Git安装程序安装(https-git-scm-com-download-mac)\"><a href=\"#2-基于OSX-Git安装程序安装(https-git-scm-com-download-mac)\" class=\"headerlink\" title=\"2.基于OSX Git安装程序安装(https://git-scm.com/download/mac)\"></a>2.基于OSX Git安装程序安装(<a href=\"https://git-scm.com/download/mac\" target=\"_blank\" rel=\"noopener\">https://git-scm.com/download/mac</a>)</h6><h6 id=\"3-基于图形化工具GitHub-for-Mac工具安装(https-desktop-github-com-)\"><a href=\"#3-基于图形化工具GitHub-for-Mac工具安装(https-desktop-github-com-)\" class=\"headerlink\" title=\"3.基于图形化工具GitHub for Mac工具安装(https://desktop.github.com/)\"></a>3.基于图形化工具GitHub for Mac工具安装(<a href=\"https://desktop.github.com/\" target=\"_blank\" rel=\"noopener\">https://desktop.github.com/</a>)</h6><h4 id=\"三、Git基本配置\"><a href=\"#三、Git基本配置\" class=\"headerlink\" title=\"三、Git基本配置\"></a>三、Git基本配置</h4><p>检查配置信息<br><figure class=\"highlight routeros\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">git<span class=\"built_in\"> config </span>--list</span><br></pre></td></tr></table></figure></p>\n<p>用户信息配置<br><figure class=\"highlight routeros\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">git<span class=\"built_in\"> config </span>--global user.name <span class=\"string\">\"bayuefen\"</span></span><br><span class=\"line\">git<span class=\"built_in\"> config </span>--global user.email wobushifunv555@163.com</span><br></pre></td></tr></table></figure></p>\n<h4 id=\"四、命令使用\"><a href=\"#四、命令使用\" class=\"headerlink\" title=\"四、命令使用\"></a>四、命令使用</h4><h6 id=\"常用命令\"><a href=\"#常用命令\" class=\"headerlink\" title=\"常用命令\"></a>常用命令</h6><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">初始化操作:git init</span><br><span class=\"line\">克隆仓库:git clone [url] <span class=\"symbol\"><name></span></span><br><span class=\"line\">查看文件状态:git status</span><br><span class=\"line\">对比文件暂存:git diff</span><br><span class=\"line\">移除文件:git rm [<span class=\"keyword\">file</span>]</span><br><span class=\"line\">查看历史提交记录:git <span class=\"built_in\">log</span></span><br><span class=\"line\">取消暂存文件:git reset HEAD <span class=\"symbol\"><file></span></span><br><span class=\"line\">查看远程仓库:git remote -v</span><br><span class=\"line\">冲突合并:git mergetool (可以使用opendiff等三方合并工具)</span><br><span class=\"line\">查看分支:git branch</span><br><span class=\"line\">创建分支:git branch <span class=\"symbol\"><name></span></span><br><span class=\"line\">切换分支:git checkout <span class=\"symbol\"><name></span></span><br><span class=\"line\">创建并切换到该分支:git checkout -<span class=\"keyword\">b</span> <span class=\"symbol\"><name></span></span><br><span class=\"line\">合并某分支到当前分支:git merge <span class=\"symbol\"><name></span></span><br><span class=\"line\">删除当前分支:git branch -d <span class=\"symbol\"><name></span></span><br><span class=\"line\">提交改动记录: git commit -<span class=\"keyword\">m</span> <span class=\"string\">\"changed records\"</span></span><br><span class=\"line\">查看标签:git <span class=\"keyword\">tag</span></span><br><span class=\"line\">添加标签:git <span class=\"keyword\">tag</span> -<span class=\"keyword\">a</span> [<span class=\"keyword\">version</span>] -<span class=\"keyword\">m</span> [version_name]</span><br><span class=\"line\">信息查看:git show</span><br></pre></td></tr></table></figure>\n<h6 id=\"拉取已有分支、开发并提交的基本流程\"><a href=\"#拉取已有分支、开发并提交的基本流程\" class=\"headerlink\" title=\"拉取已有分支、开发并提交的基本流程\"></a>拉取已有分支、开发并提交的基本流程</h6><figure class=\"highlight armasm\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">克隆现有repository:git clone domain/Project.git</span><br><span class=\"line\">切换至开发<span class=\"keyword\">branch:git </span>checkout <<span class=\"keyword\">branch_name></span></span><br><span class=\"line\"><span class=\"keyword\">blingbling的苦逼工作:coding </span>work...</span><br><span class=\"line\">分支整合:git pull</span><br><span class=\"line\">文件版本控制新增:git <span class=\"keyword\">add </span>./</span><br><span class=\"line\">提交改动至分支:git commit -m <commit_record></span><br><span class=\"line\">推送改动至分支:git <span class=\"keyword\">push</span></span><br></pre></td></tr></table></figure>\n<h6 id=\"合并分支到master\"><a href=\"#合并分支到master\" class=\"headerlink\" title=\"合并分支到master\"></a>合并分支到master</h6><figure class=\"highlight crmsh\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">切换至<span class=\"literal\">master</span>分支:git checkout <span class=\"literal\">master</span></span><br><span class=\"line\">远程分支整合:git pull origin <span class=\"literal\">master</span></span><br><span class=\"line\">合并分支至<span class=\"literal\">master</span>:git merge <span class=\"tag\"><branch></span></span><br><span class=\"line\">查看状态:git status</span><br><span class=\"line\">推送分支内容至<span class=\"literal\">master</span>:git push origin <span class=\"literal\">master</span></span><br></pre></td></tr></table></figure>\n<h4 id=\"五、References\"><a href=\"#五、References\" class=\"headerlink\" title=\"五、References\"></a>五、References</h4><p>1.<a href=\"https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000\" target=\"_blank\" rel=\"noopener\">Git教程</a><br>2.<a href=\"https://git-scm.com/book/zh/v2\" target=\"_blank\" rel=\"noopener\">Pro Git</a></p>"},{"title":"【Web安全】点击劫持","date":"2019-06-06T03:26:51.000Z","top":false,"reward":false,"comments":0,"_content":"\n今天在测试前端开发项目,在生产环境的流程回归验证中,遇到了Web页面中使用`iframe`内嵌三方告知用户协议无法加载的问题。`Error`提示为\n````\nRefused to display 'https://www.xxx.com/' in a frame because it set 'X-Frame-Options' to 'sameorigin'.\n````\n这是一种经典的防范点击劫持(*Click Jacking*)的方式,通过设置HTTP请求头`X-Frame-Options`来禁止跨域`iframe`的引入。以下通过:\n> * 什么是点击劫持\n> * 点击劫持存在的Web安全隐患\n> * 如何防范点击劫持\n\n三方面进行基本的概述与总结。\n\n<!-- more -->\n\n#### 一、什么是点击劫持\n点击劫持(ClickJacking)是一种通过视觉欺骗实现Web攻击的方式。主要的实现方式有两种:\n> * **iframe引入攻击**:使用透明的iframe,覆盖在一个网页上,诱导性用户进行操作,在不知情的情况下点击透明的iframe页面;\n> * **图片覆盖攻击**:使用图片覆盖网页,诱导用户点击;\n> * **拖拽劫持与数据窃取**:诱导用户从隐藏不可见`iframe`中拖拽窃取数据;\n\n\n##### iframe引入攻击\ntest.html\n````\n<!DOCTYPE HTML>\n<html>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n<head>\n<title>bayuefen-demos-clickJacking</title>\n<style>\n html,body,iframe{\n display: block;\n height: 100%;\n width: 100%;\n margin: 0;\n padding: 0;\n border:none;\n }\n iframe{\n opacity:0;\n filter:alpha(opacity=0);\n position:absolute;\n z-index:2;\n }\n button{\n position:absolute;\n top: 315px;\n left: 462px;\n z-index: 1;\n width: 72px;\n height: 26px;\n }\n</style>\n</head>\n <body>\n <button>Click-Jacking-Button</button>\n <iframe src=\"http://www.google.com\"></iframe>\n </body>\n</html>\n````\n\n当用户试图点击test.html里button时,实际上就会点击到iframe上的区块内容。\n本质上,通过引入`iframe`进行页面覆盖,同时设置`position`为`absolute`,通过`opacity`设置页面透明度,并将`z-index`设置到较大的值用以覆盖。\n从而实现完成一次点击劫持攻击。当然实际的攻击过程中,伪装的模式及方法更深。点击劫持本质上和`CSRF`攻击类似,都是通过诱导式,欺骗用户完成一系列操作行为,从而达到攻击劫持的目的。\n\n##### 图片覆盖攻击\nXSIO (Cross Site Image Overlaying)是一种通过调整图片style,将图片覆盖到指定的位置,从而诱导攻击的方式.\n````\n<a href=\"http://www.google.com\">\n <img src=\"IMAGE_URL\" style=\"position:absolute;top:90px;left:320px;\" />\n</a>\n````\n然后将图片伪装成正常的链接,构造一些文字吸引,欺骗用户点击,实现钓鱼的目的。\n\n##### 拖拽劫持与数据窃取\n拖拽劫持的方式是通过隐藏`iframe`中拖拽出攻击者希望得到的数据,然后放到攻击者能控制的另外一个页面中,从而窃取数据。\n详细实现可参考国内安全研究员xisigr构造的[POC](https://book.2cto.com/201208/1996.html)\n\n#### 二、点击劫持存在的Web安全隐患\n点击劫持存在的主要Web安全隐患,相对于`XSS`和`CSRF`攻击来说,主要是通过诱导式与页面产生交互,通过视觉欺诈的模式实施攻击行为,主要利用在钓鱼、欺诈、广告作弊等方面。\n而如何通过有效的手段去防范点击劫持,保证相对稳定及安全的访问环境,以及在测试过程中进行相关Web安全测试的探知是必要的先决要素。\n\n#### 三、如何防范点击劫持\n那么,如何通过技术手段,进行点击劫持的防范。主要有以下两种方式:\n> * frame busting: 基于JavaScript的iframe禁用嵌套\n> * X-Frame-Options: 基于HTTP头的拒绝iframe嵌套加载设置\n\n##### frame busting\nframe busting的条件判断语句:\n````\nif (top != self)\nif (top.location != self.location)\nif (top.location != location)\nif (parent.frames.length > 0)\nif (window != top)\nif (window.top !== window.self)\nif (window.self != window.top)\nif (parent && parent != window)\nif (parent && parent.frames && parent.frames.length>0)\nif((self.parent&&!(self.parent===self))&&(self.parent.frames.length!=0))\n````\n\nframe busting 的纠正动作代码:\n````\ntop.location = self.location\ntop.location.href = document.location.href\ntop.location.href = self.location.href\ntop.location.replace(self.location)\ntop.location.href = window.location.href\ntop.location.replace(document.location)\ntop.location.href = window.location.href\ntop.location.href = \"URL\"\ndocument.write('')\ntop.location = location\ntop.location.replace(document.location)\ntop.location.replace('URL')\ntop.location.href = document.location\ntop.location.replace(window.location.href)\ntop.location.href = location.href\nself.parent.location = document.location\nparent.location.href = self.document.location\ntop.location.href = self.location\ntop.location = window.location\ntop.location.replace(window.location.pathname)\nwindow.top.location = window.self.location\nsetTimeout(function(){document.body.innerHTML='';},1);\nwindow.self.onload = function(evt){document.body.innerHTML='';}\nvar url = window.location.href; top.location.replace(url)\n````\n\n##### X-Frame-Options\nX-Frame-Options 有三个值:\n> * DENY: 表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许。\n> * SAMEORIGIN: 表示该页面可以在相同域名页面的 frame 中展示。\n> * ALLOW-FROM uri: 表示该页面可以在指定来源的 frame 中展示。\n\n涉及`Apache`、`Nginx`、`IIS`对于相应头的配置,详情查看[MDN](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/X-Frame-Options)\n\n#### 参考文献\n1.[X-Frame-Options 响应头](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/X-Frame-Options)\n2.[Busting Frame Busting](https://www.cnblogs.com/LittleHann/p/3386055.html)\n3.[Web应用安全之点击劫持(CLICKJACKING)与X-FRAME-OPTIONS HEADER](https://www.cnblogs.com/xuanhun/p/3610981.html)\n4.[拖拽劫持与数据窃取](https://book.2cto.com/201208/1996.html)\n5.[白帽子讲Web安全](https://book.douban.com/subject/10546925)\n\n","source":"_posts/networkSecurity-web-ClickJacking.md","raw":"---\ntitle: 【Web安全】点击劫持\ndate: 2019-06-06 11:26:51\ntop: false\nreward: false\ncomments: false\ntags:\n - Web安全\n - 日记\n---\n\n今天在测试前端开发项目,在生产环境的流程回归验证中,遇到了Web页面中使用`iframe`内嵌三方告知用户协议无法加载的问题。`Error`提示为\n````\nRefused to display 'https://www.xxx.com/' in a frame because it set 'X-Frame-Options' to 'sameorigin'.\n````\n这是一种经典的防范点击劫持(*Click Jacking*)的方式,通过设置HTTP请求头`X-Frame-Options`来禁止跨域`iframe`的引入。以下通过:\n> * 什么是点击劫持\n> * 点击劫持存在的Web安全隐患\n> * 如何防范点击劫持\n\n三方面进行基本的概述与总结。\n\n<!-- more -->\n\n#### 一、什么是点击劫持\n点击劫持(ClickJacking)是一种通过视觉欺骗实现Web攻击的方式。主要的实现方式有两种:\n> * **iframe引入攻击**:使用透明的iframe,覆盖在一个网页上,诱导性用户进行操作,在不知情的情况下点击透明的iframe页面;\n> * **图片覆盖攻击**:使用图片覆盖网页,诱导用户点击;\n> * **拖拽劫持与数据窃取**:诱导用户从隐藏不可见`iframe`中拖拽窃取数据;\n\n\n##### iframe引入攻击\ntest.html\n````\n<!DOCTYPE HTML>\n<html>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n<head>\n<title>bayuefen-demos-clickJacking</title>\n<style>\n html,body,iframe{\n display: block;\n height: 100%;\n width: 100%;\n margin: 0;\n padding: 0;\n border:none;\n }\n iframe{\n opacity:0;\n filter:alpha(opacity=0);\n position:absolute;\n z-index:2;\n }\n button{\n position:absolute;\n top: 315px;\n left: 462px;\n z-index: 1;\n width: 72px;\n height: 26px;\n }\n</style>\n</head>\n <body>\n <button>Click-Jacking-Button</button>\n <iframe src=\"http://www.google.com\"></iframe>\n </body>\n</html>\n````\n\n当用户试图点击test.html里button时,实际上就会点击到iframe上的区块内容。\n本质上,通过引入`iframe`进行页面覆盖,同时设置`position`为`absolute`,通过`opacity`设置页面透明度,并将`z-index`设置到较大的值用以覆盖。\n从而实现完成一次点击劫持攻击。当然实际的攻击过程中,伪装的模式及方法更深。点击劫持本质上和`CSRF`攻击类似,都是通过诱导式,欺骗用户完成一系列操作行为,从而达到攻击劫持的目的。\n\n##### 图片覆盖攻击\nXSIO (Cross Site Image Overlaying)是一种通过调整图片style,将图片覆盖到指定的位置,从而诱导攻击的方式.\n````\n<a href=\"http://www.google.com\">\n <img src=\"IMAGE_URL\" style=\"position:absolute;top:90px;left:320px;\" />\n</a>\n````\n然后将图片伪装成正常的链接,构造一些文字吸引,欺骗用户点击,实现钓鱼的目的。\n\n##### 拖拽劫持与数据窃取\n拖拽劫持的方式是通过隐藏`iframe`中拖拽出攻击者希望得到的数据,然后放到攻击者能控制的另外一个页面中,从而窃取数据。\n详细实现可参考国内安全研究员xisigr构造的[POC](https://book.2cto.com/201208/1996.html)\n\n#### 二、点击劫持存在的Web安全隐患\n点击劫持存在的主要Web安全隐患,相对于`XSS`和`CSRF`攻击来说,主要是通过诱导式与页面产生交互,通过视觉欺诈的模式实施攻击行为,主要利用在钓鱼、欺诈、广告作弊等方面。\n而如何通过有效的手段去防范点击劫持,保证相对稳定及安全的访问环境,以及在测试过程中进行相关Web安全测试的探知是必要的先决要素。\n\n#### 三、如何防范点击劫持\n那么,如何通过技术手段,进行点击劫持的防范。主要有以下两种方式:\n> * frame busting: 基于JavaScript的iframe禁用嵌套\n> * X-Frame-Options: 基于HTTP头的拒绝iframe嵌套加载设置\n\n##### frame busting\nframe busting的条件判断语句:\n````\nif (top != self)\nif (top.location != self.location)\nif (top.location != location)\nif (parent.frames.length > 0)\nif (window != top)\nif (window.top !== window.self)\nif (window.self != window.top)\nif (parent && parent != window)\nif (parent && parent.frames && parent.frames.length>0)\nif((self.parent&&!(self.parent===self))&&(self.parent.frames.length!=0))\n````\n\nframe busting 的纠正动作代码:\n````\ntop.location = self.location\ntop.location.href = document.location.href\ntop.location.href = self.location.href\ntop.location.replace(self.location)\ntop.location.href = window.location.href\ntop.location.replace(document.location)\ntop.location.href = window.location.href\ntop.location.href = \"URL\"\ndocument.write('')\ntop.location = location\ntop.location.replace(document.location)\ntop.location.replace('URL')\ntop.location.href = document.location\ntop.location.replace(window.location.href)\ntop.location.href = location.href\nself.parent.location = document.location\nparent.location.href = self.document.location\ntop.location.href = self.location\ntop.location = window.location\ntop.location.replace(window.location.pathname)\nwindow.top.location = window.self.location\nsetTimeout(function(){document.body.innerHTML='';},1);\nwindow.self.onload = function(evt){document.body.innerHTML='';}\nvar url = window.location.href; top.location.replace(url)\n````\n\n##### X-Frame-Options\nX-Frame-Options 有三个值:\n> * DENY: 表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许。\n> * SAMEORIGIN: 表示该页面可以在相同域名页面的 frame 中展示。\n> * ALLOW-FROM uri: 表示该页面可以在指定来源的 frame 中展示。\n\n涉及`Apache`、`Nginx`、`IIS`对于相应头的配置,详情查看[MDN](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/X-Frame-Options)\n\n#### 参考文献\n1.[X-Frame-Options 响应头](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/X-Frame-Options)\n2.[Busting Frame Busting](https://www.cnblogs.com/LittleHann/p/3386055.html)\n3.[Web应用安全之点击劫持(CLICKJACKING)与X-FRAME-OPTIONS HEADER](https://www.cnblogs.com/xuanhun/p/3610981.html)\n4.[拖拽劫持与数据窃取](https://book.2cto.com/201208/1996.html)\n5.[白帽子讲Web安全](https://book.douban.com/subject/10546925)\n\n","slug":"networkSecurity-web-ClickJacking","published":1,"updated":"2019-06-09T17:21:12.949Z","layout":"post","photos":[],"link":"","_id":"cjwr0llvk0001bkvlicn04r5l","content":"<p>今天在测试前端开发项目,在生产环境的流程回归验证中,遇到了Web页面中使用<code>iframe</code>内嵌三方告知用户协议无法加载的问题。<code>Error</code>提示为<br><figure class=\"highlight livecodeserver\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">Refused <span class=\"built_in\">to</span> display <span class=\"string\">'https://www.xxx.com/'</span> <span class=\"keyword\">in</span> <span class=\"keyword\">a</span> frame because <span class=\"keyword\">it</span> <span class=\"built_in\">set</span> <span class=\"string\">'X-Frame-Options'</span> <span class=\"built_in\">to</span> <span class=\"string\">'sameorigin'</span>.</span><br></pre></td></tr></table></figure></p>\n<p>这是一种经典的防范点击劫持(<em>Click Jacking</em>)的方式,通过设置HTTP请求头<code>X-Frame-Options</code>来禁止跨域<code>iframe</code>的引入。以下通过:</p>\n<blockquote>\n<ul>\n<li>什么是点击劫持</li>\n<li>点击劫持存在的Web安全隐患</li>\n<li>如何防范点击劫持</li>\n</ul>\n</blockquote>\n<p>三方面进行基本的概述与总结。</p>\n<a id=\"more\"></a>\n<h4 id=\"一、什么是点击劫持\"><a href=\"#一、什么是点击劫持\" class=\"headerlink\" title=\"一、什么是点击劫持\"></a>一、什么是点击劫持</h4><p>点击劫持(ClickJacking)是一种通过视觉欺骗实现Web攻击的方式。主要的实现方式有两种:</p>\n<blockquote>\n<ul>\n<li><strong>iframe引入攻击</strong>:使用透明的iframe,覆盖在一个网页上,诱导性用户进行操作,在不知情的情况下点击透明的iframe页面;</li>\n<li><strong>图片覆盖攻击</strong>:使用图片覆盖网页,诱导用户点击;</li>\n<li><strong>拖拽劫持与数据窃取</strong>:诱导用户从隐藏不可见<code>iframe</code>中拖拽窃取数据;</li>\n</ul>\n</blockquote>\n<h5 id=\"iframe引入攻击\"><a href=\"#iframe引入攻击\" class=\"headerlink\" title=\"iframe引入攻击\"></a>iframe引入攻击</h5><p>test.html<br><figure class=\"highlight xml\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\"><!DOCTYPE HTML></span></span><br><span class=\"line\"><span class=\"tag\"><<span class=\"name\">html</span>></span></span><br><span class=\"line\"><span class=\"tag\"><<span class=\"name\">meta</span> <span class=\"attr\">http-equiv</span>=<span class=\"string\">\"Content-Type\"</span> <span class=\"attr\">content</span>=<span class=\"string\">\"text/html; charset=utf-8\"</span> /></span></span><br><span class=\"line\"><span class=\"tag\"><<span class=\"name\">head</span>></span></span><br><span class=\"line\"><span class=\"tag\"><<span class=\"name\">title</span>></span>bayuefen-demos-clickJacking<span class=\"tag\"></<span class=\"name\">title</span>></span></span><br><span class=\"line\"><span class=\"tag\"><<span class=\"name\">style</span>></span><span class=\"undefined\"></span></span><br><span class=\"line\"><span class=\"undefined\"> html,body,iframe{</span></span><br><span class=\"line\"><span class=\"undefined\"> display: block;</span></span><br><span class=\"line\"><span class=\"undefined\"> height: 100%;</span></span><br><span class=\"line\"><span class=\"undefined\"> width: 100%;</span></span><br><span class=\"line\"><span class=\"undefined\"> margin: 0;</span></span><br><span class=\"line\"><span class=\"undefined\"> padding: 0;</span></span><br><span class=\"line\"><span class=\"css\"> <span class=\"selector-tag\">border</span><span class=\"selector-pseudo\">:none</span>;</span></span><br><span class=\"line\"><span class=\"undefined\"> }</span></span><br><span class=\"line\"><span class=\"undefined\"> iframe{</span></span><br><span class=\"line\"><span class=\"css\"> <span class=\"selector-tag\">opacity</span><span class=\"selector-pseudo\">:0</span>;</span></span><br><span class=\"line\"><span class=\"undefined\"> filter:alpha(opacity=0);</span></span><br><span class=\"line\"><span class=\"css\"> <span class=\"selector-tag\">position</span><span class=\"selector-pseudo\">:absolute</span>;</span></span><br><span class=\"line\"><span class=\"css\"> <span class=\"selector-tag\">z-index</span><span class=\"selector-pseudo\">:2</span>;</span></span><br><span class=\"line\"><span class=\"undefined\"> }</span></span><br><span class=\"line\"><span class=\"undefined\"> button{</span></span><br><span class=\"line\"><span class=\"css\"> <span class=\"selector-tag\">position</span><span class=\"selector-pseudo\">:absolute</span>;</span></span><br><span class=\"line\"><span class=\"undefined\"> top: 315px;</span></span><br><span class=\"line\"><span class=\"undefined\"> left: 462px;</span></span><br><span class=\"line\"><span class=\"undefined\"> z-index: 1;</span></span><br><span class=\"line\"><span class=\"undefined\"> width: 72px;</span></span><br><span class=\"line\"><span class=\"undefined\"> height: 26px;</span></span><br><span class=\"line\"><span class=\"undefined\"> }</span></span><br><span class=\"line\"><span class=\"undefined\"></span><span class=\"tag\"></<span class=\"name\">style</span>></span></span><br><span class=\"line\"><span class=\"tag\"></<span class=\"name\">head</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">body</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">button</span>></span>Click-Jacking-Button<span class=\"tag\"></<span class=\"name\">button</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">iframe</span> <span class=\"attr\">src</span>=<span class=\"string\">\"http://www.google.com\"</span>></span><span class=\"tag\"></<span class=\"name\">iframe</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">body</span>></span></span><br><span class=\"line\"><span class=\"tag\"></<span class=\"name\">html</span>></span></span><br></pre></td></tr></table></figure></p>\n<p>当用户试图点击test.html里button时,实际上就会点击到iframe上的区块内容。<br>本质上,通过引入<code>iframe</code>进行页面覆盖,同时设置<code>position</code>为<code>absolute</code>,通过<code>opacity</code>设置页面透明度,并将<code>z-index</code>设置到较大的值用以覆盖。<br>从而实现完成一次点击劫持攻击。当然实际的攻击过程中,伪装的模式及方法更深。点击劫持本质上和<code>CSRF</code>攻击类似,都是通过诱导式,欺骗用户完成一系列操作行为,从而达到攻击劫持的目的。</p>\n<h5 id=\"图片覆盖攻击\"><a href=\"#图片覆盖攻击\" class=\"headerlink\" title=\"图片覆盖攻击\"></a>图片覆盖攻击</h5><p>XSIO (Cross Site Image Overlaying)是一种通过调整图片style,将图片覆盖到指定的位置,从而诱导攻击的方式.<br><figure class=\"highlight stylus\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><<span class=\"selector-tag\">a</span> href=<span class=\"string\">\"http://www.google.com\"</span>></span><br><span class=\"line\"> <<span class=\"selector-tag\">img</span> src=<span class=\"string\">\"IMAGE_URL\"</span> style=<span class=\"string\">\"position:absolute;top:90px;left:320px;\"</span> /></span><br><span class=\"line\"></a></span><br></pre></td></tr></table></figure></p>\n<p>然后将图片伪装成正常的链接,构造一些文字吸引,欺骗用户点击,实现钓鱼的目的。</p>\n<h5 id=\"拖拽劫持与数据窃取\"><a href=\"#拖拽劫持与数据窃取\" class=\"headerlink\" title=\"拖拽劫持与数据窃取\"></a>拖拽劫持与数据窃取</h5><p>拖拽劫持的方式是通过隐藏<code>iframe</code>中拖拽出攻击者希望得到的数据,然后放到攻击者能控制的另外一个页面中,从而窃取数据。<br>详细实现可参考国内安全研究员xisigr构造的<a href=\"https://book.2cto.com/201208/1996.html\" target=\"_blank\" rel=\"noopener\">POC</a></p>\n<h4 id=\"二、点击劫持存在的Web安全隐患\"><a href=\"#二、点击劫持存在的Web安全隐患\" class=\"headerlink\" title=\"二、点击劫持存在的Web安全隐患\"></a>二、点击劫持存在的Web安全隐患</h4><p>点击劫持存在的主要Web安全隐患,相对于<code>XSS</code>和<code>CSRF</code>攻击来说,主要是通过诱导式与页面产生交互,通过视觉欺诈的模式实施攻击行为,主要利用在钓鱼、欺诈、广告作弊等方面。<br>而如何通过有效的手段去防范点击劫持,保证相对稳定及安全的访问环境,以及在测试过程中进行相关Web安全测试的探知是必要的先决要素。</p>\n<h4 id=\"三、如何防范点击劫持\"><a href=\"#三、如何防范点击劫持\" class=\"headerlink\" title=\"三、如何防范点击劫持\"></a>三、如何防范点击劫持</h4><p>那么,如何通过技术手段,进行点击劫持的防范。主要有以下两种方式:</p>\n<blockquote>\n<ul>\n<li>frame busting: 基于JavaScript的iframe禁用嵌套</li>\n<li>X-Frame-Options: 基于HTTP头的拒绝iframe嵌套加载设置</li>\n</ul>\n</blockquote>\n<h5 id=\"frame-busting\"><a href=\"#frame-busting\" class=\"headerlink\" title=\"frame busting\"></a>frame busting</h5><p>frame busting的条件判断语句:<br><figure class=\"highlight lisp\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">if (<span class=\"name\">top</span> != self)</span><br><span class=\"line\">if (<span class=\"name\">top</span>.location != self.location)</span><br><span class=\"line\">if (<span class=\"name\">top</span>.location != location)</span><br><span class=\"line\">if (<span class=\"name\">parent</span>.frames.length > <span class=\"number\">0</span>)</span><br><span class=\"line\">if (<span class=\"name\">window</span> != top)</span><br><span class=\"line\">if (<span class=\"name\">window</span>.top !== window.self)</span><br><span class=\"line\">if (<span class=\"name\">window</span>.self != window.top)</span><br><span class=\"line\">if (<span class=\"name\">parent</span> <span class=\"symbol\">&&</span> parent != window)</span><br><span class=\"line\">if (<span class=\"name\">parent</span> <span class=\"symbol\">&&</span> parent.frames <span class=\"symbol\">&&</span> parent.frames.length>0)</span><br><span class=\"line\">if((<span class=\"name\">self</span>.parent&&!(<span class=\"name\">self</span>.parent===self))<span class=\"symbol\">&&</span>(<span class=\"name\">self</span>.parent.frames.length!=0))</span><br></pre></td></tr></table></figure></p>\n<p>frame busting 的纠正动作代码:<br><figure class=\"highlight crmsh\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">top.<span class=\"keyword\">location</span> <span class=\"title\">= self</span>.location</span><br><span class=\"line\">top.location.href = document.location.href</span><br><span class=\"line\">top.location.href = self.location.href</span><br><span class=\"line\">top.location.replace(self.location)</span><br><span class=\"line\">top.location.href = window.location.href</span><br><span class=\"line\">top.location.replace(document.location)</span><br><span class=\"line\">top.location.href = window.location.href</span><br><span class=\"line\">top.location.href = <span class=\"string\">\"URL\"</span></span><br><span class=\"line\">document.<span class=\"keyword\">write</span>('')</span><br><span class=\"line\">top.<span class=\"keyword\">location</span> <span class=\"title\">= location</span></span><br><span class=\"line\">top.location.replace(document.location)</span><br><span class=\"line\">top.location.replace('URL')</span><br><span class=\"line\">top.location.href = document.location</span><br><span class=\"line\">top.location.replace(window.location.href)</span><br><span class=\"line\">top.location.href = location.href</span><br><span class=\"line\">self.parent.<span class=\"keyword\">location</span> <span class=\"title\">= document</span>.location</span><br><span class=\"line\">parent.location.href = self.document.location</span><br><span class=\"line\">top.location.href = self.location</span><br><span class=\"line\">top.<span class=\"keyword\">location</span> <span class=\"title\">= window</span>.location</span><br><span class=\"line\">top.location.replace(window.location.pathname)</span><br><span class=\"line\">window.top.<span class=\"keyword\">location</span> <span class=\"title\">= window</span>.self.location</span><br><span class=\"line\">setTimeout(function(){document.body.<span class=\"attr\">innerHTML=</span>'';},<span class=\"number\">1</span>);</span><br><span class=\"line\">window.self.onload = function(evt){document.body.<span class=\"attr\">innerHTML=</span>'';}</span><br><span class=\"line\">var url = window.location.href; top.location.replace(url)</span><br></pre></td></tr></table></figure></p>\n<h5 id=\"X-Frame-Options\"><a href=\"#X-Frame-Options\" class=\"headerlink\" title=\"X-Frame-Options\"></a>X-Frame-Options</h5><p>X-Frame-Options 有三个值:</p>\n<blockquote>\n<ul>\n<li>DENY: 表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许。</li>\n<li>SAMEORIGIN: 表示该页面可以在相同域名页面的 frame 中展示。</li>\n<li>ALLOW-FROM uri: 表示该页面可以在指定来源的 frame 中展示。</li>\n</ul>\n</blockquote>\n<p>涉及<code>Apache</code>、<code>Nginx</code>、<code>IIS</code>对于相应头的配置,详情查看<a href=\"https://developer.mozilla.org/zh-CN/docs/Web/HTTP/X-Frame-Options\" target=\"_blank\" rel=\"noopener\">MDN</a></p>\n<h4 id=\"参考文献\"><a href=\"#参考文献\" class=\"headerlink\" title=\"参考文献\"></a>参考文献</h4><p>1.<a href=\"https://developer.mozilla.org/zh-CN/docs/Web/HTTP/X-Frame-Options\" target=\"_blank\" rel=\"noopener\">X-Frame-Options 响应头</a><br>2.<a href=\"https://www.cnblogs.com/LittleHann/p/3386055.html\" target=\"_blank\" rel=\"noopener\">Busting Frame Busting</a><br>3.<a href=\"https://www.cnblogs.com/xuanhun/p/3610981.html\" target=\"_blank\" rel=\"noopener\">Web应用安全之点击劫持(CLICKJACKING)与X-FRAME-OPTIONS HEADER</a><br>4.<a href=\"https://book.2cto.com/201208/1996.html\" target=\"_blank\" rel=\"noopener\">拖拽劫持与数据窃取</a><br>5.<a href=\"https://book.douban.com/subject/10546925\" target=\"_blank\" rel=\"noopener\">白帽子讲Web安全</a></p>\n","site":{"data":{}},"excerpt":"<p>今天在测试前端开发项目,在生产环境的流程回归验证中,遇到了Web页面中使用<code>iframe</code>内嵌三方告知用户协议无法加载的问题。<code>Error</code>提示为<br><figure class=\"highlight livecodeserver\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">Refused <span class=\"built_in\">to</span> display <span class=\"string\">'https://www.xxx.com/'</span> <span class=\"keyword\">in</span> <span class=\"keyword\">a</span> frame because <span class=\"keyword\">it</span> <span class=\"built_in\">set</span> <span class=\"string\">'X-Frame-Options'</span> <span class=\"built_in\">to</span> <span class=\"string\">'sameorigin'</span>.</span><br></pre></td></tr></table></figure></p>\n<p>这是一种经典的防范点击劫持(<em>Click Jacking</em>)的方式,通过设置HTTP请求头<code>X-Frame-Options</code>来禁止跨域<code>iframe</code>的引入。以下通过:</p>\n<blockquote>\n<ul>\n<li>什么是点击劫持</li>\n<li>点击劫持存在的Web安全隐患</li>\n<li>如何防范点击劫持</li>\n</ul>\n</blockquote>\n<p>三方面进行基本的概述与总结。</p>","more":"<h4 id=\"一、什么是点击劫持\"><a href=\"#一、什么是点击劫持\" class=\"headerlink\" title=\"一、什么是点击劫持\"></a>一、什么是点击劫持</h4><p>点击劫持(ClickJacking)是一种通过视觉欺骗实现Web攻击的方式。主要的实现方式有两种:</p>\n<blockquote>\n<ul>\n<li><strong>iframe引入攻击</strong>:使用透明的iframe,覆盖在一个网页上,诱导性用户进行操作,在不知情的情况下点击透明的iframe页面;</li>\n<li><strong>图片覆盖攻击</strong>:使用图片覆盖网页,诱导用户点击;</li>\n<li><strong>拖拽劫持与数据窃取</strong>:诱导用户从隐藏不可见<code>iframe</code>中拖拽窃取数据;</li>\n</ul>\n</blockquote>\n<h5 id=\"iframe引入攻击\"><a href=\"#iframe引入攻击\" class=\"headerlink\" title=\"iframe引入攻击\"></a>iframe引入攻击</h5><p>test.html<br><figure class=\"highlight xml\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\"><!DOCTYPE HTML></span></span><br><span class=\"line\"><span class=\"tag\"><<span class=\"name\">html</span>></span></span><br><span class=\"line\"><span class=\"tag\"><<span class=\"name\">meta</span> <span class=\"attr\">http-equiv</span>=<span class=\"string\">\"Content-Type\"</span> <span class=\"attr\">content</span>=<span class=\"string\">\"text/html; charset=utf-8\"</span> /></span></span><br><span class=\"line\"><span class=\"tag\"><<span class=\"name\">head</span>></span></span><br><span class=\"line\"><span class=\"tag\"><<span class=\"name\">title</span>></span>bayuefen-demos-clickJacking<span class=\"tag\"></<span class=\"name\">title</span>></span></span><br><span class=\"line\"><span class=\"tag\"><<span class=\"name\">style</span>></span><span class=\"undefined\"></span></span><br><span class=\"line\"><span class=\"undefined\"> html,body,iframe{</span></span><br><span class=\"line\"><span class=\"undefined\"> display: block;</span></span><br><span class=\"line\"><span class=\"undefined\"> height: 100%;</span></span><br><span class=\"line\"><span class=\"undefined\"> width: 100%;</span></span><br><span class=\"line\"><span class=\"undefined\"> margin: 0;</span></span><br><span class=\"line\"><span class=\"undefined\"> padding: 0;</span></span><br><span class=\"line\"><span class=\"css\"> <span class=\"selector-tag\">border</span><span class=\"selector-pseudo\">:none</span>;</span></span><br><span class=\"line\"><span class=\"undefined\"> }</span></span><br><span class=\"line\"><span class=\"undefined\"> iframe{</span></span><br><span class=\"line\"><span class=\"css\"> <span class=\"selector-tag\">opacity</span><span class=\"selector-pseudo\">:0</span>;</span></span><br><span class=\"line\"><span class=\"undefined\"> filter:alpha(opacity=0);</span></span><br><span class=\"line\"><span class=\"css\"> <span class=\"selector-tag\">position</span><span class=\"selector-pseudo\">:absolute</span>;</span></span><br><span class=\"line\"><span class=\"css\"> <span class=\"selector-tag\">z-index</span><span class=\"selector-pseudo\">:2</span>;</span></span><br><span class=\"line\"><span class=\"undefined\"> }</span></span><br><span class=\"line\"><span class=\"undefined\"> button{</span></span><br><span class=\"line\"><span class=\"css\"> <span class=\"selector-tag\">position</span><span class=\"selector-pseudo\">:absolute</span>;</span></span><br><span class=\"line\"><span class=\"undefined\"> top: 315px;</span></span><br><span class=\"line\"><span class=\"undefined\"> left: 462px;</span></span><br><span class=\"line\"><span class=\"undefined\"> z-index: 1;</span></span><br><span class=\"line\"><span class=\"undefined\"> width: 72px;</span></span><br><span class=\"line\"><span class=\"undefined\"> height: 26px;</span></span><br><span class=\"line\"><span class=\"undefined\"> }</span></span><br><span class=\"line\"><span class=\"undefined\"></span><span class=\"tag\"></<span class=\"name\">style</span>></span></span><br><span class=\"line\"><span class=\"tag\"></<span class=\"name\">head</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">body</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">button</span>></span>Click-Jacking-Button<span class=\"tag\"></<span class=\"name\">button</span>></span></span><br><span class=\"line\"> <span class=\"tag\"><<span class=\"name\">iframe</span> <span class=\"attr\">src</span>=<span class=\"string\">\"http://www.google.com\"</span>></span><span class=\"tag\"></<span class=\"name\">iframe</span>></span></span><br><span class=\"line\"> <span class=\"tag\"></<span class=\"name\">body</span>></span></span><br><span class=\"line\"><span class=\"tag\"></<span class=\"name\">html</span>></span></span><br></pre></td></tr></table></figure></p>\n<p>当用户试图点击test.html里button时,实际上就会点击到iframe上的区块内容。<br>本质上,通过引入<code>iframe</code>进行页面覆盖,同时设置<code>position</code>为<code>absolute</code>,通过<code>opacity</code>设置页面透明度,并将<code>z-index</code>设置到较大的值用以覆盖。<br>从而实现完成一次点击劫持攻击。当然实际的攻击过程中,伪装的模式及方法更深。点击劫持本质上和<code>CSRF</code>攻击类似,都是通过诱导式,欺骗用户完成一系列操作行为,从而达到攻击劫持的目的。</p>\n<h5 id=\"图片覆盖攻击\"><a href=\"#图片覆盖攻击\" class=\"headerlink\" title=\"图片覆盖攻击\"></a>图片覆盖攻击</h5><p>XSIO (Cross Site Image Overlaying)是一种通过调整图片style,将图片覆盖到指定的位置,从而诱导攻击的方式.<br><figure class=\"highlight stylus\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><<span class=\"selector-tag\">a</span> href=<span class=\"string\">\"http://www.google.com\"</span>></span><br><span class=\"line\"> <<span class=\"selector-tag\">img</span> src=<span class=\"string\">\"IMAGE_URL\"</span> style=<span class=\"string\">\"position:absolute;top:90px;left:320px;\"</span> /></span><br><span class=\"line\"></a></span><br></pre></td></tr></table></figure></p>\n<p>然后将图片伪装成正常的链接,构造一些文字吸引,欺骗用户点击,实现钓鱼的目的。</p>\n<h5 id=\"拖拽劫持与数据窃取\"><a href=\"#拖拽劫持与数据窃取\" class=\"headerlink\" title=\"拖拽劫持与数据窃取\"></a>拖拽劫持与数据窃取</h5><p>拖拽劫持的方式是通过隐藏<code>iframe</code>中拖拽出攻击者希望得到的数据,然后放到攻击者能控制的另外一个页面中,从而窃取数据。<br>详细实现可参考国内安全研究员xisigr构造的<a href=\"https://book.2cto.com/201208/1996.html\" target=\"_blank\" rel=\"noopener\">POC</a></p>\n<h4 id=\"二、点击劫持存在的Web安全隐患\"><a href=\"#二、点击劫持存在的Web安全隐患\" class=\"headerlink\" title=\"二、点击劫持存在的Web安全隐患\"></a>二、点击劫持存在的Web安全隐患</h4><p>点击劫持存在的主要Web安全隐患,相对于<code>XSS</code>和<code>CSRF</code>攻击来说,主要是通过诱导式与页面产生交互,通过视觉欺诈的模式实施攻击行为,主要利用在钓鱼、欺诈、广告作弊等方面。<br>而如何通过有效的手段去防范点击劫持,保证相对稳定及安全的访问环境,以及在测试过程中进行相关Web安全测试的探知是必要的先决要素。</p>\n<h4 id=\"三、如何防范点击劫持\"><a href=\"#三、如何防范点击劫持\" class=\"headerlink\" title=\"三、如何防范点击劫持\"></a>三、如何防范点击劫持</h4><p>那么,如何通过技术手段,进行点击劫持的防范。主要有以下两种方式:</p>\n<blockquote>\n<ul>\n<li>frame busting: 基于JavaScript的iframe禁用嵌套</li>\n<li>X-Frame-Options: 基于HTTP头的拒绝iframe嵌套加载设置</li>\n</ul>\n</blockquote>\n<h5 id=\"frame-busting\"><a href=\"#frame-busting\" class=\"headerlink\" title=\"frame busting\"></a>frame busting</h5><p>frame busting的条件判断语句:<br><figure class=\"highlight lisp\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">if (<span class=\"name\">top</span> != self)</span><br><span class=\"line\">if (<span class=\"name\">top</span>.location != self.location)</span><br><span class=\"line\">if (<span class=\"name\">top</span>.location != location)</span><br><span class=\"line\">if (<span class=\"name\">parent</span>.frames.length > <span class=\"number\">0</span>)</span><br><span class=\"line\">if (<span class=\"name\">window</span> != top)</span><br><span class=\"line\">if (<span class=\"name\">window</span>.top !== window.self)</span><br><span class=\"line\">if (<span class=\"name\">window</span>.self != window.top)</span><br><span class=\"line\">if (<span class=\"name\">parent</span> <span class=\"symbol\">&&</span> parent != window)</span><br><span class=\"line\">if (<span class=\"name\">parent</span> <span class=\"symbol\">&&</span> parent.frames <span class=\"symbol\">&&</span> parent.frames.length>0)</span><br><span class=\"line\">if((<span class=\"name\">self</span>.parent&&!(<span class=\"name\">self</span>.parent===self))<span class=\"symbol\">&&</span>(<span class=\"name\">self</span>.parent.frames.length!=0))</span><br></pre></td></tr></table></figure></p>\n<p>frame busting 的纠正动作代码:<br><figure class=\"highlight crmsh\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">top.<span class=\"keyword\">location</span> <span class=\"title\">= self</span>.location</span><br><span class=\"line\">top.location.href = document.location.href</span><br><span class=\"line\">top.location.href = self.location.href</span><br><span class=\"line\">top.location.replace(self.location)</span><br><span class=\"line\">top.location.href = window.location.href</span><br><span class=\"line\">top.location.replace(document.location)</span><br><span class=\"line\">top.location.href = window.location.href</span><br><span class=\"line\">top.location.href = <span class=\"string\">\"URL\"</span></span><br><span class=\"line\">document.<span class=\"keyword\">write</span>('')</span><br><span class=\"line\">top.<span class=\"keyword\">location</span> <span class=\"title\">= location</span></span><br><span class=\"line\">top.location.replace(document.location)</span><br><span class=\"line\">top.location.replace('URL')</span><br><span class=\"line\">top.location.href = document.location</span><br><span class=\"line\">top.location.replace(window.location.href)</span><br><span class=\"line\">top.location.href = location.href</span><br><span class=\"line\">self.parent.<span class=\"keyword\">location</span> <span class=\"title\">= document</span>.location</span><br><span class=\"line\">parent.location.href = self.document.location</span><br><span class=\"line\">top.location.href = self.location</span><br><span class=\"line\">top.<span class=\"keyword\">location</span> <span class=\"title\">= window</span>.location</span><br><span class=\"line\">top.location.replace(window.location.pathname)</span><br><span class=\"line\">window.top.<span class=\"keyword\">location</span> <span class=\"title\">= window</span>.self.location</span><br><span class=\"line\">setTimeout(function(){document.body.<span class=\"attr\">innerHTML=</span>'';},<span class=\"number\">1</span>);</span><br><span class=\"line\">window.self.onload = function(evt){document.body.<span class=\"attr\">innerHTML=</span>'';}</span><br><span class=\"line\">var url = window.location.href; top.location.replace(url)</span><br></pre></td></tr></table></figure></p>\n<h5 id=\"X-Frame-Options\"><a href=\"#X-Frame-Options\" class=\"headerlink\" title=\"X-Frame-Options\"></a>X-Frame-Options</h5><p>X-Frame-Options 有三个值:</p>\n<blockquote>\n<ul>\n<li>DENY: 表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许。</li>\n<li>SAMEORIGIN: 表示该页面可以在相同域名页面的 frame 中展示。</li>\n<li>ALLOW-FROM uri: 表示该页面可以在指定来源的 frame 中展示。</li>\n</ul>\n</blockquote>\n<p>涉及<code>Apache</code>、<code>Nginx</code>、<code>IIS</code>对于相应头的配置,详情查看<a href=\"https://developer.mozilla.org/zh-CN/docs/Web/HTTP/X-Frame-Options\" target=\"_blank\" rel=\"noopener\">MDN</a></p>\n<h4 id=\"参考文献\"><a href=\"#参考文献\" class=\"headerlink\" title=\"参考文献\"></a>参考文献</h4><p>1.<a href=\"https://developer.mozilla.org/zh-CN/docs/Web/HTTP/X-Frame-Options\" target=\"_blank\" rel=\"noopener\">X-Frame-Options 响应头</a><br>2.<a href=\"https://www.cnblogs.com/LittleHann/p/3386055.html\" target=\"_blank\" rel=\"noopener\">Busting Frame Busting</a><br>3.<a href=\"https://www.cnblogs.com/xuanhun/p/3610981.html\" target=\"_blank\" rel=\"noopener\">Web应用安全之点击劫持(CLICKJACKING)与X-FRAME-OPTIONS HEADER</a><br>4.<a href=\"https://book.2cto.com/201208/1996.html\" target=\"_blank\" rel=\"noopener\">拖拽劫持与数据窃取</a><br>5.<a href=\"https://book.douban.com/subject/10546925\" target=\"_blank\" rel=\"noopener\">白帽子讲Web安全</a></p>"},{"title":"【Python】基于Jenkins自动化部署的环境健康检查及发布通知服务的工具实现","date":"2019-06-09T09:20:26.000Z","top":false,"reward":false,"comments":0,"_content":"create Markdown file.\nbalabala and so on etc...\n\n<!-- more -->\n","source":"_posts/python-health-check-util.md","raw":"---\ntitle: 【Python】基于Jenkins自动化部署的环境健康检查及发布通知服务的工具实现\ndate: 2019-06-09 17:20:26\ntop: false\nreward: false\ncomments: false\ntags:\n - Python\n - jenkins\n - itchat\n - 日记\n---\ncreate Markdown file.\nbalabala and so on etc...\n\n<!-- more -->\n","slug":"python-health-check-util","published":1,"updated":"2019-06-09T17:21:12.950Z","layout":"post","photos":[],"link":"","_id":"cjwr0llvn0003bkvlyhrfgjfm","content":"<p>create Markdown file.<br>balabala and so on etc…</p>\n<a id=\"more\"></a>\n","site":{"data":{}},"excerpt":"<p>create Markdown file.<br>balabala and so on etc…</p>","more":""},{"title":"【Server】基于Node及MongoDB的服务配置与部署","date":"2019-04-19T02:52:36.000Z","_content":"在过去几天的调研参考之后,拟定*Fork*Github相关的repositories,构建整站解决方案。\n- 后端服务基于[node-elm](https://github.com/bayuefen/node-elm)\n- 数据库采用[MongoDB](https://www.mongodb.com/)\n- 商户系统Web服务基于[vue2-manage](https://github.com/bayuefen/vue2-manage)\n- 用户系统Web服务基于[vue2-elm](https://github.com/bayuefen/vue2-elm)\n\n本文主要根据Node及MongoDB,在服务配置与部署过程中所遇到的一些问题的解决方案进行总结。\n\n<!-- more -->\n\n### Node Server install\n#### 安装依赖\n1、node (6.0 及以上版本)\n2、mongodb (开启状态)\n3、GraphicsMagick (裁切图片)\n4、yarn/npm(Node packages管理)\n5、pm2(守护进程)\n\n#### 项目运行\n````\ngit clone https://github.com/bayuefen/node-elm \n\ncd node-elm\n\nyarn install / npm install\n\nyarn run dev\n\n````\n访问: `http://localhost:8001`(如果已启动前台程序,则不需打开此地址)\n本人将`node-elm`工程项目clone至阿里云ECS上,安装好上述环境依赖及node packages后,使用pm2守护进程\n````\npm2 start 'yarn run dev'\n````\n\n\n### MongoDB Community Edition install(.rpm Packages)\n#### 安装流程\n1、创建`/etc/yum.repos.d/mongodb-org-4.2.repo`文件;\n2、写入`yum`安装配置信息\n````\n[mongodb-org-4.2]\nname=MongoDB Repository\nbaseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.1/x86_64/\ngpgcheck=1\nenabled=1\ngpgkey=https://www.mongodb.org/static/pgp/server-4.2.asc\n````\n3、安装MongoDB packages\n````\nsudo yum install -y mongodb-org-unstable \n````\n4、!特定版本packages及组件安装\n````\nsudo yum install -y mongodb-org-unstable-4.1.10 mongodb-org-unstable-server-4.1.10 mongodb-org-unstable-shell-4.1.10 mongodb-org-unstable-mongos-4.1.10 mongodb-org-unstable-tools-4.1.10\n````\n\n#### 启用流程\n\n1、MongoDB默认文件创建\n- /var/lib/mongo (the data directory)\n- /var/log/mongodb (the log directory)\n\n2、启用MongoDB\n````\nsudo service mongod start\n````\n\n3、验证启用是否成功\n`27017`是默认端口,可以在`/etc/mongod.conf`中更改。\n````\nsudo chkconfig mongod on\n````\n4、关闭MongoDB\n````\nsudo service mongod stop\n````\n5、重启MongoDB\n````\nsudo service mongod restart\n````\n6、MongoDB使用\n````\nmongo\n````\n\n#### MongoDB权限配置\n默认安装的MongoDB是不具备权限验证的,因为需要通过Navicat访问远程ECS上的DB服务,为了安全起见,给MongoDB配置相应的权限验证机制是非常必要的。\n##### 超级管理员创建\n````\n# 启用mongoDB后使用\nmongo\n\n# 切换至超级管理员模式\nuse admin\n\n# 创建超级管理员\ndb.createUser({user:'userName', pwd:'password', roles:['userAdminAnyDatabase']})\n\n````\nrole类型:\n- readAnyDatabase 任何数据库的只读权限(和read相似)\n- readWriteAnyDatabase 任何数据库的读写权限(和readWrite相似)\n- userAdminAnyDatabase 任何数据库用户的管理权限(和userAdmin相似)\n- dbAdminAnyDatabase 任何数据库的管理权限(dbAdmin相似)\n\n##### 创建用户\n````\n# 进入超级管理员模式并验证\nuse admin\n\ndb.auth('userName','password')\n\n# 查看数据库\nshow dbs\n\n# 进入到elm数据库\nshow elm\n\n# 创建elm数据库用户\ndb.createUser({user:'user-bayuefen',pwd:'password',roles:[{role:'readWrite',db:'elm'}]})\n````\n以上步骤核心完成超级管理员的创建验证及单个数据库的用户创建。\n具备了以上的权限配置,我们即可通过navicat进行数据库的查看与管理;同时,在`Node`服务中我们可以使用`mongoose` package对数据库进行CURD\n###### Node\n````\nimport mongoose from 'mongoose';\nmongoose.connect('mongodb://user:pwd@localhost:27017/elm', {useMongoClient:true});\n````\n###### Navicat\n````\nserver: ECS ip\nport: 27017 (default)\nuser: user\npwd: password\ndb: db_name\n````\nps:ECS需要通过阿里云后台服务打开安全策略中的27017端口的外网访问权限\n\n#### 问题记录\nQ:MongoDB在配置权限之后,重启Mongo,无法启动服务。抛`Unit mongod.service entered failed state.`异常\nA:排查后,发现Centos需要将MongoDB添加至`systemd`中,否则会出现问题。[Issues](https://github.com/jingxinxin/tiankeng/issues/5)\n添加`systemd`\n````\nvim /usr/lib/systemd/system/mongod.service\n\n````\n> [Unit]\n> Description=mongodb database\n> \n> [Service]\n> User=mongod\n> Group=mongod\n> Environment=\"OPTIONS=--quiet -f /etc/mongod.conf\"\n> ExecStart=/usr/bin/mongod $OPTIONS run\n> PIDFile=/var/run/mongodb/mongod.pid\n> \n> [Install]\n> WantedBy=multi-user.target\n\n创建链接:\n````\nln -s /usr/lib/systemd/system/mongod.service /etc/systemd/system/multi-user.target.wants/\n````\n\n重新加载systemctl\n````\nsystemctl daemon-reload\n````\n#### 参考文档\n- [Centos7](https://docs.mongodb.com/master/tutorial/install-mongodb-on-red-hat/#to-use-non-default-directories)\n- [macOS](https://docs.mongodb.com/master/tutorial/install-mongodb-on-os-x/)\n- [Windows](https://docs.mongodb.com/master/tutorial/install-mongodb-on-windows/)\n\n\n\n","source":"_posts/node-server-deploy.md","raw":"---\ntitle: 【Server】基于Node及MongoDB的服务配置与部署\ndate: 2019-04-19 10:52:36\ntags:\n - Node\n - MongoDB\n - 服务部署\n - 日记\n---\n在过去几天的调研参考之后,拟定*Fork*Github相关的repositories,构建整站解决方案。\n- 后端服务基于[node-elm](https://github.com/bayuefen/node-elm)\n- 数据库采用[MongoDB](https://www.mongodb.com/)\n- 商户系统Web服务基于[vue2-manage](https://github.com/bayuefen/vue2-manage)\n- 用户系统Web服务基于[vue2-elm](https://github.com/bayuefen/vue2-elm)\n\n本文主要根据Node及MongoDB,在服务配置与部署过程中所遇到的一些问题的解决方案进行总结。\n\n<!-- more -->\n\n### Node Server install\n#### 安装依赖\n1、node (6.0 及以上版本)\n2、mongodb (开启状态)\n3、GraphicsMagick (裁切图片)\n4、yarn/npm(Node packages管理)\n5、pm2(守护进程)\n\n#### 项目运行\n````\ngit clone https://github.com/bayuefen/node-elm \n\ncd node-elm\n\nyarn install / npm install\n\nyarn run dev\n\n````\n访问: `http://localhost:8001`(如果已启动前台程序,则不需打开此地址)\n本人将`node-elm`工程项目clone至阿里云ECS上,安装好上述环境依赖及node packages后,使用pm2守护进程\n````\npm2 start 'yarn run dev'\n````\n\n\n### MongoDB Community Edition install(.rpm Packages)\n#### 安装流程\n1、创建`/etc/yum.repos.d/mongodb-org-4.2.repo`文件;\n2、写入`yum`安装配置信息\n````\n[mongodb-org-4.2]\nname=MongoDB Repository\nbaseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.1/x86_64/\ngpgcheck=1\nenabled=1\ngpgkey=https://www.mongodb.org/static/pgp/server-4.2.asc\n````\n3、安装MongoDB packages\n````\nsudo yum install -y mongodb-org-unstable \n````\n4、!特定版本packages及组件安装\n````\nsudo yum install -y mongodb-org-unstable-4.1.10 mongodb-org-unstable-server-4.1.10 mongodb-org-unstable-shell-4.1.10 mongodb-org-unstable-mongos-4.1.10 mongodb-org-unstable-tools-4.1.10\n````\n\n#### 启用流程\n\n1、MongoDB默认文件创建\n- /var/lib/mongo (the data directory)\n- /var/log/mongodb (the log directory)\n\n2、启用MongoDB\n````\nsudo service mongod start\n````\n\n3、验证启用是否成功\n`27017`是默认端口,可以在`/etc/mongod.conf`中更改。\n````\nsudo chkconfig mongod on\n````\n4、关闭MongoDB\n````\nsudo service mongod stop\n````\n5、重启MongoDB\n````\nsudo service mongod restart\n````\n6、MongoDB使用\n````\nmongo\n````\n\n#### MongoDB权限配置\n默认安装的MongoDB是不具备权限验证的,因为需要通过Navicat访问远程ECS上的DB服务,为了安全起见,给MongoDB配置相应的权限验证机制是非常必要的。\n##### 超级管理员创建\n````\n# 启用mongoDB后使用\nmongo\n\n# 切换至超级管理员模式\nuse admin\n\n# 创建超级管理员\ndb.createUser({user:'userName', pwd:'password', roles:['userAdminAnyDatabase']})\n\n````\nrole类型:\n- readAnyDatabase 任何数据库的只读权限(和read相似)\n- readWriteAnyDatabase 任何数据库的读写权限(和readWrite相似)\n- userAdminAnyDatabase 任何数据库用户的管理权限(和userAdmin相似)\n- dbAdminAnyDatabase 任何数据库的管理权限(dbAdmin相似)\n\n##### 创建用户\n````\n# 进入超级管理员模式并验证\nuse admin\n\ndb.auth('userName','password')\n\n# 查看数据库\nshow dbs\n\n# 进入到elm数据库\nshow elm\n\n# 创建elm数据库用户\ndb.createUser({user:'user-bayuefen',pwd:'password',roles:[{role:'readWrite',db:'elm'}]})\n````\n以上步骤核心完成超级管理员的创建验证及单个数据库的用户创建。\n具备了以上的权限配置,我们即可通过navicat进行数据库的查看与管理;同时,在`Node`服务中我们可以使用`mongoose` package对数据库进行CURD\n###### Node\n````\nimport mongoose from 'mongoose';\nmongoose.connect('mongodb://user:pwd@localhost:27017/elm', {useMongoClient:true});\n````\n###### Navicat\n````\nserver: ECS ip\nport: 27017 (default)\nuser: user\npwd: password\ndb: db_name\n````\nps:ECS需要通过阿里云后台服务打开安全策略中的27017端口的外网访问权限\n\n#### 问题记录\nQ:MongoDB在配置权限之后,重启Mongo,无法启动服务。抛`Unit mongod.service entered failed state.`异常\nA:排查后,发现Centos需要将MongoDB添加至`systemd`中,否则会出现问题。[Issues](https://github.com/jingxinxin/tiankeng/issues/5)\n添加`systemd`\n````\nvim /usr/lib/systemd/system/mongod.service\n\n````\n> [Unit]\n> Description=mongodb database\n> \n> [Service]\n> User=mongod\n> Group=mongod\n> Environment=\"OPTIONS=--quiet -f /etc/mongod.conf\"\n> ExecStart=/usr/bin/mongod $OPTIONS run\n> PIDFile=/var/run/mongodb/mongod.pid\n> \n> [Install]\n> WantedBy=multi-user.target\n\n创建链接:\n````\nln -s /usr/lib/systemd/system/mongod.service /etc/systemd/system/multi-user.target.wants/\n````\n\n重新加载systemctl\n````\nsystemctl daemon-reload\n````\n#### 参考文档\n- [Centos7](https://docs.mongodb.com/master/tutorial/install-mongodb-on-red-hat/#to-use-non-default-directories)\n- [macOS](https://docs.mongodb.com/master/tutorial/install-mongodb-on-os-x/)\n- [Windows](https://docs.mongodb.com/master/tutorial/install-mongodb-on-windows/)\n\n\n\n","slug":"node-server-deploy","published":1,"updated":"2019-06-09T17:21:12.949Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cjwr0llvo0004bkvluwexs1gi","content":"<p>在过去几天的调研参考之后,拟定<em>Fork</em>Github相关的repositories,构建整站解决方案。</p>\n<ul>\n<li>后端服务基于<a href=\"https://github.com/bayuefen/node-elm\" target=\"_blank\" rel=\"noopener\">node-elm</a></li>\n<li>数据库采用<a href=\"https://www.mongodb.com/\" target=\"_blank\" rel=\"noopener\">MongoDB</a></li>\n<li>商户系统Web服务基于<a href=\"https://github.com/bayuefen/vue2-manage\" target=\"_blank\" rel=\"noopener\">vue2-manage</a></li>\n<li>用户系统Web服务基于<a href=\"https://github.com/bayuefen/vue2-elm\" target=\"_blank\" rel=\"noopener\">vue2-elm</a></li>\n</ul>\n<p>本文主要根据Node及MongoDB,在服务配置与部署过程中所遇到的一些问题的解决方案进行总结。</p>\n<a id=\"more\"></a>\n<h3 id=\"Node-Server-install\"><a href=\"#Node-Server-install\" class=\"headerlink\" title=\"Node Server install\"></a>Node Server install</h3><h4 id=\"安装依赖\"><a href=\"#安装依赖\" class=\"headerlink\" title=\"安装依赖\"></a>安装依赖</h4><p>1、node (6.0 及以上版本)<br>2、mongodb (开启状态)<br>3、GraphicsMagick (裁切图片)<br>4、yarn/npm(Node packages管理)<br>5、pm2(守护进程)</p>\n<h4 id=\"项目运行\"><a href=\"#项目运行\" class=\"headerlink\" title=\"项目运行\"></a>项目运行</h4><figure class=\"highlight crmsh\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">git <span class=\"keyword\">clone</span> <span class=\"title\">https</span>://github.com/bayuefen/<span class=\"keyword\">node</span><span class=\"title\">-elm</span> </span><br><span class=\"line\"></span><br><span class=\"line\">cd <span class=\"keyword\">node</span><span class=\"title\">-elm</span></span><br><span class=\"line\"></span><br><span class=\"line\">yarn install / npm install</span><br><span class=\"line\"></span><br><span class=\"line\">yarn run dev</span><br></pre></td></tr></table></figure>\n<p>访问: <code>http://localhost:8001</code>(如果已启动前台程序,则不需打开此地址)<br>本人将<code>node-elm</code>工程项目clone至阿里云ECS上,安装好上述环境依赖及node packages后,使用pm2守护进程<br><figure class=\"highlight gcode\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">p<span class=\"name\">m2</span> start <span class=\"string\">'yarn run dev'</span></span><br></pre></td></tr></table></figure></p>\n<h3 id=\"MongoDB-Community-Edition-install-rpm-Packages\"><a href=\"#MongoDB-Community-Edition-install-rpm-Packages\" class=\"headerlink\" title=\"MongoDB Community Edition install(.rpm Packages)\"></a>MongoDB Community Edition install(.rpm Packages)</h3><h4 id=\"安装流程\"><a href=\"#安装流程\" class=\"headerlink\" title=\"安装流程\"></a>安装流程</h4><p>1、创建<code>/etc/yum.repos.d/mongodb-org-4.2.repo</code>文件;<br>2、写入<code>yum</code>安装配置信息<br><figure class=\"highlight ini\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"section\">[mongodb-org-4.2]</span></span><br><span class=\"line\"><span class=\"attr\">name</span>=MongoDB Repository</span><br><span class=\"line\"><span class=\"attr\">baseurl</span>=https://repo.mongodb.org/yum/redhat/<span class=\"variable\">$releasever</span>/mongodb-org/<span class=\"number\">4.1</span>/x<span class=\"number\">86_64</span>/</span><br><span class=\"line\"><span class=\"attr\">gpgcheck</span>=<span class=\"number\">1</span></span><br><span class=\"line\"><span class=\"attr\">enabled</span>=<span class=\"number\">1</span></span><br><span class=\"line\"><span class=\"attr\">gpgkey</span>=https://www.mongodb.org/static/pgp/server-<span class=\"number\">4.2</span>.asc</span><br></pre></td></tr></table></figure></p>\n<p>3、安装MongoDB packages<br><figure class=\"highlight cmake\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">sudo yum <span class=\"keyword\">install</span> -y mongodb-org-unstable</span><br></pre></td></tr></table></figure></p>\n<p>4、!特定版本packages及组件安装<br><figure class=\"highlight css\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"selector-tag\">sudo</span> <span class=\"selector-tag\">yum</span> <span class=\"selector-tag\">install</span> <span class=\"selector-tag\">-y</span> <span class=\"selector-tag\">mongodb-org-unstable-4</span><span class=\"selector-class\">.1</span><span class=\"selector-class\">.10</span> <span class=\"selector-tag\">mongodb-org-unstable-server-4</span><span class=\"selector-class\">.1</span><span class=\"selector-class\">.10</span> <span class=\"selector-tag\">mongodb-org-unstable-shell-4</span><span class=\"selector-class\">.1</span><span class=\"selector-class\">.10</span> <span class=\"selector-tag\">mongodb-org-unstable-mongos-4</span><span class=\"selector-class\">.1</span><span class=\"selector-class\">.10</span> <span class=\"selector-tag\">mongodb-org-unstable-tools-4</span><span class=\"selector-class\">.1</span><span class=\"selector-class\">.10</span></span><br></pre></td></tr></table></figure></p>\n<h4 id=\"启用流程\"><a href=\"#启用流程\" class=\"headerlink\" title=\"启用流程\"></a>启用流程</h4><p>1、MongoDB默认文件创建</p>\n<ul>\n<li>/var/lib/mongo (the data directory)</li>\n<li>/var/log/mongodb (the log directory)</li>\n</ul>\n<p>2、启用MongoDB<br><figure class=\"highlight routeros\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">sudo<span class=\"built_in\"> service </span>mongod start</span><br></pre></td></tr></table></figure></p>\n<p>3、验证启用是否成功<br><code>27017</code>是默认端口,可以在<code>/etc/mongod.conf</code>中更改。<br><figure class=\"highlight applescript\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">sudo chkconfig mongod <span class=\"keyword\">on</span></span><br></pre></td></tr></table></figure></p>\n<p>4、关闭MongoDB<br><figure class=\"highlight routeros\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">sudo<span class=\"built_in\"> service </span>mongod stop</span><br></pre></td></tr></table></figure></p>\n<p>5、重启MongoDB<br><figure class=\"highlight routeros\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">sudo<span class=\"built_in\"> service </span>mongod restart</span><br></pre></td></tr></table></figure></p>\n<p>6、MongoDB使用<br><figure class=\"highlight ebnf\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"attribute\">mongo</span></span><br></pre></td></tr></table></figure></p>\n<h4 id=\"MongoDB权限配置\"><a href=\"#MongoDB权限配置\" class=\"headerlink\" title=\"MongoDB权限配置\"></a>MongoDB权限配置</h4><p>默认安装的MongoDB是不具备权限验证的,因为需要通过Navicat访问远程ECS上的DB服务,为了安全起见,给MongoDB配置相应的权限验证机制是非常必要的。</p>\n<h5 id=\"超级管理员创建\"><a href=\"#超级管理员创建\" class=\"headerlink\" title=\"超级管理员创建\"></a>超级管理员创建</h5><figure class=\"highlight sql\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\"># 启用mongoDB后使用</span></span><br><span class=\"line\">mongo</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\"># 切换至超级管理员模式</span></span><br><span class=\"line\"><span class=\"keyword\">use</span> <span class=\"keyword\">admin</span></span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\"># 创建超级管理员</span></span><br><span class=\"line\">db.createUser({<span class=\"keyword\">user</span>:<span class=\"string\">'userName'</span>, pwd:<span class=\"string\">'password'</span>, <span class=\"keyword\">roles</span>:[<span class=\"string\">'userAdminAnyDatabase'</span>]})</span><br></pre></td></tr></table></figure>\n<p>role类型:</p>\n<ul>\n<li>readAnyDatabase 任何数据库的只读权限(和read相似)</li>\n<li>readWriteAnyDatabase 任何数据库的读写权限(和readWrite相似)</li>\n<li>userAdminAnyDatabase 任何数据库用户的管理权限(和userAdmin相似)</li>\n<li>dbAdminAnyDatabase 任何数据库的管理权限(dbAdmin相似)</li>\n</ul>\n<h5 id=\"创建用户\"><a href=\"#创建用户\" class=\"headerlink\" title=\"创建用户\"></a>创建用户</h5><figure class=\"highlight sql\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\"># 进入超级管理员模式并验证</span></span><br><span class=\"line\"><span class=\"keyword\">use</span> <span class=\"keyword\">admin</span></span><br><span class=\"line\"></span><br><span class=\"line\">db.auth(<span class=\"string\">'userName'</span>,<span class=\"string\">'password'</span>)</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\"># 查看数据库</span></span><br><span class=\"line\"><span class=\"keyword\">show</span> dbs</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\"># 进入到elm数据库</span></span><br><span class=\"line\"><span class=\"keyword\">show</span> elm</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\"># 创建elm数据库用户</span></span><br><span class=\"line\">db.createUser({<span class=\"keyword\">user</span>:<span class=\"string\">'user-bayuefen'</span>,pwd:<span class=\"string\">'password'</span>,<span class=\"keyword\">roles</span>:[{<span class=\"keyword\">role</span>:<span class=\"string\">'readWrite'</span>,db:<span class=\"string\">'elm'</span>}]})</span><br></pre></td></tr></table></figure>\n<p>以上步骤核心完成超级管理员的创建验证及单个数据库的用户创建。<br>具备了以上的权限配置,我们即可通过navicat进行数据库的查看与管理;同时,在<code>Node</code>服务中我们可以使用<code>mongoose</code> package对数据库进行CURD</p>\n<h6 id=\"Node\"><a href=\"#Node\" class=\"headerlink\" title=\"Node\"></a>Node</h6><figure class=\"highlight coffeescript\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">import</span> mongoose <span class=\"keyword\">from</span> <span class=\"string\">'mongoose'</span>;</span><br><span class=\"line\">mongoose.connect(<span class=\"string\">'mongodb://user:pwd@localhost:27017/elm'</span>, {useMongoClient:<span class=\"literal\">true</span>});</span><br></pre></td></tr></table></figure>\n<h6 id=\"Navicat\"><a href=\"#Navicat\" class=\"headerlink\" title=\"Navicat\"></a>Navicat</h6><figure class=\"highlight routeros\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">server: ECS ip</span><br><span class=\"line\">port: 27017 (default)</span><br><span class=\"line\">user: user</span><br><span class=\"line\">pwd: password</span><br><span class=\"line\">db: db_name</span><br></pre></td></tr></table></figure>\n<p>ps:ECS需要通过阿里云后台服务打开安全策略中的27017端口的外网访问权限</p>\n<h4 id=\"问题记录\"><a href=\"#问题记录\" class=\"headerlink\" title=\"问题记录\"></a>问题记录</h4><p>Q:MongoDB在配置权限之后,重启Mongo,无法启动服务。抛<code>Unit mongod.service entered failed state.</code>异常<br>A:排查后,发现Centos需要将MongoDB添加至<code>systemd</code>中,否则会出现问题。<a href=\"https://github.com/jingxinxin/tiankeng/issues/5\" target=\"_blank\" rel=\"noopener\">Issues</a><br>添加<code>systemd</code><br><figure class=\"highlight crystal\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">vim /usr/<span class=\"class\"><span class=\"keyword\">lib</span>/<span class=\"title\">systemd</span>/<span class=\"title\">system</span>/<span class=\"title\">mongod</span>.<span class=\"title\">service</span></span></span><br></pre></td></tr></table></figure></p>\n<blockquote>\n<p>[Unit]<br>Description=mongodb database</p>\n<p>[Service]<br>User=mongod<br>Group=mongod<br>Environment=”OPTIONS=–quiet -f /etc/mongod.conf”<br>ExecStart=/usr/bin/mongod $OPTIONS run<br>PIDFile=/var/run/mongodb/mongod.pid</p>\n<p>[Install]<br>WantedBy=multi-user.target</p>\n</blockquote>\n<p>创建链接:<br><figure class=\"highlight crystal\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">ln -s /usr/<span class=\"class\"><span class=\"keyword\">lib</span>/<span class=\"title\">systemd</span>/<span class=\"title\">system</span>/<span class=\"title\">mongod</span>.<span class=\"title\">service</span> /<span class=\"title\">etc</span>/<span class=\"title\">systemd</span>/<span class=\"title\">system</span>/<span class=\"title\">multi</span>-<span class=\"title\">user</span>.<span class=\"title\">target</span>.<span class=\"title\">wants</span>/</span></span><br></pre></td></tr></table></figure></p>\n<p>重新加载systemctl<br><figure class=\"highlight ebnf\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"attribute\">systemctl daemon-reload</span></span><br></pre></td></tr></table></figure></p>\n<h4 id=\"参考文档\"><a href=\"#参考文档\" class=\"headerlink\" title=\"参考文档\"></a>参考文档</h4><ul>\n<li><a href=\"https://docs.mongodb.com/master/tutorial/install-mongodb-on-red-hat/#to-use-non-default-directories\" target=\"_blank\" rel=\"noopener\">Centos7</a></li>\n<li><a href=\"https://docs.mongodb.com/master/tutorial/install-mongodb-on-os-x/\" target=\"_blank\" rel=\"noopener\">macOS</a></li>\n<li><a href=\"https://docs.mongodb.com/master/tutorial/install-mongodb-on-windows/\" target=\"_blank\" rel=\"noopener\">Windows</a></li>\n</ul>\n","site":{"data":{}},"excerpt":"<p>在过去几天的调研参考之后,拟定<em>Fork</em>Github相关的repositories,构建整站解决方案。</p>\n<ul>\n<li>后端服务基于<a href=\"https://github.com/bayuefen/node-elm\" target=\"_blank\" rel=\"noopener\">node-elm</a></li>\n<li>数据库采用<a href=\"https://www.mongodb.com/\" target=\"_blank\" rel=\"noopener\">MongoDB</a></li>\n<li>商户系统Web服务基于<a href=\"https://github.com/bayuefen/vue2-manage\" target=\"_blank\" rel=\"noopener\">vue2-manage</a></li>\n<li>用户系统Web服务基于<a href=\"https://github.com/bayuefen/vue2-elm\" target=\"_blank\" rel=\"noopener\">vue2-elm</a></li>\n</ul>\n<p>本文主要根据Node及MongoDB,在服务配置与部署过程中所遇到的一些问题的解决方案进行总结。</p>","more":"<h3 id=\"Node-Server-install\"><a href=\"#Node-Server-install\" class=\"headerlink\" title=\"Node Server install\"></a>Node Server install</h3><h4 id=\"安装依赖\"><a href=\"#安装依赖\" class=\"headerlink\" title=\"安装依赖\"></a>安装依赖</h4><p>1、node (6.0 及以上版本)<br>2、mongodb (开启状态)<br>3、GraphicsMagick (裁切图片)<br>4、yarn/npm(Node packages管理)<br>5、pm2(守护进程)</p>\n<h4 id=\"项目运行\"><a href=\"#项目运行\" class=\"headerlink\" title=\"项目运行\"></a>项目运行</h4><figure class=\"highlight crmsh\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">git <span class=\"keyword\">clone</span> <span class=\"title\">https</span>://github.com/bayuefen/<span class=\"keyword\">node</span><span class=\"title\">-elm</span> </span><br><span class=\"line\"></span><br><span class=\"line\">cd <span class=\"keyword\">node</span><span class=\"title\">-elm</span></span><br><span class=\"line\"></span><br><span class=\"line\">yarn install / npm install</span><br><span class=\"line\"></span><br><span class=\"line\">yarn run dev</span><br></pre></td></tr></table></figure>\n<p>访问: <code>http://localhost:8001</code>(如果已启动前台程序,则不需打开此地址)<br>本人将<code>node-elm</code>工程项目clone至阿里云ECS上,安装好上述环境依赖及node packages后,使用pm2守护进程<br><figure class=\"highlight gcode\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">p<span class=\"name\">m2</span> start <span class=\"string\">'yarn run dev'</span></span><br></pre></td></tr></table></figure></p>\n<h3 id=\"MongoDB-Community-Edition-install-rpm-Packages\"><a href=\"#MongoDB-Community-Edition-install-rpm-Packages\" class=\"headerlink\" title=\"MongoDB Community Edition install(.rpm Packages)\"></a>MongoDB Community Edition install(.rpm Packages)</h3><h4 id=\"安装流程\"><a href=\"#安装流程\" class=\"headerlink\" title=\"安装流程\"></a>安装流程</h4><p>1、创建<code>/etc/yum.repos.d/mongodb-org-4.2.repo</code>文件;<br>2、写入<code>yum</code>安装配置信息<br><figure class=\"highlight ini\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"section\">[mongodb-org-4.2]</span></span><br><span class=\"line\"><span class=\"attr\">name</span>=MongoDB Repository</span><br><span class=\"line\"><span class=\"attr\">baseurl</span>=https://repo.mongodb.org/yum/redhat/<span class=\"variable\">$releasever</span>/mongodb-org/<span class=\"number\">4.1</span>/x<span class=\"number\">86_64</span>/</span><br><span class=\"line\"><span class=\"attr\">gpgcheck</span>=<span class=\"number\">1</span></span><br><span class=\"line\"><span class=\"attr\">enabled</span>=<span class=\"number\">1</span></span><br><span class=\"line\"><span class=\"attr\">gpgkey</span>=https://www.mongodb.org/static/pgp/server-<span class=\"number\">4.2</span>.asc</span><br></pre></td></tr></table></figure></p>\n<p>3、安装MongoDB packages<br><figure class=\"highlight cmake\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">sudo yum <span class=\"keyword\">install</span> -y mongodb-org-unstable</span><br></pre></td></tr></table></figure></p>\n<p>4、!特定版本packages及组件安装<br><figure class=\"highlight css\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"selector-tag\">sudo</span> <span class=\"selector-tag\">yum</span> <span class=\"selector-tag\">install</span> <span class=\"selector-tag\">-y</span> <span class=\"selector-tag\">mongodb-org-unstable-4</span><span class=\"selector-class\">.1</span><span class=\"selector-class\">.10</span> <span class=\"selector-tag\">mongodb-org-unstable-server-4</span><span class=\"selector-class\">.1</span><span class=\"selector-class\">.10</span> <span class=\"selector-tag\">mongodb-org-unstable-shell-4</span><span class=\"selector-class\">.1</span><span class=\"selector-class\">.10</span> <span class=\"selector-tag\">mongodb-org-unstable-mongos-4</span><span class=\"selector-class\">.1</span><span class=\"selector-class\">.10</span> <span class=\"selector-tag\">mongodb-org-unstable-tools-4</span><span class=\"selector-class\">.1</span><span class=\"selector-class\">.10</span></span><br></pre></td></tr></table></figure></p>\n<h4 id=\"启用流程\"><a href=\"#启用流程\" class=\"headerlink\" title=\"启用流程\"></a>启用流程</h4><p>1、MongoDB默认文件创建</p>\n<ul>\n<li>/var/lib/mongo (the data directory)</li>\n<li>/var/log/mongodb (the log directory)</li>\n</ul>\n<p>2、启用MongoDB<br><figure class=\"highlight routeros\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">sudo<span class=\"built_in\"> service </span>mongod start</span><br></pre></td></tr></table></figure></p>\n<p>3、验证启用是否成功<br><code>27017</code>是默认端口,可以在<code>/etc/mongod.conf</code>中更改。<br><figure class=\"highlight applescript\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">sudo chkconfig mongod <span class=\"keyword\">on</span></span><br></pre></td></tr></table></figure></p>\n<p>4、关闭MongoDB<br><figure class=\"highlight routeros\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">sudo<span class=\"built_in\"> service </span>mongod stop</span><br></pre></td></tr></table></figure></p>\n<p>5、重启MongoDB<br><figure class=\"highlight routeros\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">sudo<span class=\"built_in\"> service </span>mongod restart</span><br></pre></td></tr></table></figure></p>\n<p>6、MongoDB使用<br><figure class=\"highlight ebnf\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"attribute\">mongo</span></span><br></pre></td></tr></table></figure></p>\n<h4 id=\"MongoDB权限配置\"><a href=\"#MongoDB权限配置\" class=\"headerlink\" title=\"MongoDB权限配置\"></a>MongoDB权限配置</h4><p>默认安装的MongoDB是不具备权限验证的,因为需要通过Navicat访问远程ECS上的DB服务,为了安全起见,给MongoDB配置相应的权限验证机制是非常必要的。</p>\n<h5 id=\"超级管理员创建\"><a href=\"#超级管理员创建\" class=\"headerlink\" title=\"超级管理员创建\"></a>超级管理员创建</h5><figure class=\"highlight sql\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\"># 启用mongoDB后使用</span></span><br><span class=\"line\">mongo</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\"># 切换至超级管理员模式</span></span><br><span class=\"line\"><span class=\"keyword\">use</span> <span class=\"keyword\">admin</span></span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\"># 创建超级管理员</span></span><br><span class=\"line\">db.createUser({<span class=\"keyword\">user</span>:<span class=\"string\">'userName'</span>, pwd:<span class=\"string\">'password'</span>, <span class=\"keyword\">roles</span>:[<span class=\"string\">'userAdminAnyDatabase'</span>]})</span><br></pre></td></tr></table></figure>\n<p>role类型:</p>\n<ul>\n<li>readAnyDatabase 任何数据库的只读权限(和read相似)</li>\n<li>readWriteAnyDatabase 任何数据库的读写权限(和readWrite相似)</li>\n<li>userAdminAnyDatabase 任何数据库用户的管理权限(和userAdmin相似)</li>\n<li>dbAdminAnyDatabase 任何数据库的管理权限(dbAdmin相似)</li>\n</ul>\n<h5 id=\"创建用户\"><a href=\"#创建用户\" class=\"headerlink\" title=\"创建用户\"></a>创建用户</h5><figure class=\"highlight sql\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\"># 进入超级管理员模式并验证</span></span><br><span class=\"line\"><span class=\"keyword\">use</span> <span class=\"keyword\">admin</span></span><br><span class=\"line\"></span><br><span class=\"line\">db.auth(<span class=\"string\">'userName'</span>,<span class=\"string\">'password'</span>)</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\"># 查看数据库</span></span><br><span class=\"line\"><span class=\"keyword\">show</span> dbs</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\"># 进入到elm数据库</span></span><br><span class=\"line\"><span class=\"keyword\">show</span> elm</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\"># 创建elm数据库用户</span></span><br><span class=\"line\">db.createUser({<span class=\"keyword\">user</span>:<span class=\"string\">'user-bayuefen'</span>,pwd:<span class=\"string\">'password'</span>,<span class=\"keyword\">roles</span>:[{<span class=\"keyword\">role</span>:<span class=\"string\">'readWrite'</span>,db:<span class=\"string\">'elm'</span>}]})</span><br></pre></td></tr></table></figure>\n<p>以上步骤核心完成超级管理员的创建验证及单个数据库的用户创建。<br>具备了以上的权限配置,我们即可通过navicat进行数据库的查看与管理;同时,在<code>Node</code>服务中我们可以使用<code>mongoose</code> package对数据库进行CURD</p>\n<h6 id=\"Node\"><a href=\"#Node\" class=\"headerlink\" title=\"Node\"></a>Node</h6><figure class=\"highlight coffeescript\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">import</span> mongoose <span class=\"keyword\">from</span> <span class=\"string\">'mongoose'</span>;</span><br><span class=\"line\">mongoose.connect(<span class=\"string\">'mongodb://user:pwd@localhost:27017/elm'</span>, {useMongoClient:<span class=\"literal\">true</span>});</span><br></pre></td></tr></table></figure>\n<h6 id=\"Navicat\"><a href=\"#Navicat\" class=\"headerlink\" title=\"Navicat\"></a>Navicat</h6><figure class=\"highlight routeros\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">server: ECS ip</span><br><span class=\"line\">port: 27017 (default)</span><br><span class=\"line\">user: user</span><br><span class=\"line\">pwd: password</span><br><span class=\"line\">db: db_name</span><br></pre></td></tr></table></figure>\n<p>ps:ECS需要通过阿里云后台服务打开安全策略中的27017端口的外网访问权限</p>\n<h4 id=\"问题记录\"><a href=\"#问题记录\" class=\"headerlink\" title=\"问题记录\"></a>问题记录</h4><p>Q:MongoDB在配置权限之后,重启Mongo,无法启动服务。抛<code>Unit mongod.service entered failed state.</code>异常<br>A:排查后,发现Centos需要将MongoDB添加至<code>systemd</code>中,否则会出现问题。<a href=\"https://github.com/jingxinxin/tiankeng/issues/5\" target=\"_blank\" rel=\"noopener\">Issues</a><br>添加<code>systemd</code><br><figure class=\"highlight crystal\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">vim /usr/<span class=\"class\"><span class=\"keyword\">lib</span>/<span class=\"title\">systemd</span>/<span class=\"title\">system</span>/<span class=\"title\">mongod</span>.<span class=\"title\">service</span></span></span><br></pre></td></tr></table></figure></p>\n<blockquote>\n<p>[Unit]<br>Description=mongodb database</p>\n<p>[Service]<br>User=mongod<br>Group=mongod<br>Environment=”OPTIONS=–quiet -f /etc/mongod.conf”<br>ExecStart=/usr/bin/mongod $OPTIONS run<br>PIDFile=/var/run/mongodb/mongod.pid</p>\n<p>[Install]<br>WantedBy=multi-user.target</p>\n</blockquote>\n<p>创建链接:<br><figure class=\"highlight crystal\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">ln -s /usr/<span class=\"class\"><span class=\"keyword\">lib</span>/<span class=\"title\">systemd</span>/<span class=\"title\">system</span>/<span class=\"title\">mongod</span>.<span class=\"title\">service</span> /<span class=\"title\">etc</span>/<span class=\"title\">systemd</span>/<span class=\"title\">system</span>/<span class=\"title\">multi</span>-<span class=\"title\">user</span>.<span class=\"title\">target</span>.<span class=\"title\">wants</span>/</span></span><br></pre></td></tr></table></figure></p>\n<p>重新加载systemctl<br><figure class=\"highlight ebnf\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"attribute\">systemctl daemon-reload</span></span><br></pre></td></tr></table></figure></p>\n<h4 id=\"参考文档\"><a href=\"#参考文档\" class=\"headerlink\" title=\"参考文档\"></a>参考文档</h4><ul>\n<li><a href=\"https://docs.mongodb.com/master/tutorial/install-mongodb-on-red-hat/#to-use-non-default-directories\" target=\"_blank\" rel=\"noopener\">Centos7</a></li>\n<li><a href=\"https://docs.mongodb.com/master/tutorial/install-mongodb-on-os-x/\" target=\"_blank\" rel=\"noopener\">macOS</a></li>\n<li><a href=\"https://docs.mongodb.com/master/tutorial/install-mongodb-on-windows/\" target=\"_blank\" rel=\"noopener\">Windows</a></li>\n</ul>"},{"title":"【Python】标准库unittest基本应用","date":"2019-04-17T09:36:29.000Z","_content":"\n[`unittest`](https://docs.python.org/3.7/library/unittest.html)的入门基本应用记录。\n\n概念\n`unittest`是`python`自带的标准库之一,是`python`版的`junit`,主要用于维护执行自动化测试框架的用例。\n\n<!-- more -->\n\n## 核心工作原理\n核心应有有:`TestCase` `TestSuite` `TestLoader` `TestRunner` `TestFixture` 等构造函数。\n\n- `TestCase`: 测试用例。一个完整的测试单元包括 测试前准备环境的搭建(`setUp`),执行测试代码(`run`),以及测试后环境的还原(`tearDown`)。一系列具有完整测试流程的测试单元组成测试用例。\n- `TestSuite`: 测试用例合集。多个`TestCase`集合组成`TestSuite`。且`TestSuite`可以嵌套其他`TestSuite`。\n- `TestLoader`: 测试用例装载器。主要用于加载`TestCase`值`TestSuite`。通过`loadTestsFrom__()`方法加载`TestCase`,并创建其实例,然后将该实例添加至`TestSuite`中,返回`TestSuite`实例。\n- `TestRunner`: 测试用例执行器。执行器中的`run(test)`方法会自动执行`TestCase`、`TestSuite`中的`run(result)`方法,并将执行的测试结果保存至`TextTestResult`实例中,用于统计测试用例执行结果合计。\n- `TestFixture`: 测试环境构建与销毁维护。在测试用例运行初始化前使用`setUp()`准备初始环境,在测试用例执行完毕后使用`tearDown()`执行销毁,还原运行测试环境。\n\n\n参考链接\n\nhttps://blog.csdn.net/luanpeng825485697/article/details/79459771\n\n","source":"_posts/python-lib-unittest.md","raw":"---\ntitle: 【Python】标准库unittest基本应用\ndate: 2019-04-17 17:36:29\ntags:\n - Python\n - unittest\n - 日记\n---\n\n[`unittest`](https://docs.python.org/3.7/library/unittest.html)的入门基本应用记录。\n\n概念\n`unittest`是`python`自带的标准库之一,是`python`版的`junit`,主要用于维护执行自动化测试框架的用例。\n\n<!-- more -->\n\n## 核心工作原理\n核心应有有:`TestCase` `TestSuite` `TestLoader` `TestRunner` `TestFixture` 等构造函数。\n\n- `TestCase`: 测试用例。一个完整的测试单元包括 测试前准备环境的搭建(`setUp`),执行测试代码(`run`),以及测试后环境的还原(`tearDown`)。一系列具有完整测试流程的测试单元组成测试用例。\n- `TestSuite`: 测试用例合集。多个`TestCase`集合组成`TestSuite`。且`TestSuite`可以嵌套其他`TestSuite`。\n- `TestLoader`: 测试用例装载器。主要用于加载`TestCase`值`TestSuite`。通过`loadTestsFrom__()`方法加载`TestCase`,并创建其实例,然后将该实例添加至`TestSuite`中,返回`TestSuite`实例。\n- `TestRunner`: 测试用例执行器。执行器中的`run(test)`方法会自动执行`TestCase`、`TestSuite`中的`run(result)`方法,并将执行的测试结果保存至`TextTestResult`实例中,用于统计测试用例执行结果合计。\n- `TestFixture`: 测试环境构建与销毁维护。在测试用例运行初始化前使用`setUp()`准备初始环境,在测试用例执行完毕后使用`tearDown()`执行销毁,还原运行测试环境。\n\n\n参考链接\n\nhttps://blog.csdn.net/luanpeng825485697/article/details/79459771\n\n","slug":"python-lib-unittest","published":1,"updated":"2019-06-09T17:21:12.950Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cjwr0llvp0005bkvlbktmg3ve","content":"<p><a href=\"https://docs.python.org/3.7/library/unittest.html\" target=\"_blank\" rel=\"noopener\"><code>unittest</code></a>的入门基本应用记录。</p>\n<p>概念<br><code>unittest</code>是<code>python</code>自带的标准库之一,是<code>python</code>版的<code>junit</code>,主要用于维护执行自动化测试框架的用例。</p>\n<a id=\"more\"></a>\n<h2 id=\"核心工作原理\"><a href=\"#核心工作原理\" class=\"headerlink\" title=\"核心工作原理\"></a>核心工作原理</h2><p>核心应有有:<code>TestCase</code> <code>TestSuite</code> <code>TestLoader</code> <code>TestRunner</code> <code>TestFixture</code> 等构造函数。</p>\n<ul>\n<li><code>TestCase</code>: 测试用例。一个完整的测试单元包括 测试前准备环境的搭建(<code>setUp</code>),执行测试代码(<code>run</code>),以及测试后环境的还原(<code>tearDown</code>)。一系列具有完整测试流程的测试单元组成测试用例。</li>\n<li><code>TestSuite</code>: 测试用例合集。多个<code>TestCase</code>集合组成<code>TestSuite</code>。且<code>TestSuite</code>可以嵌套其他<code>TestSuite</code>。</li>\n<li><code>TestLoader</code>: 测试用例装载器。主要用于加载<code>TestCase</code>值<code>TestSuite</code>。通过<code>loadTestsFrom__()</code>方法加载<code>TestCase</code>,并创建其实例,然后将该实例添加至<code>TestSuite</code>中,返回<code>TestSuite</code>实例。</li>\n<li><code>TestRunner</code>: 测试用例执行器。执行器中的<code>run(test)</code>方法会自动执行<code>TestCase</code>、<code>TestSuite</code>中的<code>run(result)</code>方法,并将执行的测试结果保存至<code>TextTestResult</code>实例中,用于统计测试用例执行结果合计。</li>\n<li><code>TestFixture</code>: 测试环境构建与销毁维护。在测试用例运行初始化前使用<code>setUp()</code>准备初始环境,在测试用例执行完毕后使用<code>tearDown()</code>执行销毁,还原运行测试环境。</li>\n</ul>\n<p>参考链接</p>\n<p><a href=\"https://blog.csdn.net/luanpeng825485697/article/details/79459771\" target=\"_blank\" rel=\"noopener\">https://blog.csdn.net/luanpeng825485697/article/details/79459771</a></p>\n","site":{"data":{}},"excerpt":"<p><a href=\"https://docs.python.org/3.7/library/unittest.html\" target=\"_blank\" rel=\"noopener\"><code>unittest</code></a>的入门基本应用记录。</p>\n<p>概念<br><code>unittest</code>是<code>python</code>自带的标准库之一,是<code>python</code>版的<code>junit</code>,主要用于维护执行自动化测试框架的用例。</p>","more":"<h2 id=\"核心工作原理\"><a href=\"#核心工作原理\" class=\"headerlink\" title=\"核心工作原理\"></a>核心工作原理</h2><p>核心应有有:<code>TestCase</code> <code>TestSuite</code> <code>TestLoader</code> <code>TestRunner</code> <code>TestFixture</code> 等构造函数。</p>\n<ul>\n<li><code>TestCase</code>: 测试用例。一个完整的测试单元包括 测试前准备环境的搭建(<code>setUp</code>),执行测试代码(<code>run</code>),以及测试后环境的还原(<code>tearDown</code>)。一系列具有完整测试流程的测试单元组成测试用例。</li>\n<li><code>TestSuite</code>: 测试用例合集。多个<code>TestCase</code>集合组成<code>TestSuite</code>。且<code>TestSuite</code>可以嵌套其他<code>TestSuite</code>。</li>\n<li><code>TestLoader</code>: 测试用例装载器。主要用于加载<code>TestCase</code>值<code>TestSuite</code>。通过<code>loadTestsFrom__()</code>方法加载<code>TestCase</code>,并创建其实例,然后将该实例添加至<code>TestSuite</code>中,返回<code>TestSuite</code>实例。</li>\n<li><code>TestRunner</code>: 测试用例执行器。执行器中的<code>run(test)</code>方法会自动执行<code>TestCase</code>、<code>TestSuite</code>中的<code>run(result)</code>方法,并将执行的测试结果保存至<code>TextTestResult</code>实例中,用于统计测试用例执行结果合计。</li>\n<li><code>TestFixture</code>: 测试环境构建与销毁维护。在测试用例运行初始化前使用<code>setUp()</code>准备初始环境,在测试用例执行完毕后使用<code>tearDown()</code>执行销毁,还原运行测试环境。</li>\n</ul>\n<p>参考链接</p>\n<p><a href=\"https://blog.csdn.net/luanpeng825485697/article/details/79459771\" target=\"_blank\" rel=\"noopener\">https://blog.csdn.net/luanpeng825485697/article/details/79459771</a></p>"},{"title":"【Python】基础教程学习记录","date":"2019-04-23T08:35:57.000Z","_content":"本文核心记录在`Python`基础知识与进阶教程学习过程中的一些零碎知识记录,主要用于基础知识的巩固,以便后续使用`Python`过程遇到疑问参照。\n<!-- more -->\n\n## \n","source":"_posts/python-tutorial-record.md","raw":"---\ntitle: 【Python】基础教程学习记录\ndate: 2019-04-23 16:35:57\ntags:\n - Python\n - 基础知识\n - 日记\n---\n本文核心记录在`Python`基础知识与进阶教程学习过程中的一些零碎知识记录,主要用于基础知识的巩固,以便后续使用`Python`过程遇到疑问参照。\n<!-- more -->\n\n## \n","slug":"python-tutorial-record","published":1,"updated":"2019-06-09T17:21:12.952Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cjwr0llvq0007bkvlxrdbmenp","content":"<p>本文核心记录在<code>Python</code>基础知识与进阶教程学习过程中的一些零碎知识记录,主要用于基础知识的巩固,以便后续使用<code>Python</code>过程遇到疑问参照。<br><a id=\"more\"></a></p>\n<p>## </p>\n","site":{"data":{}},"excerpt":"<p>本文核心记录在<code>Python</code>基础知识与进阶教程学习过程中的一些零碎知识记录,主要用于基础知识的巩固,以便后续使用<code>Python</code>过程遇到疑问参照。<br>","more":"</p>\n<p>## </p>"},{"title":"【Python】三方模块requests入门应用","date":"2019-04-17T05:47:08.000Z","_content":"本文基于`python3.7`及第三方模块`requests`编写入门级简易模式的HTTP请求应用于详解。\n`requests`是由python编写,基于urllib, 采用Apache2 Licensed开源协议的HTTP三方模块。详情应用见[官网](http://docs.python-requests.org/zh_CN/latest/index.html)\n\n<!-- more -->\n\n## 安装\n> 1. 如果已安装Anaconda,默认是含`requests`模块。\n> 2. 终端命令行安装,[详情](http://docs.python-requests.org/zh_CN/latest/user/install.html#install)\n ````\n pip install requests\n ````\n \n## 应用\n\n\n````\nimport requests\nimport logging\nimport json\nfrom requests.auth import AuthBase\n\ndomain = 'http://httpbin.org'\n````\n\n#### GET\n````\nres = requests.get(domain + '/get', params={'aa': 1, 'bb': 2})\nprint(res.status_code, res.text)\nprint(res.url, res.json())\n````\n\n#### POST\n````\nres = requests.post(domain + '/post', params={'aa': 11, 'bb': 22}, data={'username': 'bayuefen', 'password': '123456'})\nprint(res.status_code, res.text)\nprint(res.url, res.json())\n````\n\n#### PATCH\n````\nres = requests.patch(domain + '/patch', data={'username':'bayuefen','password':'123456','visibility':'private'})\nprint(res.status_code, res.text)\nprint(res.headers)\n````\n\n#### DELETE\n````\nres = requests.delete(domain + '/delete')\nprint(res.status_code, res.text)\nprint(res.headers)\n````\n\n#### Session (⭐️⭐️⭐️)\n````\nrs = requests.Session()\nrs.get(domain + '/cookies/set/username/bayuefen')\nres = rs.get(domain + '/cookies')\nprint(res.text)\n````\n> 1.Session对象能够跨请求保持某些特定参数,并且同一个Session实例发出的所有请求之间保持cookie\n> 2.对于同一个服务器发送多个请求,底层TCP连接可被重用,有显著的性能提升\n\n#### Cookie (⭐️⭐️⭐️)\n````\ncookies = dict(admin_token='xxxxxxxxx')\nres = requests.get(domain + '/cookies/set', cookies=cookies)\nprint(res.text)\n````\n\n````\njar = requests.cookies.RequestsCookieJar()\njar.set('bayuefen_cookies', '123456', domain='httpbin.org', path='/cookies')\njar.set('bayuefen_values', 'my_values', domain='httpbin.org', path='/elsewhere')\nres1 = requests.get(domain + '/cookies', cookies=jar)\nprint(res1.text)\n````\n> Cookie的返回对象是RequestsCookieJar,这种模式适用于跨域名跨路径使用\n\n#### redirect\n````\nres = requests.get(domain + '/redirect-to', params={'url':'https://www.baidu.com', 'status_code':302})\nprint(res.url, res.status_code)\nprint(res.history)\n````\n> 可配置allow_redirects = False 禁用重定向\n\n#### timeout\n````\nres = requests.get(domain + '/get', timeout=0.001)\nprint(res.raise_for_status())\n````\n> 超时的异常触发仅仅针对连接过程,与相应体的下载无关,即请求发起至服务器相应的最大时间,超过则以异常处理\n\n#### file upload\n````\nfile = {'txtFile': open('./test.txt', 'rb')}\nupload_data = {\"parentId\": \"\", \"fileCategory\": \"personal\", \"fileSize\": 179, \"fileName\": \"test.txt\", \"uoType\": 1}\nres = requests.post(domain + '/post', upload_data, files=file)\nprint(res.url, res.status_code)\nprint(res.json())\n````\n> 超大文件的传输使用multipart/form-data,默认情况下requests是不支持的,需要使用requests-toolbelt\n\n\n#### file download\n````\nres = requests.get(domain + '/image/jpeg', stream=True)\nprint(res.raw, res.raw.read(10))\nwith open('./test.jpeg', 'wb') as f:\n f.write(res.content)\n````\n> notice: 下载原始相应内容(images/xlsx/pdf等),通过设置stream获取raw\n#### JSON解码器\n````\nres = requests.post(domain + '/post', data={'aa': 11, 'bb': 2})\nprint(res.raise_for_status(), res.status_code)\ntry:\n res.json()\nexcept ValueError:\n logging.error('No JSON object could be decoded')\n````\n> JSON被解码返回成功,不一定代表HTTP相应成功;检查请求响应是否成功,需通过`response.raise_for_status()` or `response.status_code`去判别\n\n\n#### 定制 headers\n````\nheaders = {\n 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'\n}\nres = requests.get(domain + '/get', headers=headers)\nprint(res.json())\n# 响应头\nprint(res.headers.get('content-type'))\n````\n> 定制headers低于特定场景的信息源\n> 1. 如果在 .netrc 中设置了用户认证信息,使用 headers= 设置的授权就不会生效。而如果设置了 auth= 参数,``.netrc`` 的设置就无效了。\n> 2. 如果被重定向到别的主机,授权 header 就会被删除。\n> 3. 代理授权 header 会被 URL 中提供的代理身份覆盖掉。\n> 4. 在我们能判断内容长度的情况下,header 的 Content-Length 会被改写。\n\n#### SSL\n````\nrequests.get('https://github.com', verify=True)\n````\n\n#### 流式请求\n````\nres = requests.get(domain + '/stream/20', stream=True)\nprint(res.raw)\nif res.encoding is None:\n res.encoding = 'utf-8'\nfor line in res.iter_lines():\n if line:\n decoded_line = line.decode('utf-8')\n print(json.loads(decoded_line))\n````\n\n#### Auth HTTP基本身份认证\n````\nauth = requests.auth.HTTPBasicAuth('user', 'passwd')\nres = requests.get(domain + '/hidden-basic-auth/user/passwd', auth=auth)\nprint(res.json())\n````\n\n## 参考资料\n1.[Requests: 让 HTTP 服务人类](http://docs.python-requests.org/zh_CN/latest/index.html#)\n2.[httpbin](http://httpbin.org/)\n3.[code](https://github.com/bayuefen/demos/blob/master/requestDemo/test.py)","source":"_posts/python-module-requests.md","raw":"---\ntitle: 【Python】三方模块requests入门应用\ndate: 2019-04-17 13:47:08\ntags:\n - Python\n - requests\n - 日记\n---\n本文基于`python3.7`及第三方模块`requests`编写入门级简易模式的HTTP请求应用于详解。\n`requests`是由python编写,基于urllib, 采用Apache2 Licensed开源协议的HTTP三方模块。详情应用见[官网](http://docs.python-requests.org/zh_CN/latest/index.html)\n\n<!-- more -->\n\n## 安装\n> 1. 如果已安装Anaconda,默认是含`requests`模块。\n> 2. 终端命令行安装,[详情](http://docs.python-requests.org/zh_CN/latest/user/install.html#install)\n ````\n pip install requests\n ````\n \n## 应用\n\n\n````\nimport requests\nimport logging\nimport json\nfrom requests.auth import AuthBase\n\ndomain = 'http://httpbin.org'\n````\n\n#### GET\n````\nres = requests.get(domain + '/get', params={'aa': 1, 'bb': 2})\nprint(res.status_code, res.text)\nprint(res.url, res.json())\n````\n\n#### POST\n````\nres = requests.post(domain + '/post', params={'aa': 11, 'bb': 22}, data={'username': 'bayuefen', 'password': '123456'})\nprint(res.status_code, res.text)\nprint(res.url, res.json())\n````\n\n#### PATCH\n````\nres = requests.patch(domain + '/patch', data={'username':'bayuefen','password':'123456','visibility':'private'})\nprint(res.status_code, res.text)\nprint(res.headers)\n````\n\n#### DELETE\n````\nres = requests.delete(domain + '/delete')\nprint(res.status_code, res.text)\nprint(res.headers)\n````\n\n#### Session (⭐️⭐️⭐️)\n````\nrs = requests.Session()\nrs.get(domain + '/cookies/set/username/bayuefen')\nres = rs.get(domain + '/cookies')\nprint(res.text)\n````\n> 1.Session对象能够跨请求保持某些特定参数,并且同一个Session实例发出的所有请求之间保持cookie\n> 2.对于同一个服务器发送多个请求,底层TCP连接可被重用,有显著的性能提升\n\n#### Cookie (⭐️⭐️⭐️)\n````\ncookies = dict(admin_token='xxxxxxxxx')\nres = requests.get(domain + '/cookies/set', cookies=cookies)\nprint(res.text)\n````\n\n````\njar = requests.cookies.RequestsCookieJar()\njar.set('bayuefen_cookies', '123456', domain='httpbin.org', path='/cookies')\njar.set('bayuefen_values', 'my_values', domain='httpbin.org', path='/elsewhere')\nres1 = requests.get(domain + '/cookies', cookies=jar)\nprint(res1.text)\n````\n> Cookie的返回对象是RequestsCookieJar,这种模式适用于跨域名跨路径使用\n\n#### redirect\n````\nres = requests.get(domain + '/redirect-to', params={'url':'https://www.baidu.com', 'status_code':302})\nprint(res.url, res.status_code)\nprint(res.history)\n````\n> 可配置allow_redirects = False 禁用重定向\n\n#### timeout\n````\nres = requests.get(domain + '/get', timeout=0.001)\nprint(res.raise_for_status())\n````\n> 超时的异常触发仅仅针对连接过程,与相应体的下载无关,即请求发起至服务器相应的最大时间,超过则以异常处理\n\n#### file upload\n````\nfile = {'txtFile': open('./test.txt', 'rb')}\nupload_data = {\"parentId\": \"\", \"fileCategory\": \"personal\", \"fileSize\": 179, \"fileName\": \"test.txt\", \"uoType\": 1}\nres = requests.post(domain + '/post', upload_data, files=file)\nprint(res.url, res.status_code)\nprint(res.json())\n````\n> 超大文件的传输使用multipart/form-data,默认情况下requests是不支持的,需要使用requests-toolbelt\n\n\n#### file download\n````\nres = requests.get(domain + '/image/jpeg', stream=True)\nprint(res.raw, res.raw.read(10))\nwith open('./test.jpeg', 'wb') as f:\n f.write(res.content)\n````\n> notice: 下载原始相应内容(images/xlsx/pdf等),通过设置stream获取raw\n#### JSON解码器\n````\nres = requests.post(domain + '/post', data={'aa': 11, 'bb': 2})\nprint(res.raise_for_status(), res.status_code)\ntry:\n res.json()\nexcept ValueError:\n logging.error('No JSON object could be decoded')\n````\n> JSON被解码返回成功,不一定代表HTTP相应成功;检查请求响应是否成功,需通过`response.raise_for_status()` or `response.status_code`去判别\n\n\n#### 定制 headers\n````\nheaders = {\n 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'\n}\nres = requests.get(domain + '/get', headers=headers)\nprint(res.json())\n# 响应头\nprint(res.headers.get('content-type'))\n````\n> 定制headers低于特定场景的信息源\n> 1. 如果在 .netrc 中设置了用户认证信息,使用 headers= 设置的授权就不会生效。而如果设置了 auth= 参数,``.netrc`` 的设置就无效了。\n> 2. 如果被重定向到别的主机,授权 header 就会被删除。\n> 3. 代理授权 header 会被 URL 中提供的代理身份覆盖掉。\n> 4. 在我们能判断内容长度的情况下,header 的 Content-Length 会被改写。\n\n#### SSL\n````\nrequests.get('https://github.com', verify=True)\n````\n\n#### 流式请求\n````\nres = requests.get(domain + '/stream/20', stream=True)\nprint(res.raw)\nif res.encoding is None:\n res.encoding = 'utf-8'\nfor line in res.iter_lines():\n if line:\n decoded_line = line.decode('utf-8')\n print(json.loads(decoded_line))\n````\n\n#### Auth HTTP基本身份认证\n````\nauth = requests.auth.HTTPBasicAuth('user', 'passwd')\nres = requests.get(domain + '/hidden-basic-auth/user/passwd', auth=auth)\nprint(res.json())\n````\n\n## 参考资料\n1.[Requests: 让 HTTP 服务人类](http://docs.python-requests.org/zh_CN/latest/index.html#)\n2.[httpbin](http://httpbin.org/)\n3.[code](https://github.com/bayuefen/demos/blob/master/requestDemo/test.py)","slug":"python-module-requests","published":1,"updated":"2019-06-09T17:21:12.952Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cjwr0llvr0008bkvlcazs79o9","content":"<p>本文基于<code>python3.7</code>及第三方模块<code>requests</code>编写入门级简易模式的HTTP请求应用于详解。<br><code>requests</code>是由python编写,基于urllib, 采用Apache2 Licensed开源协议的HTTP三方模块。详情应用见<a href=\"http://docs.python-requests.org/zh_CN/latest/index.html\" target=\"_blank\" rel=\"noopener\">官网</a></p>\n<a id=\"more\"></a>\n<h2 id=\"安装\"><a href=\"#安装\" class=\"headerlink\" title=\"安装\"></a>安装</h2><blockquote>\n<ol>\n<li>如果已安装Anaconda,默认是含<code>requests</code>模块。</li>\n<li>终端命令行安装,<a href=\"http://docs.python-requests.org/zh_CN/latest/user/install.html#install\" target=\"_blank\" rel=\"noopener\">详情</a> <figure class=\"highlight cmake\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">pip <span class=\"keyword\">install</span> requests</span><br></pre></td></tr></table></figure>\n</li>\n</ol>\n</blockquote>\n<h2 id=\"应用\"><a href=\"#应用\" class=\"headerlink\" title=\"应用\"></a>应用</h2><figure class=\"highlight swift\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">import</span> requests</span><br><span class=\"line\"><span class=\"keyword\">import</span> logging</span><br><span class=\"line\"><span class=\"keyword\">import</span> json</span><br><span class=\"line\">from requests.auth <span class=\"keyword\">import</span> AuthBase</span><br><span class=\"line\"></span><br><span class=\"line\">domain = 'http:<span class=\"comment\">//httpbin.org'</span></span><br></pre></td></tr></table></figure>\n<h4 id=\"GET\"><a href=\"#GET\" class=\"headerlink\" title=\"GET\"></a>GET</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">res</span> = requests.<span class=\"built_in\">get</span>(domain + <span class=\"string\">'/get'</span>, params={<span class=\"string\">'aa'</span>: <span class=\"number\">1</span>, <span class=\"string\">'bb'</span>: <span class=\"number\">2</span>})</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.status_code, <span class=\"keyword\">res</span>.text)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.url, <span class=\"keyword\">res</span>.json())</span><br></pre></td></tr></table></figure>\n<h4 id=\"POST\"><a href=\"#POST\" class=\"headerlink\" title=\"POST\"></a>POST</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">res</span> = requests.post(domain + <span class=\"string\">'/post'</span>, params={<span class=\"string\">'aa'</span>: <span class=\"number\">11</span>, <span class=\"string\">'bb'</span>: <span class=\"number\">22</span>}, data={<span class=\"string\">'username'</span>: <span class=\"string\">'bayuefen'</span>, <span class=\"string\">'password'</span>: <span class=\"string\">'123456'</span>})</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.status_code, <span class=\"keyword\">res</span>.text)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.url, <span class=\"keyword\">res</span>.json())</span><br></pre></td></tr></table></figure>\n<h4 id=\"PATCH\"><a href=\"#PATCH\" class=\"headerlink\" title=\"PATCH\"></a>PATCH</h4><figure class=\"highlight stylus\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">res = requests.patch(domain + <span class=\"string\">'/patch'</span>, data={<span class=\"string\">'username'</span>:<span class=\"string\">'bayuefen'</span>,<span class=\"string\">'password'</span>:<span class=\"string\">'123456'</span>,<span class=\"string\">'visibility'</span>:<span class=\"string\">'private'</span>})</span><br><span class=\"line\"><span class=\"function\"><span class=\"title\">print</span><span class=\"params\">(res.status_code, res.text)</span></span></span><br><span class=\"line\"><span class=\"function\"><span class=\"title\">print</span><span class=\"params\">(res.headers)</span></span></span><br></pre></td></tr></table></figure>\n<h4 id=\"DELETE\"><a href=\"#DELETE\" class=\"headerlink\" title=\"DELETE\"></a>DELETE</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">res</span> = requests.<span class=\"keyword\">delete</span>(domain + <span class=\"string\">'/delete'</span>)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.status_code, <span class=\"keyword\">res</span>.text)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.headers)</span><br></pre></td></tr></table></figure>\n<h4 id=\"Session-⭐️⭐️⭐️\"><a href=\"#Session-⭐️⭐️⭐️\" class=\"headerlink\" title=\"Session (⭐️⭐️⭐️)\"></a>Session (⭐️⭐️⭐️)</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">rs = requests.Session()</span><br><span class=\"line\">rs.<span class=\"built_in\">get</span>(domain + <span class=\"string\">'/cookies/set/username/bayuefen'</span>)</span><br><span class=\"line\"><span class=\"keyword\">res</span> = rs.<span class=\"built_in\">get</span>(domain + <span class=\"string\">'/cookies'</span>)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.text)</span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>1.Session对象能够跨请求保持某些特定参数,并且同一个Session实例发出的所有请求之间保持cookie<br>2.对于同一个服务器发送多个请求,底层TCP连接可被重用,有显著的性能提升</p>\n</blockquote>\n<h4 id=\"Cookie-⭐️⭐️⭐️\"><a href=\"#Cookie-⭐️⭐️⭐️\" class=\"headerlink\" title=\"Cookie (⭐️⭐️⭐️)\"></a>Cookie (⭐️⭐️⭐️)</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">cookies = dict(admin_token=<span class=\"string\">'xxxxxxxxx'</span>)</span><br><span class=\"line\"><span class=\"keyword\">res</span> = requests.<span class=\"built_in\">get</span>(domain + <span class=\"string\">'/cookies/set'</span>, cookies=cookies)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.text)</span><br></pre></td></tr></table></figure>\n<figure class=\"highlight stylus\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">jar = requests<span class=\"selector-class\">.cookies</span><span class=\"selector-class\">.RequestsCookieJar</span>()</span><br><span class=\"line\">jar.set(<span class=\"string\">'bayuefen_cookies'</span>, <span class=\"string\">'123456'</span>, domain=<span class=\"string\">'httpbin.org'</span>, path=<span class=\"string\">'/cookies'</span>)</span><br><span class=\"line\">jar.set(<span class=\"string\">'bayuefen_values'</span>, <span class=\"string\">'my_values'</span>, domain=<span class=\"string\">'httpbin.org'</span>, path=<span class=\"string\">'/elsewhere'</span>)</span><br><span class=\"line\">res1 = requests.get(domain + <span class=\"string\">'/cookies'</span>, cookies=jar)</span><br><span class=\"line\"><span class=\"function\"><span class=\"title\">print</span><span class=\"params\">(res1.text)</span></span></span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>Cookie的返回对象是RequestsCookieJar,这种模式适用于跨域名跨路径使用</p>\n</blockquote>\n<h4 id=\"redirect\"><a href=\"#redirect\" class=\"headerlink\" title=\"redirect\"></a>redirect</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">res</span> = requests.<span class=\"built_in\">get</span>(domain + <span class=\"string\">'/redirect-to'</span>, params={<span class=\"string\">'url'</span>:<span class=\"string\">'https://www.baidu.com'</span>, <span class=\"string\">'status_code'</span>:<span class=\"number\">302</span>})</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.url, <span class=\"keyword\">res</span>.status_code)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.<span class=\"keyword\">history</span>)</span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>可配置allow_redirects = False 禁用重定向</p>\n</blockquote>\n<h4 id=\"timeout\"><a href=\"#timeout\" class=\"headerlink\" title=\"timeout\"></a>timeout</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">res</span> = requests.<span class=\"built_in\">get</span>(domain + <span class=\"string\">'/get'</span>, timeout=<span class=\"number\">0.001</span>)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.raise_for_status())</span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>超时的异常触发仅仅针对连接过程,与相应体的下载无关,即请求发起至服务器相应的最大时间,超过则以异常处理</p>\n</blockquote>\n<h4 id=\"file-upload\"><a href=\"#file-upload\" class=\"headerlink\" title=\"file upload\"></a>file upload</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">file</span> = {<span class=\"string\">'txtFile'</span>: <span class=\"keyword\">open</span>(<span class=\"string\">'./test.txt'</span>, <span class=\"string\">'rb'</span>)}</span><br><span class=\"line\">upload_data = {<span class=\"string\">\"parentId\"</span>: <span class=\"string\">\"\"</span>, <span class=\"string\">\"fileCategory\"</span>: <span class=\"string\">\"personal\"</span>, <span class=\"string\">\"fileSize\"</span>: <span class=\"number\">179</span>, <span class=\"string\">\"fileName\"</span>: <span class=\"string\">\"test.txt\"</span>, <span class=\"string\">\"uoType\"</span>: <span class=\"number\">1</span>}</span><br><span class=\"line\"><span class=\"keyword\">res</span> = requests.post(domain + <span class=\"string\">'/post'</span>, upload_data, <span class=\"keyword\">files</span>=<span class=\"keyword\">file</span>)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.url, <span class=\"keyword\">res</span>.status_code)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.json())</span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>超大文件的传输使用multipart/form-data,默认情况下requests是不支持的,需要使用requests-toolbelt</p>\n</blockquote>\n<h4 id=\"file-download\"><a href=\"#file-download\" class=\"headerlink\" title=\"file download\"></a>file download</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">res</span> = requests.<span class=\"built_in\">get</span>(domain + <span class=\"string\">'/image/jpeg'</span>, stream=True)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.raw, <span class=\"keyword\">res</span>.raw.<span class=\"keyword\">read</span>(<span class=\"number\">10</span>))</span><br><span class=\"line\">with <span class=\"keyword\">open</span>(<span class=\"string\">'./test.jpeg'</span>, <span class=\"string\">'wb'</span>) <span class=\"keyword\">as</span> <span class=\"keyword\">f</span>:</span><br><span class=\"line\"> <span class=\"keyword\">f</span>.<span class=\"keyword\">write</span>(<span class=\"keyword\">res</span>.content)</span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>notice: 下载原始相应内容(images/xlsx/pdf等),通过设置stream获取raw</p>\n</blockquote>\n<h4 id=\"JSON解码器\"><a href=\"#JSON解码器\" class=\"headerlink\" title=\"JSON解码器\"></a>JSON解码器</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">res</span> = requests.post(domain + <span class=\"string\">'/post'</span>, data={<span class=\"string\">'aa'</span>: <span class=\"number\">11</span>, <span class=\"string\">'bb'</span>: <span class=\"number\">2</span>})</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.raise_for_status(), <span class=\"keyword\">res</span>.status_code)</span><br><span class=\"line\"><span class=\"keyword\">try</span>:</span><br><span class=\"line\"> <span class=\"keyword\">res</span>.json()</span><br><span class=\"line\">except ValueError:</span><br><span class=\"line\"> logging.error(<span class=\"string\">'No JSON object could be decoded'</span>)</span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>JSON被解码返回成功,不一定代表HTTP相应成功;检查请求响应是否成功,需通过<code>response.raise_for_status()</code> or <code>response.status_code</code>去判别</p>\n</blockquote>\n<h4 id=\"定制-headers\"><a href=\"#定制-headers\" class=\"headerlink\" title=\"定制 headers\"></a>定制 headers</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">headers = {</span><br><span class=\"line\"> <span class=\"string\">'user-agent'</span>: <span class=\"string\">'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'</span></span><br><span class=\"line\">}</span><br><span class=\"line\"><span class=\"keyword\">res</span> = requests.<span class=\"built_in\">get</span>(domain + <span class=\"string\">'/get'</span>, headers=headers)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.json())</span><br><span class=\"line\"># 响应头</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.headers.<span class=\"built_in\">get</span>(<span class=\"string\">'content-type'</span>))</span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>定制headers低于特定场景的信息源</p>\n<ol>\n<li>如果在 .netrc 中设置了用户认证信息,使用 headers= 设置的授权就不会生效。而如果设置了 auth= 参数,<code>.netrc</code> 的设置就无效了。</li>\n<li>如果被重定向到别的主机,授权 header 就会被删除。</li>\n<li>代理授权 header 会被 URL 中提供的代理身份覆盖掉。</li>\n<li>在我们能判断内容长度的情况下,header 的 Content-Length 会被改写。</li>\n</ol>\n</blockquote>\n<h4 id=\"SSL\"><a href=\"#SSL\" class=\"headerlink\" title=\"SSL\"></a>SSL</h4><figure class=\"highlight ada\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">requests.get(<span class=\"symbol\">'https</span>://github.com', verify=<span class=\"literal\">True</span>)</span><br></pre></td></tr></table></figure>\n<h4 id=\"流式请求\"><a href=\"#流式请求\" class=\"headerlink\" title=\"流式请求\"></a>流式请求</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">res</span> = requests.<span class=\"built_in\">get</span>(domain + <span class=\"string\">'/stream/20'</span>, stream=True)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.raw)</span><br><span class=\"line\"><span class=\"keyword\">if</span> <span class=\"keyword\">res</span>.encoding <span class=\"keyword\">is</span> None:</span><br><span class=\"line\"> <span class=\"keyword\">res</span>.encoding = <span class=\"string\">'utf-8'</span></span><br><span class=\"line\"><span class=\"keyword\">for</span> <span class=\"built_in\">line</span> in <span class=\"keyword\">res</span>.iter_lines():</span><br><span class=\"line\"> <span class=\"keyword\">if</span> <span class=\"built_in\">line</span>:</span><br><span class=\"line\"> decoded_line = <span class=\"built_in\">line</span>.decode(<span class=\"string\">'utf-8'</span>)</span><br><span class=\"line\"> <span class=\"keyword\">print</span>(json.loads(decoded_line))</span><br></pre></td></tr></table></figure>\n<h4 id=\"Auth-HTTP基本身份认证\"><a href=\"#Auth-HTTP基本身份认证\" class=\"headerlink\" title=\"Auth HTTP基本身份认证\"></a>Auth HTTP基本身份认证</h4><figure class=\"highlight stylus\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">auth = requests<span class=\"selector-class\">.auth</span><span class=\"selector-class\">.HTTPBasicAuth</span>(<span class=\"string\">'user'</span>, <span class=\"string\">'passwd'</span>)</span><br><span class=\"line\">res = requests.get(domain + <span class=\"string\">'/hidden-basic-auth/user/passwd'</span>, auth=auth)</span><br><span class=\"line\"><span class=\"function\"><span class=\"title\">print</span><span class=\"params\">(res.json()</span></span>)</span><br></pre></td></tr></table></figure>\n<h2 id=\"参考资料\"><a href=\"#参考资料\" class=\"headerlink\" title=\"参考资料\"></a>参考资料</h2><p>1.<a href=\"http://docs.python-requests.org/zh_CN/latest/index.html#\" target=\"_blank\" rel=\"noopener\">Requests: 让 HTTP 服务人类</a><br>2.<a href=\"http://httpbin.org/\" target=\"_blank\" rel=\"noopener\">httpbin</a><br>3.<a href=\"https://github.com/bayuefen/demos/blob/master/requestDemo/test.py\" target=\"_blank\" rel=\"noopener\">code</a></p>\n","site":{"data":{}},"excerpt":"<p>本文基于<code>python3.7</code>及第三方模块<code>requests</code>编写入门级简易模式的HTTP请求应用于详解。<br><code>requests</code>是由python编写,基于urllib, 采用Apache2 Licensed开源协议的HTTP三方模块。详情应用见<a href=\"http://docs.python-requests.org/zh_CN/latest/index.html\" target=\"_blank\" rel=\"noopener\">官网</a></p>","more":"<h2 id=\"安装\"><a href=\"#安装\" class=\"headerlink\" title=\"安装\"></a>安装</h2><blockquote>\n<ol>\n<li>如果已安装Anaconda,默认是含<code>requests</code>模块。</li>\n<li>终端命令行安装,<a href=\"http://docs.python-requests.org/zh_CN/latest/user/install.html#install\" target=\"_blank\" rel=\"noopener\">详情</a> <figure class=\"highlight cmake\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">pip <span class=\"keyword\">install</span> requests</span><br></pre></td></tr></table></figure>\n</li>\n</ol>\n</blockquote>\n<h2 id=\"应用\"><a href=\"#应用\" class=\"headerlink\" title=\"应用\"></a>应用</h2><figure class=\"highlight swift\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">import</span> requests</span><br><span class=\"line\"><span class=\"keyword\">import</span> logging</span><br><span class=\"line\"><span class=\"keyword\">import</span> json</span><br><span class=\"line\">from requests.auth <span class=\"keyword\">import</span> AuthBase</span><br><span class=\"line\"></span><br><span class=\"line\">domain = 'http:<span class=\"comment\">//httpbin.org'</span></span><br></pre></td></tr></table></figure>\n<h4 id=\"GET\"><a href=\"#GET\" class=\"headerlink\" title=\"GET\"></a>GET</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">res</span> = requests.<span class=\"built_in\">get</span>(domain + <span class=\"string\">'/get'</span>, params={<span class=\"string\">'aa'</span>: <span class=\"number\">1</span>, <span class=\"string\">'bb'</span>: <span class=\"number\">2</span>})</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.status_code, <span class=\"keyword\">res</span>.text)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.url, <span class=\"keyword\">res</span>.json())</span><br></pre></td></tr></table></figure>\n<h4 id=\"POST\"><a href=\"#POST\" class=\"headerlink\" title=\"POST\"></a>POST</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">res</span> = requests.post(domain + <span class=\"string\">'/post'</span>, params={<span class=\"string\">'aa'</span>: <span class=\"number\">11</span>, <span class=\"string\">'bb'</span>: <span class=\"number\">22</span>}, data={<span class=\"string\">'username'</span>: <span class=\"string\">'bayuefen'</span>, <span class=\"string\">'password'</span>: <span class=\"string\">'123456'</span>})</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.status_code, <span class=\"keyword\">res</span>.text)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.url, <span class=\"keyword\">res</span>.json())</span><br></pre></td></tr></table></figure>\n<h4 id=\"PATCH\"><a href=\"#PATCH\" class=\"headerlink\" title=\"PATCH\"></a>PATCH</h4><figure class=\"highlight stylus\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">res = requests.patch(domain + <span class=\"string\">'/patch'</span>, data={<span class=\"string\">'username'</span>:<span class=\"string\">'bayuefen'</span>,<span class=\"string\">'password'</span>:<span class=\"string\">'123456'</span>,<span class=\"string\">'visibility'</span>:<span class=\"string\">'private'</span>})</span><br><span class=\"line\"><span class=\"function\"><span class=\"title\">print</span><span class=\"params\">(res.status_code, res.text)</span></span></span><br><span class=\"line\"><span class=\"function\"><span class=\"title\">print</span><span class=\"params\">(res.headers)</span></span></span><br></pre></td></tr></table></figure>\n<h4 id=\"DELETE\"><a href=\"#DELETE\" class=\"headerlink\" title=\"DELETE\"></a>DELETE</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">res</span> = requests.<span class=\"keyword\">delete</span>(domain + <span class=\"string\">'/delete'</span>)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.status_code, <span class=\"keyword\">res</span>.text)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.headers)</span><br></pre></td></tr></table></figure>\n<h4 id=\"Session-⭐️⭐️⭐️\"><a href=\"#Session-⭐️⭐️⭐️\" class=\"headerlink\" title=\"Session (⭐️⭐️⭐️)\"></a>Session (⭐️⭐️⭐️)</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">rs = requests.Session()</span><br><span class=\"line\">rs.<span class=\"built_in\">get</span>(domain + <span class=\"string\">'/cookies/set/username/bayuefen'</span>)</span><br><span class=\"line\"><span class=\"keyword\">res</span> = rs.<span class=\"built_in\">get</span>(domain + <span class=\"string\">'/cookies'</span>)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.text)</span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>1.Session对象能够跨请求保持某些特定参数,并且同一个Session实例发出的所有请求之间保持cookie<br>2.对于同一个服务器发送多个请求,底层TCP连接可被重用,有显著的性能提升</p>\n</blockquote>\n<h4 id=\"Cookie-⭐️⭐️⭐️\"><a href=\"#Cookie-⭐️⭐️⭐️\" class=\"headerlink\" title=\"Cookie (⭐️⭐️⭐️)\"></a>Cookie (⭐️⭐️⭐️)</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">cookies = dict(admin_token=<span class=\"string\">'xxxxxxxxx'</span>)</span><br><span class=\"line\"><span class=\"keyword\">res</span> = requests.<span class=\"built_in\">get</span>(domain + <span class=\"string\">'/cookies/set'</span>, cookies=cookies)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.text)</span><br></pre></td></tr></table></figure>\n<figure class=\"highlight stylus\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">jar = requests<span class=\"selector-class\">.cookies</span><span class=\"selector-class\">.RequestsCookieJar</span>()</span><br><span class=\"line\">jar.set(<span class=\"string\">'bayuefen_cookies'</span>, <span class=\"string\">'123456'</span>, domain=<span class=\"string\">'httpbin.org'</span>, path=<span class=\"string\">'/cookies'</span>)</span><br><span class=\"line\">jar.set(<span class=\"string\">'bayuefen_values'</span>, <span class=\"string\">'my_values'</span>, domain=<span class=\"string\">'httpbin.org'</span>, path=<span class=\"string\">'/elsewhere'</span>)</span><br><span class=\"line\">res1 = requests.get(domain + <span class=\"string\">'/cookies'</span>, cookies=jar)</span><br><span class=\"line\"><span class=\"function\"><span class=\"title\">print</span><span class=\"params\">(res1.text)</span></span></span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>Cookie的返回对象是RequestsCookieJar,这种模式适用于跨域名跨路径使用</p>\n</blockquote>\n<h4 id=\"redirect\"><a href=\"#redirect\" class=\"headerlink\" title=\"redirect\"></a>redirect</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">res</span> = requests.<span class=\"built_in\">get</span>(domain + <span class=\"string\">'/redirect-to'</span>, params={<span class=\"string\">'url'</span>:<span class=\"string\">'https://www.baidu.com'</span>, <span class=\"string\">'status_code'</span>:<span class=\"number\">302</span>})</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.url, <span class=\"keyword\">res</span>.status_code)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.<span class=\"keyword\">history</span>)</span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>可配置allow_redirects = False 禁用重定向</p>\n</blockquote>\n<h4 id=\"timeout\"><a href=\"#timeout\" class=\"headerlink\" title=\"timeout\"></a>timeout</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">res</span> = requests.<span class=\"built_in\">get</span>(domain + <span class=\"string\">'/get'</span>, timeout=<span class=\"number\">0.001</span>)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.raise_for_status())</span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>超时的异常触发仅仅针对连接过程,与相应体的下载无关,即请求发起至服务器相应的最大时间,超过则以异常处理</p>\n</blockquote>\n<h4 id=\"file-upload\"><a href=\"#file-upload\" class=\"headerlink\" title=\"file upload\"></a>file upload</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">file</span> = {<span class=\"string\">'txtFile'</span>: <span class=\"keyword\">open</span>(<span class=\"string\">'./test.txt'</span>, <span class=\"string\">'rb'</span>)}</span><br><span class=\"line\">upload_data = {<span class=\"string\">\"parentId\"</span>: <span class=\"string\">\"\"</span>, <span class=\"string\">\"fileCategory\"</span>: <span class=\"string\">\"personal\"</span>, <span class=\"string\">\"fileSize\"</span>: <span class=\"number\">179</span>, <span class=\"string\">\"fileName\"</span>: <span class=\"string\">\"test.txt\"</span>, <span class=\"string\">\"uoType\"</span>: <span class=\"number\">1</span>}</span><br><span class=\"line\"><span class=\"keyword\">res</span> = requests.post(domain + <span class=\"string\">'/post'</span>, upload_data, <span class=\"keyword\">files</span>=<span class=\"keyword\">file</span>)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.url, <span class=\"keyword\">res</span>.status_code)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.json())</span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>超大文件的传输使用multipart/form-data,默认情况下requests是不支持的,需要使用requests-toolbelt</p>\n</blockquote>\n<h4 id=\"file-download\"><a href=\"#file-download\" class=\"headerlink\" title=\"file download\"></a>file download</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">res</span> = requests.<span class=\"built_in\">get</span>(domain + <span class=\"string\">'/image/jpeg'</span>, stream=True)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.raw, <span class=\"keyword\">res</span>.raw.<span class=\"keyword\">read</span>(<span class=\"number\">10</span>))</span><br><span class=\"line\">with <span class=\"keyword\">open</span>(<span class=\"string\">'./test.jpeg'</span>, <span class=\"string\">'wb'</span>) <span class=\"keyword\">as</span> <span class=\"keyword\">f</span>:</span><br><span class=\"line\"> <span class=\"keyword\">f</span>.<span class=\"keyword\">write</span>(<span class=\"keyword\">res</span>.content)</span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>notice: 下载原始相应内容(images/xlsx/pdf等),通过设置stream获取raw</p>\n</blockquote>\n<h4 id=\"JSON解码器\"><a href=\"#JSON解码器\" class=\"headerlink\" title=\"JSON解码器\"></a>JSON解码器</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">res</span> = requests.post(domain + <span class=\"string\">'/post'</span>, data={<span class=\"string\">'aa'</span>: <span class=\"number\">11</span>, <span class=\"string\">'bb'</span>: <span class=\"number\">2</span>})</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.raise_for_status(), <span class=\"keyword\">res</span>.status_code)</span><br><span class=\"line\"><span class=\"keyword\">try</span>:</span><br><span class=\"line\"> <span class=\"keyword\">res</span>.json()</span><br><span class=\"line\">except ValueError:</span><br><span class=\"line\"> logging.error(<span class=\"string\">'No JSON object could be decoded'</span>)</span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>JSON被解码返回成功,不一定代表HTTP相应成功;检查请求响应是否成功,需通过<code>response.raise_for_status()</code> or <code>response.status_code</code>去判别</p>\n</blockquote>\n<h4 id=\"定制-headers\"><a href=\"#定制-headers\" class=\"headerlink\" title=\"定制 headers\"></a>定制 headers</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">headers = {</span><br><span class=\"line\"> <span class=\"string\">'user-agent'</span>: <span class=\"string\">'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'</span></span><br><span class=\"line\">}</span><br><span class=\"line\"><span class=\"keyword\">res</span> = requests.<span class=\"built_in\">get</span>(domain + <span class=\"string\">'/get'</span>, headers=headers)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.json())</span><br><span class=\"line\"># 响应头</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.headers.<span class=\"built_in\">get</span>(<span class=\"string\">'content-type'</span>))</span><br></pre></td></tr></table></figure>\n<blockquote>\n<p>定制headers低于特定场景的信息源</p>\n<ol>\n<li>如果在 .netrc 中设置了用户认证信息,使用 headers= 设置的授权就不会生效。而如果设置了 auth= 参数,<code>.netrc</code> 的设置就无效了。</li>\n<li>如果被重定向到别的主机,授权 header 就会被删除。</li>\n<li>代理授权 header 会被 URL 中提供的代理身份覆盖掉。</li>\n<li>在我们能判断内容长度的情况下,header 的 Content-Length 会被改写。</li>\n</ol>\n</blockquote>\n<h4 id=\"SSL\"><a href=\"#SSL\" class=\"headerlink\" title=\"SSL\"></a>SSL</h4><figure class=\"highlight ada\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">requests.get(<span class=\"symbol\">'https</span>://github.com', verify=<span class=\"literal\">True</span>)</span><br></pre></td></tr></table></figure>\n<h4 id=\"流式请求\"><a href=\"#流式请求\" class=\"headerlink\" title=\"流式请求\"></a>流式请求</h4><figure class=\"highlight vim\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">res</span> = requests.<span class=\"built_in\">get</span>(domain + <span class=\"string\">'/stream/20'</span>, stream=True)</span><br><span class=\"line\"><span class=\"keyword\">print</span>(<span class=\"keyword\">res</span>.raw)</span><br><span class=\"line\"><span class=\"keyword\">if</span> <span class=\"keyword\">res</span>.encoding <span class=\"keyword\">is</span> None:</span><br><span class=\"line\"> <span class=\"keyword\">res</span>.encoding = <span class=\"string\">'utf-8'</span></span><br><span class=\"line\"><span class=\"keyword\">for</span> <span class=\"built_in\">line</span> in <span class=\"keyword\">res</span>.iter_lines():</span><br><span class=\"line\"> <span class=\"keyword\">if</span> <span class=\"built_in\">line</span>:</span><br><span class=\"line\"> decoded_line = <span class=\"built_in\">line</span>.decode(<span class=\"string\">'utf-8'</span>)</span><br><span class=\"line\"> <span class=\"keyword\">print</span>(json.loads(decoded_line))</span><br></pre></td></tr></table></figure>\n<h4 id=\"Auth-HTTP基本身份认证\"><a href=\"#Auth-HTTP基本身份认证\" class=\"headerlink\" title=\"Auth HTTP基本身份认证\"></a>Auth HTTP基本身份认证</h4><figure class=\"highlight stylus\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">auth = requests<span class=\"selector-class\">.auth</span><span class=\"selector-class\">.HTTPBasicAuth</span>(<span class=\"string\">'user'</span>, <span class=\"string\">'passwd'</span>)</span><br><span class=\"line\">res = requests.get(domain + <span class=\"string\">'/hidden-basic-auth/user/passwd'</span>, auth=auth)</span><br><span class=\"line\"><span class=\"function\"><span class=\"title\">print</span><span class=\"params\">(res.json()</span></span>)</span><br></pre></td></tr></table></figure>\n<h2 id=\"参考资料\"><a href=\"#参考资料\" class=\"headerlink\" title=\"参考资料\"></a>参考资料</h2><p>1.<a href=\"http://docs.python-requests.org/zh_CN/latest/index.html#\" target=\"_blank\" rel=\"noopener\">Requests: 让 HTTP 服务人类</a><br>2.<a href=\"http://httpbin.org/\" target=\"_blank\" rel=\"noopener\">httpbin</a><br>3.<a href=\"https://github.com/bayuefen/demos/blob/master/requestDemo/test.py\" target=\"_blank\" rel=\"noopener\">code</a></p>"},{"title":"Web 安全","date":"2019-06-09T16:00:00.000Z","_content":"一些学习笔记。\n<!-- more -->\n基于客户端的脚本安全\n****************************************************************************\n### XSS\n什么是XSS?\nXSS是一种跨站脚本攻击。\n\n怎样发起XSS攻击?\n通过HTML注入篡改网页,插入恶意脚本,从而在用户浏览网页时,控制用户浏览器的一种攻击。\n反射型XSS:\n构造特殊的URL里面包含恶意代码,诱使用户触发(“点击”)一个恶意链接,攻击成功。效果来讲“非持久型XSS”\n存储型XSS:\n恶意脚本存储在数据库(服务器端),所有访问服务器的用户都会在他们的浏览器中执行这段恶意的JavaScript代码。效果来讲存在时间较久\nDOM Based XSS:\n效果来说与反射型差不多,修改页面的DOM节点形成的XSS\n\nXSS攻击手段:\n+ cookie 劫持\n+ 构造GET 与 POST 请求\n+ 钓鱼网站\n+ 识别用户的浏览器\n+ 识别用户安装的软件\n+ 通过CSS发现用户曾经访问过的网站\n+ 获取用户的真实IP地址\n\nXSS的防御:\n+ 对cookie设置HttpOnly属性\n+ 对用户输入和富文本及变量输出进行编码和转义\n\n如何测试XSS漏洞:\n应用XSS攻击平台渗透测试使用(Attack API、BeEF、XSS-Proxy)\n\n### CSRF\n什么是CSRF?\nCSRF是跨站点请求伪造\n\nCSRF怎么发起攻击?\n诱使用户在第三方站点执行了攻击者伪造的带参数的请求。\n请求被验证通过是因为某些浏览器策略允许发送第三方cookie,或是利用P3P头的介入。\nCSRF攻击不仅限于GET请求,还可以把参数构建一个POST表单提交。\n\nCSRF的防御:\n+ 验证码\n+ Anti CSRF Token(前提是token的值是随机的)\n\n如何测试CSRF漏洞:\n+ 抓到数据包是否带有token验证,或者还有一个固定的变量可以被控制,就会存在CSRF。\n+ 构造一个没有token,referer为空请求的post请求,成功就存在CSRF\n\n### 点击劫持\n什么是ClickJacking?\n点击劫持是一种视觉欺骗手段\n\n怎么发起ClickJacking?\n使用一个透明的、不可见的iframe,覆盖在讴歌网页上,然后诱使用户在该网页上进行操作,通过调整iframe页面位置,点击触发一些功能性按钮。\n浏览器的拖拽操作不受同源策略限制,运用拖拽劫持可以达到数据窃取\n\nClickJacking的防御:\n使用 X-Frame-Options HTTP 响应头。\nX-Frame-Options 有三个值:\nDENY - 表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许。\nSAMEORIGIN - 表示该页面可以在相同域名页面的 frame 中展示。\nALLOW-FROM - 表示该页面可以在指定来源的 frame 中展示。\n\n如何测试ClickJacking漏洞:\n是否可以进行iframe嵌套\n\n基于服务器的脚本安全\n****************************************************************************\n### 注入攻击\n本质是用户输入的数据当做代码执行。\n关键条件是用户能够控制输入;原本程序要执行的代码,拼接了用户输入的数据\n\n常见的注入攻击:\nSQL注入、XML注入、代码注入、CRLF注入\n\n如何防御SQL注入:\n+ 使用预编译语句,绑定变量\n+ 使用安全的存储过程\n+ 检查数据类型\n\n如何测试SQL注入漏洞:\n在URL中“盲注”SQL语句,判断哪些参数存在SQL注入漏洞\n\n****************************************************************************\n实际场景中攻击手段都不是单一的,往往都是同时嵌套使用","source":"_posts/web-secure.md","raw":"---\ntitle: Web 安全\ndate: 2019-06-10 \ntags:\n - 日记\n - 学习Vlog\n---\n一些学习笔记。\n<!-- more -->\n基于客户端的脚本安全\n****************************************************************************\n### XSS\n什么是XSS?\nXSS是一种跨站脚本攻击。\n\n怎样发起XSS攻击?\n通过HTML注入篡改网页,插入恶意脚本,从而在用户浏览网页时,控制用户浏览器的一种攻击。\n反射型XSS:\n构造特殊的URL里面包含恶意代码,诱使用户触发(“点击”)一个恶意链接,攻击成功。效果来讲“非持久型XSS”\n存储型XSS:\n恶意脚本存储在数据库(服务器端),所有访问服务器的用户都会在他们的浏览器中执行这段恶意的JavaScript代码。效果来讲存在时间较久\nDOM Based XSS:\n效果来说与反射型差不多,修改页面的DOM节点形成的XSS\n\nXSS攻击手段:\n+ cookie 劫持\n+ 构造GET 与 POST 请求\n+ 钓鱼网站\n+ 识别用户的浏览器\n+ 识别用户安装的软件\n+ 通过CSS发现用户曾经访问过的网站\n+ 获取用户的真实IP地址\n\nXSS的防御:\n+ 对cookie设置HttpOnly属性\n+ 对用户输入和富文本及变量输出进行编码和转义\n\n如何测试XSS漏洞:\n应用XSS攻击平台渗透测试使用(Attack API、BeEF、XSS-Proxy)\n\n### CSRF\n什么是CSRF?\nCSRF是跨站点请求伪造\n\nCSRF怎么发起攻击?\n诱使用户在第三方站点执行了攻击者伪造的带参数的请求。\n请求被验证通过是因为某些浏览器策略允许发送第三方cookie,或是利用P3P头的介入。\nCSRF攻击不仅限于GET请求,还可以把参数构建一个POST表单提交。\n\nCSRF的防御:\n+ 验证码\n+ Anti CSRF Token(前提是token的值是随机的)\n\n如何测试CSRF漏洞:\n+ 抓到数据包是否带有token验证,或者还有一个固定的变量可以被控制,就会存在CSRF。\n+ 构造一个没有token,referer为空请求的post请求,成功就存在CSRF\n\n### 点击劫持\n什么是ClickJacking?\n点击劫持是一种视觉欺骗手段\n\n怎么发起ClickJacking?\n使用一个透明的、不可见的iframe,覆盖在讴歌网页上,然后诱使用户在该网页上进行操作,通过调整iframe页面位置,点击触发一些功能性按钮。\n浏览器的拖拽操作不受同源策略限制,运用拖拽劫持可以达到数据窃取\n\nClickJacking的防御:\n使用 X-Frame-Options HTTP 响应头。\nX-Frame-Options 有三个值:\nDENY - 表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许。\nSAMEORIGIN - 表示该页面可以在相同域名页面的 frame 中展示。\nALLOW-FROM - 表示该页面可以在指定来源的 frame 中展示。\n\n如何测试ClickJacking漏洞:\n是否可以进行iframe嵌套\n\n基于服务器的脚本安全\n****************************************************************************\n### 注入攻击\n本质是用户输入的数据当做代码执行。\n关键条件是用户能够控制输入;原本程序要执行的代码,拼接了用户输入的数据\n\n常见的注入攻击:\nSQL注入、XML注入、代码注入、CRLF注入\n\n如何防御SQL注入:\n+ 使用预编译语句,绑定变量\n+ 使用安全的存储过程\n+ 检查数据类型\n\n如何测试SQL注入漏洞:\n在URL中“盲注”SQL语句,判断哪些参数存在SQL注入漏洞\n\n****************************************************************************\n实际场景中攻击手段都不是单一的,往往都是同时嵌套使用","slug":"web-secure","published":1,"updated":"2019-06-10T23:39:04.560Z","_id":"cjwr0llvs000abkvlh12udhhz","comments":1,"layout":"post","photos":[],"link":"","content":"<p>一些学习笔记。<br><a id=\"more\"></a><br>基于客户端的脚本安全</p>\n<hr>\n<h3 id=\"XSS\"><a href=\"#XSS\" class=\"headerlink\" title=\"XSS\"></a>XSS</h3><p>什么是XSS?<br>XSS是一种跨站脚本攻击。</p>\n<p>怎样发起XSS攻击?<br>通过HTML注入篡改网页,插入恶意脚本,从而在用户浏览网页时,控制用户浏览器的一种攻击。<br>反射型XSS:<br>构造特殊的URL里面包含恶意代码,诱使用户触发(“点击”)一个恶意链接,攻击成功。效果来讲“非持久型XSS”<br>存储型XSS:<br>恶意脚本存储在数据库(服务器端),所有访问服务器的用户都会在他们的浏览器中执行这段恶意的JavaScript代码。效果来讲存在时间较久<br>DOM Based XSS:<br>效果来说与反射型差不多,修改页面的DOM节点形成的XSS</p>\n<p>XSS攻击手段:</p>\n<ul>\n<li>cookie 劫持</li>\n<li>构造GET 与 POST 请求</li>\n<li>钓鱼网站</li>\n<li>识别用户的浏览器</li>\n<li>识别用户安装的软件</li>\n<li>通过CSS发现用户曾经访问过的网站</li>\n<li>获取用户的真实IP地址</li>\n</ul>\n<p>XSS的防御:</p>\n<ul>\n<li>对cookie设置HttpOnly属性</li>\n<li>对用户输入和富文本及变量输出进行编码和转义</li>\n</ul>\n<p>如何测试XSS漏洞:<br>应用XSS攻击平台渗透测试使用(Attack API、BeEF、XSS-Proxy)</p>\n<h3 id=\"CSRF\"><a href=\"#CSRF\" class=\"headerlink\" title=\"CSRF\"></a>CSRF</h3><p>什么是CSRF?<br>CSRF是跨站点请求伪造</p>\n<p>CSRF怎么发起攻击?<br>诱使用户在第三方站点执行了攻击者伪造的带参数的请求。<br>请求被验证通过是因为某些浏览器策略允许发送第三方cookie,或是利用P3P头的介入。<br>CSRF攻击不仅限于GET请求,还可以把参数构建一个POST表单提交。</p>\n<p>CSRF的防御:</p>\n<ul>\n<li>验证码</li>\n<li>Anti CSRF Token(前提是token的值是随机的)</li>\n</ul>\n<p>如何测试CSRF漏洞:</p>\n<ul>\n<li>抓到数据包是否带有token验证,或者还有一个固定的变量可以被控制,就会存在CSRF。</li>\n<li>构造一个没有token,referer为空请求的post请求,成功就存在CSRF</li>\n</ul>\n<h3 id=\"点击劫持\"><a href=\"#点击劫持\" class=\"headerlink\" title=\"点击劫持\"></a>点击劫持</h3><p>什么是ClickJacking?<br>点击劫持是一种视觉欺骗手段</p>\n<p>怎么发起ClickJacking?<br>使用一个透明的、不可见的iframe,覆盖在讴歌网页上,然后诱使用户在该网页上进行操作,通过调整iframe页面位置,点击触发一些功能性按钮。<br>浏览器的拖拽操作不受同源策略限制,运用拖拽劫持可以达到数据窃取</p>\n<p>ClickJacking的防御:<br>使用 X-Frame-Options HTTP 响应头。<br>X-Frame-Options 有三个值:<br>DENY - 表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许。<br>SAMEORIGIN - 表示该页面可以在相同域名页面的 frame 中展示。<br>ALLOW-FROM - 表示该页面可以在指定来源的 frame 中展示。</p>\n<p>如何测试ClickJacking漏洞:<br>是否可以进行iframe嵌套</p>\n<p>基于服务器的脚本安全</p>\n<hr>\n<h3 id=\"注入攻击\"><a href=\"#注入攻击\" class=\"headerlink\" title=\"注入攻击\"></a>注入攻击</h3><p>本质是用户输入的数据当做代码执行。<br>关键条件是用户能够控制输入;原本程序要执行的代码,拼接了用户输入的数据</p>\n<p>常见的注入攻击:<br>SQL注入、XML注入、代码注入、CRLF注入</p>\n<p>如何防御SQL注入:</p>\n<ul>\n<li>使用预编译语句,绑定变量</li>\n<li>使用安全的存储过程</li>\n<li>检查数据类型</li>\n</ul>\n<p>如何测试SQL注入漏洞:<br>在URL中“盲注”SQL语句,判断哪些参数存在SQL注入漏洞</p>\n<hr>\n<p>实际场景中攻击手段都不是单一的,往往都是同时嵌套使用</p>\n","site":{"data":{}},"excerpt":"<p>一些学习笔记。<br>","more":"<br>基于客户端的脚本安全</p>\n<hr>\n<h3 id=\"XSS\"><a href=\"#XSS\" class=\"headerlink\" title=\"XSS\"></a>XSS</h3><p>什么是XSS?<br>XSS是一种跨站脚本攻击。</p>\n<p>怎样发起XSS攻击?<br>通过HTML注入篡改网页,插入恶意脚本,从而在用户浏览网页时,控制用户浏览器的一种攻击。<br>反射型XSS:<br>构造特殊的URL里面包含恶意代码,诱使用户触发(“点击”)一个恶意链接,攻击成功。效果来讲“非持久型XSS”<br>存储型XSS:<br>恶意脚本存储在数据库(服务器端),所有访问服务器的用户都会在他们的浏览器中执行这段恶意的JavaScript代码。效果来讲存在时间较久<br>DOM Based XSS:<br>效果来说与反射型差不多,修改页面的DOM节点形成的XSS</p>\n<p>XSS攻击手段:</p>\n<ul>\n<li>cookie 劫持</li>\n<li>构造GET 与 POST 请求</li>\n<li>钓鱼网站</li>\n<li>识别用户的浏览器</li>\n<li>识别用户安装的软件</li>\n<li>通过CSS发现用户曾经访问过的网站</li>\n<li>获取用户的真实IP地址</li>\n</ul>\n<p>XSS的防御:</p>\n<ul>\n<li>对cookie设置HttpOnly属性</li>\n<li>对用户输入和富文本及变量输出进行编码和转义</li>\n</ul>\n<p>如何测试XSS漏洞:<br>应用XSS攻击平台渗透测试使用(Attack API、BeEF、XSS-Proxy)</p>\n<h3 id=\"CSRF\"><a href=\"#CSRF\" class=\"headerlink\" title=\"CSRF\"></a>CSRF</h3><p>什么是CSRF?<br>CSRF是跨站点请求伪造</p>\n<p>CSRF怎么发起攻击?<br>诱使用户在第三方站点执行了攻击者伪造的带参数的请求。<br>请求被验证通过是因为某些浏览器策略允许发送第三方cookie,或是利用P3P头的介入。<br>CSRF攻击不仅限于GET请求,还可以把参数构建一个POST表单提交。</p>\n<p>CSRF的防御:</p>\n<ul>\n<li>验证码</li>\n<li>Anti CSRF Token(前提是token的值是随机的)</li>\n</ul>\n<p>如何测试CSRF漏洞:</p>\n<ul>\n<li>抓到数据包是否带有token验证,或者还有一个固定的变量可以被控制,就会存在CSRF。</li>\n<li>构造一个没有token,referer为空请求的post请求,成功就存在CSRF</li>\n</ul>\n<h3 id=\"点击劫持\"><a href=\"#点击劫持\" class=\"headerlink\" title=\"点击劫持\"></a>点击劫持</h3><p>什么是ClickJacking?<br>点击劫持是一种视觉欺骗手段</p>\n<p>怎么发起ClickJacking?<br>使用一个透明的、不可见的iframe,覆盖在讴歌网页上,然后诱使用户在该网页上进行操作,通过调整iframe页面位置,点击触发一些功能性按钮。<br>浏览器的拖拽操作不受同源策略限制,运用拖拽劫持可以达到数据窃取</p>\n<p>ClickJacking的防御:<br>使用 X-Frame-Options HTTP 响应头。<br>X-Frame-Options 有三个值:<br>DENY - 表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许。<br>SAMEORIGIN - 表示该页面可以在相同域名页面的 frame 中展示。<br>ALLOW-FROM - 表示该页面可以在指定来源的 frame 中展示。</p>\n<p>如何测试ClickJacking漏洞:<br>是否可以进行iframe嵌套</p>\n<p>基于服务器的脚本安全</p>\n<hr>\n<h3 id=\"注入攻击\"><a href=\"#注入攻击\" class=\"headerlink\" title=\"注入攻击\"></a>注入攻击</h3><p>本质是用户输入的数据当做代码执行。<br>关键条件是用户能够控制输入;原本程序要执行的代码,拼接了用户输入的数据</p>\n<p>常见的注入攻击:<br>SQL注入、XML注入、代码注入、CRLF注入</p>\n<p>如何防御SQL注入:</p>\n<ul>\n<li>使用预编译语句,绑定变量</li>\n<li>使用安全的存储过程</li>\n<li>检查数据类型</li>\n</ul>\n<p>如何测试SQL注入漏洞:<br>在URL中“盲注”SQL语句,判断哪些参数存在SQL注入漏洞</p>\n<hr>\n<p>实际场景中攻击手段都不是单一的,往往都是同时嵌套使用</p>"},{"title":"Generate SSH key and How deploy it","date":"2019-05-29T11:21:12.000Z","top":false,"reward":false,"comments":0,"_content":"\n#### SSH\n\nGit is a distributed version control system, which means you can work locally but you can also share or \"push\" your changes to other servers. Before you can push your changes to a GitLab server you need a secure communication channel for sharing information.\n\n<!-- more -->\n\nThe SSH protocol provides this security and allows you to authenticate to the GitLab remote server without supplying your username or password each time.\n\nFor a more detailed explanation of how the SSH protocol works, we advise you to read [this nice tutorial by DigitalOcean](https://www.digitalocean.com/community/tutorials/understanding-the-ssh-encryption-and-connection-process).\n\n#### Locating an existing SSH key pair\n\nBefore generating a new SSH key check if your system already has one at the default location by opening a shell, or Command Prompt on Windows, and running the following command:\n\n###### Windows Command Prompt:\n````\ntype %userprofile%\\.ssh\\id_rsa.pub\n````\n\n###### GNU/Linux / macOS / PowerShell:\n````\ncat ~/.ssh/id_rsa.pub\n````\n\nIf you see a string starting with `ssh-rsa` you already have an SSH key pair and you can skip the next step **Generating a new SSH key pair** and continue onto **Copying your public SSH key to the clipboard**.\nIf you don't see the string or would like to generate a SSH key pair with a custom name continue onto the next step.\n\n#### Generating a new SSH key pair\n\n##### 1. To generate a new SSH key, use the following command:\n\n###### GNU/Linux / macOS:\n````\nssh-keygen -t rsa -C \"GitLab\" -b 4096\n````\n\n###### Windows:\nOn Windows you will need to download [PuttyGen](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html) and follow this [documentation article](https://the.earth.li/~sgtatham/putty/0.67/htmldoc/Chapter8.html#pubkey-puttygen) to generate a SSH key pair.\n\n##### 2. Next, you will be prompted to input a file path to save your key pair to.\n\nIf you don't already have an SSH key pair use the suggested path by pressing enter. Using the suggested path will allow your SSH client to automatically use the key pair with no additional configuration.\n\nIf you already have a key pair with the suggested file path, you will need to input a new file path and declare what host this key pair will be used for in your `.ssh/config` file, see **Working with non-default SSH key pair paths** for more information.\n\n##### 3. Once you have input a file path you will be prompted to input a password to secure your SSH key pair. It is a best practice to use a password for an SSH key pair, but it is not required and you can skip creating a password by pressing enter.\n> * Note: If you want to change the password of your key, you can use `ssh-keygen -p <keyname>`.\n\n##### 4. The next step is to copy the public key as we will need it afterwards. To copy your public key to the clipboard, use the appropriate code for your operating system below:\n\n###### macOS:\n````\npbcopy < ~/.ssh/id_rsa.pub\n````\n\n###### GNU/Linux (requires the xclip package):\n````\nxclip -sel clip < ~/.ssh/id_rsa.pub\n````\n\n###### Windows Command Line:\n````\ntype %userprofile%\\.ssh\\id_rsa.pub | clip\n````\n\n###### Windows PowerShell:\n````\ncat ~/.ssh/id_rsa.pub | clip\n````\n\n##### 5. The final step is to add your public SSH key to GitLab.\nNavigate to the 'SSH Keys' tab in you 'Profile Settings'. Paste your key in the 'Key' section and give it a relevant 'Title'. Use an identifiable title like 'Work Laptop - Windows 7' or 'Home MacBook Pro 15'.\n\nIf you manually copied your public SSH key make sure you copied the entire key starting with `ssh-rsa` and ending with your email.\n\n\n#### Working with non-default SSH key pair paths\nIf you used a non-default file path for your GitLab SSH key pair, you must configure your SSH client to find your GitLab SSH private key for connections to your GitLab server (perhaps gitlab.com).\n\nFor OpenSSH clients this is configured in the `~/.ssh/config file`.Below are two example host configurations using their own key:\n````\n# GitLab.com server\nHost gitlab.com\nRSAAuthentication yes\nIdentityFile ~/.ssh/config/private-key-filename-01\n\n# Private GitLab server\nHost gitlab.company.com\nRSAAuthentication yes\nIdentityFile ~/.ssh/config/private-key-filename\n````\nDue to the wide variety of SSH clients and their very large number of configuration options, further explanation of these topics is beyond the scope of this document.\n\nPublic SSH keys need to be unique, as they will bind to your account. Your SSH key is the only identifier you'll have when pushing code via SSH. That's why it needs to uniquely map to a single user.\n\n#### Deploy keys\nDeploy keys allow read-only access to multiple projects with a single SSH key.\n\nThis is really useful for cloning repositories to your Continuous Integration (CI) server. By using deploy keys, you don't have to setup a dummy user account.\n\nIf you are a project master or owner, you can add a deploy key in the project settings under the section 'Deploy Keys'. Press the 'New Deploy Key' button and upload a public SSH key. After this, the machine that uses the corresponding private key has read-only access to the project.\n\nYou can't add the same deploy key twice with the 'New Deploy Key' option. If you want to add the same key to another project, please enable it in the list that says 'Deploy keys from projects available to you'. All the deploy keys of all the projects you have access to are available. This project access can happen through being a direct member of the project, or through a group.\n\nDeploy keys can be shared between projects, you just need to add them to each project.\n\n#### Applications\n##### Eclipse\nHow to add your ssh key to Eclipse: [https://wiki.eclipse.org/EGit/User_Guide#Eclipse_SSH_Configuration](https://wiki.eclipse.org/EGit/User_Guide#Eclipse_SSH_Configuration)\n\nPs: copyright belongs to gitlab.com","source":"_posts/ssh-generate-SSH-key-and-deploy-it.md","raw":"title: Generate SSH key and How deploy it\ndate: 2019-05-29 19:21:12\ntop: false\nreward: false\ncomments: false\ntags: \n - SSH\n - git\n - 日记\n---\n\n#### SSH\n\nGit is a distributed version control system, which means you can work locally but you can also share or \"push\" your changes to other servers. Before you can push your changes to a GitLab server you need a secure communication channel for sharing information.\n\n<!-- more -->\n\nThe SSH protocol provides this security and allows you to authenticate to the GitLab remote server without supplying your username or password each time.\n\nFor a more detailed explanation of how the SSH protocol works, we advise you to read [this nice tutorial by DigitalOcean](https://www.digitalocean.com/community/tutorials/understanding-the-ssh-encryption-and-connection-process).\n\n#### Locating an existing SSH key pair\n\nBefore generating a new SSH key check if your system already has one at the default location by opening a shell, or Command Prompt on Windows, and running the following command:\n\n###### Windows Command Prompt:\n````\ntype %userprofile%\\.ssh\\id_rsa.pub\n````\n\n###### GNU/Linux / macOS / PowerShell:\n````\ncat ~/.ssh/id_rsa.pub\n````\n\nIf you see a string starting with `ssh-rsa` you already have an SSH key pair and you can skip the next step **Generating a new SSH key pair** and continue onto **Copying your public SSH key to the clipboard**.\nIf you don't see the string or would like to generate a SSH key pair with a custom name continue onto the next step.\n\n#### Generating a new SSH key pair\n\n##### 1. To generate a new SSH key, use the following command:\n\n###### GNU/Linux / macOS:\n````\nssh-keygen -t rsa -C \"GitLab\" -b 4096\n````\n\n###### Windows:\nOn Windows you will need to download [PuttyGen](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html) and follow this [documentation article](https://the.earth.li/~sgtatham/putty/0.67/htmldoc/Chapter8.html#pubkey-puttygen) to generate a SSH key pair.\n\n##### 2. Next, you will be prompted to input a file path to save your key pair to.\n\nIf you don't already have an SSH key pair use the suggested path by pressing enter. Using the suggested path will allow your SSH client to automatically use the key pair with no additional configuration.\n\nIf you already have a key pair with the suggested file path, you will need to input a new file path and declare what host this key pair will be used for in your `.ssh/config` file, see **Working with non-default SSH key pair paths** for more information.\n\n##### 3. Once you have input a file path you will be prompted to input a password to secure your SSH key pair. It is a best practice to use a password for an SSH key pair, but it is not required and you can skip creating a password by pressing enter.\n> * Note: If you want to change the password of your key, you can use `ssh-keygen -p <keyname>`.\n\n##### 4. The next step is to copy the public key as we will need it afterwards. To copy your public key to the clipboard, use the appropriate code for your operating system below:\n\n###### macOS:\n````\npbcopy < ~/.ssh/id_rsa.pub\n````\n\n###### GNU/Linux (requires the xclip package):\n````\nxclip -sel clip < ~/.ssh/id_rsa.pub\n````\n\n###### Windows Command Line:\n````\ntype %userprofile%\\.ssh\\id_rsa.pub | clip\n````\n\n###### Windows PowerShell:\n````\ncat ~/.ssh/id_rsa.pub | clip\n````\n\n##### 5. The final step is to add your public SSH key to GitLab.\nNavigate to the 'SSH Keys' tab in you 'Profile Settings'. Paste your key in the 'Key' section and give it a relevant 'Title'. Use an identifiable title like 'Work Laptop - Windows 7' or 'Home MacBook Pro 15'.\n\nIf you manually copied your public SSH key make sure you copied the entire key starting with `ssh-rsa` and ending with your email.\n\n\n#### Working with non-default SSH key pair paths\nIf you used a non-default file path for your GitLab SSH key pair, you must configure your SSH client to find your GitLab SSH private key for connections to your GitLab server (perhaps gitlab.com).\n\nFor OpenSSH clients this is configured in the `~/.ssh/config file`.Below are two example host configurations using their own key:\n````\n# GitLab.com server\nHost gitlab.com\nRSAAuthentication yes\nIdentityFile ~/.ssh/config/private-key-filename-01\n\n# Private GitLab server\nHost gitlab.company.com\nRSAAuthentication yes\nIdentityFile ~/.ssh/config/private-key-filename\n````\nDue to the wide variety of SSH clients and their very large number of configuration options, further explanation of these topics is beyond the scope of this document.\n\nPublic SSH keys need to be unique, as they will bind to your account. Your SSH key is the only identifier you'll have when pushing code via SSH. That's why it needs to uniquely map to a single user.\n\n#### Deploy keys\nDeploy keys allow read-only access to multiple projects with a single SSH key.\n\nThis is really useful for cloning repositories to your Continuous Integration (CI) server. By using deploy keys, you don't have to setup a dummy user account.\n\nIf you are a project master or owner, you can add a deploy key in the project settings under the section 'Deploy Keys'. Press the 'New Deploy Key' button and upload a public SSH key. After this, the machine that uses the corresponding private key has read-only access to the project.\n\nYou can't add the same deploy key twice with the 'New Deploy Key' option. If you want to add the same key to another project, please enable it in the list that says 'Deploy keys from projects available to you'. All the deploy keys of all the projects you have access to are available. This project access can happen through being a direct member of the project, or through a group.\n\nDeploy keys can be shared between projects, you just need to add them to each project.\n\n#### Applications\n##### Eclipse\nHow to add your ssh key to Eclipse: [https://wiki.eclipse.org/EGit/User_Guide#Eclipse_SSH_Configuration](https://wiki.eclipse.org/EGit/User_Guide#Eclipse_SSH_Configuration)\n\nPs: copyright belongs to gitlab.com","slug":"ssh-generate-SSH-key-and-deploy-it","published":1,"updated":"2019-06-09T17:21:12.953Z","layout":"post","photos":[],"link":"","_id":"cjwr0llvt000bbkvltjcgwnqp","content":"<h4 id=\"SSH\"><a href=\"#SSH\" class=\"headerlink\" title=\"SSH\"></a>SSH</h4><p>Git is a distributed version control system, which means you can work locally but you can also share or “push” your changes to other servers. Before you can push your changes to a GitLab server you need a secure communication channel for sharing information.</p>\n<a id=\"more\"></a>\n<p>The SSH protocol provides this security and allows you to authenticate to the GitLab remote server without supplying your username or password each time.</p>\n<p>For a more detailed explanation of how the SSH protocol works, we advise you to read <a href=\"https://www.digitalocean.com/community/tutorials/understanding-the-ssh-encryption-and-connection-process\" target=\"_blank\" rel=\"noopener\">this nice tutorial by DigitalOcean</a>.</p>\n<h4 id=\"Locating-an-existing-SSH-key-pair\"><a href=\"#Locating-an-existing-SSH-key-pair\" class=\"headerlink\" title=\"Locating an existing SSH key pair\"></a>Locating an existing SSH key pair</h4><p>Before generating a new SSH key check if your system already has one at the default location by opening a shell, or Command Prompt on Windows, and running the following command:</p>\n<h6 id=\"Windows-Command-Prompt\"><a href=\"#Windows-Command-Prompt\" class=\"headerlink\" title=\"Windows Command Prompt:\"></a>Windows Command Prompt:</h6><figure class=\"highlight taggerscript\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">type <span class=\"variable\">%userprofile%</span><span class=\"symbol\">\\.</span>ssh<span class=\"symbol\">\\i</span>d_rsa.pub</span><br></pre></td></tr></table></figure>\n<h6 id=\"GNU-Linux-macOS-PowerShell\"><a href=\"#GNU-Linux-macOS-PowerShell\" class=\"headerlink\" title=\"GNU/Linux / macOS / PowerShell:\"></a>GNU/Linux / macOS / PowerShell:</h6><figure class=\"highlight awk\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">cat ~<span class=\"regexp\">/.ssh/i</span>d_rsa.pub</span><br></pre></td></tr></table></figure>\n<p>If you see a string starting with <code>ssh-rsa</code> you already have an SSH key pair and you can skip the next step <strong>Generating a new SSH key pair</strong> and continue onto <strong>Copying your public SSH key to the clipboard</strong>.<br>If you don’t see the string or would like to generate a SSH key pair with a custom name continue onto the next step.</p>\n<h4 id=\"Generating-a-new-SSH-key-pair\"><a href=\"#Generating-a-new-SSH-key-pair\" class=\"headerlink\" title=\"Generating a new SSH key pair\"></a>Generating a new SSH key pair</h4><h5 id=\"1-To-generate-a-new-SSH-key-use-the-following-command\"><a href=\"#1-To-generate-a-new-SSH-key-use-the-following-command\" class=\"headerlink\" title=\"1. To generate a new SSH key, use the following command:\"></a>1. To generate a new SSH key, use the following command:</h5><h6 id=\"GNU-Linux-macOS\"><a href=\"#GNU-Linux-macOS\" class=\"headerlink\" title=\"GNU/Linux / macOS:\"></a>GNU/Linux / macOS:</h6><figure class=\"highlight stylus\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">ssh-keygen -t rsa -C <span class=\"string\">\"GitLab\"</span> -<span class=\"selector-tag\">b</span> <span class=\"number\">4096</span></span><br></pre></td></tr></table></figure>\n<h6 id=\"Windows\"><a href=\"#Windows\" class=\"headerlink\" title=\"Windows:\"></a>Windows:</h6><p>On Windows you will need to download <a href=\"http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html\" target=\"_blank\" rel=\"noopener\">PuttyGen</a> and follow this <a href=\"https://the.earth.li/~sgtatham/putty/0.67/htmldoc/Chapter8.html#pubkey-puttygen\" target=\"_blank\" rel=\"noopener\">documentation article</a> to generate a SSH key pair.</p>\n<h5 id=\"2-Next-you-will-be-prompted-to-input-a-file-path-to-save-your-key-pair-to\"><a href=\"#2-Next-you-will-be-prompted-to-input-a-file-path-to-save-your-key-pair-to\" class=\"headerlink\" title=\"2. Next, you will be prompted to input a file path to save your key pair to.\"></a>2. Next, you will be prompted to input a file path to save your key pair to.</h5><p>If you don’t already have an SSH key pair use the suggested path by pressing enter. Using the suggested path will allow your SSH client to automatically use the key pair with no additional configuration.</p>\n<p>If you already have a key pair with the suggested file path, you will need to input a new file path and declare what host this key pair will be used for in your <code>.ssh/config</code> file, see <strong>Working with non-default SSH key pair paths</strong> for more information.</p>\n<h5 id=\"3-Once-you-have-input-a-file-path-you-will-be-prompted-to-input-a-password-to-secure-your-SSH-key-pair-It-is-a-best-practice-to-use-a-password-for-an-SSH-key-pair-but-it-is-not-required-and-you-can-skip-creating-a-password-by-pressing-enter\"><a href=\"#3-Once-you-have-input-a-file-path-you-will-be-prompted-to-input-a-password-to-secure-your-SSH-key-pair-It-is-a-best-practice-to-use-a-password-for-an-SSH-key-pair-but-it-is-not-required-and-you-can-skip-creating-a-password-by-pressing-enter\" class=\"headerlink\" title=\"3. Once you have input a file path you will be prompted to input a password to secure your SSH key pair. It is a best practice to use a password for an SSH key pair, but it is not required and you can skip creating a password by pressing enter.\"></a>3. Once you have input a file path you will be prompted to input a password to secure your SSH key pair. It is a best practice to use a password for an SSH key pair, but it is not required and you can skip creating a password by pressing enter.</h5><blockquote>\n<ul>\n<li>Note: If you want to change the password of your key, you can use <code>ssh-keygen -p <keyname></code>.</li>\n</ul>\n</blockquote>\n<h5 id=\"4-The-next-step-is-to-copy-the-public-key-as-we-will-need-it-afterwards-To-copy-your-public-key-to-the-clipboard-use-the-appropriate-code-for-your-operating-system-below\"><a href=\"#4-The-next-step-is-to-copy-the-public-key-as-we-will-need-it-afterwards-To-copy-your-public-key-to-the-clipboard-use-the-appropriate-code-for-your-operating-system-below\" class=\"headerlink\" title=\"4. The next step is to copy the public key as we will need it afterwards. To copy your public key to the clipboard, use the appropriate code for your operating system below:\"></a>4. The next step is to copy the public key as we will need it afterwards. To copy your public key to the clipboard, use the appropriate code for your operating system below:</h5><h6 id=\"macOS\"><a href=\"#macOS\" class=\"headerlink\" title=\"macOS:\"></a>macOS:</h6><figure class=\"highlight awk\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">pbcopy < ~<span class=\"regexp\">/.ssh/i</span>d_rsa.pub</span><br></pre></td></tr></table></figure>\n<h6 id=\"GNU-Linux-requires-the-xclip-package\"><a href=\"#GNU-Linux-requires-the-xclip-package\" class=\"headerlink\" title=\"GNU/Linux (requires the xclip package):\"></a>GNU/Linux (requires the xclip package):</h6><figure class=\"highlight armasm\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"symbol\">xclip</span> -<span class=\"keyword\">sel </span>clip < ~/.ssh/id_rsa.pub</span><br></pre></td></tr></table></figure>\n<h6 id=\"Windows-Command-Line\"><a href=\"#Windows-Command-Line\" class=\"headerlink\" title=\"Windows Command Line:\"></a>Windows Command Line:</h6><figure class=\"highlight mel\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">type %userprofile%\\.ssh\\id_rsa.pub | <span class=\"keyword\">clip</span></span><br></pre></td></tr></table></figure>\n<h6 id=\"Windows-PowerShell\"><a href=\"#Windows-PowerShell\" class=\"headerlink\" title=\"Windows PowerShell:\"></a>Windows PowerShell:</h6><figure class=\"highlight stata\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">cat</span> ~/.ssh/id_rsa.pub | <span class=\"built_in\">clip</span></span><br></pre></td></tr></table></figure>\n<h5 id=\"5-The-final-step-is-to-add-your-public-SSH-key-to-GitLab\"><a href=\"#5-The-final-step-is-to-add-your-public-SSH-key-to-GitLab\" class=\"headerlink\" title=\"5. The final step is to add your public SSH key to GitLab.\"></a>5. The final step is to add your public SSH key to GitLab.</h5><p>Navigate to the ‘SSH Keys’ tab in you ‘Profile Settings’. Paste your key in the ‘Key’ section and give it a relevant ‘Title’. Use an identifiable title like ‘Work Laptop - Windows 7’ or ‘Home MacBook Pro 15’.</p>\n<p>If you manually copied your public SSH key make sure you copied the entire key starting with <code>ssh-rsa</code> and ending with your email.</p>\n<h4 id=\"Working-with-non-default-SSH-key-pair-paths\"><a href=\"#Working-with-non-default-SSH-key-pair-paths\" class=\"headerlink\" title=\"Working with non-default SSH key pair paths\"></a>Working with non-default SSH key pair paths</h4><p>If you used a non-default file path for your GitLab SSH key pair, you must configure your SSH client to find your GitLab SSH private key for connections to your GitLab server (perhaps gitlab.com).</p>\n<p>For OpenSSH clients this is configured in the <code>~/.ssh/config file</code>.Below are two example host configurations using their own key:<br><figure class=\"highlight yaml\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\"># GitLab.com server</span></span><br><span class=\"line\"><span class=\"string\">Host</span> <span class=\"string\">gitlab.com</span></span><br><span class=\"line\"><span class=\"string\">RSAAuthentication</span> <span class=\"literal\">yes</span></span><br><span class=\"line\"><span class=\"string\">IdentityFile</span> <span class=\"string\">~/.ssh/config/private-key-filename-01</span></span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\"># Private GitLab server</span></span><br><span class=\"line\"><span class=\"string\">Host</span> <span class=\"string\">gitlab.company.com</span></span><br><span class=\"line\"><span class=\"string\">RSAAuthentication</span> <span class=\"literal\">yes</span></span><br><span class=\"line\"><span class=\"string\">IdentityFile</span> <span class=\"string\">~/.ssh/config/private-key-filename</span></span><br></pre></td></tr></table></figure></p>\n<p>Due to the wide variety of SSH clients and their very large number of configuration options, further explanation of these topics is beyond the scope of this document.</p>\n<p>Public SSH keys need to be unique, as they will bind to your account. Your SSH key is the only identifier you’ll have when pushing code via SSH. That’s why it needs to uniquely map to a single user.</p>\n<h4 id=\"Deploy-keys\"><a href=\"#Deploy-keys\" class=\"headerlink\" title=\"Deploy keys\"></a>Deploy keys</h4><p>Deploy keys allow read-only access to multiple projects with a single SSH key.</p>\n<p>This is really useful for cloning repositories to your Continuous Integration (CI) server. By using deploy keys, you don’t have to setup a dummy user account.</p>\n<p>If you are a project master or owner, you can add a deploy key in the project settings under the section ‘Deploy Keys’. Press the ‘New Deploy Key’ button and upload a public SSH key. After this, the machine that uses the corresponding private key has read-only access to the project.</p>\n<p>You can’t add the same deploy key twice with the ‘New Deploy Key’ option. If you want to add the same key to another project, please enable it in the list that says ‘Deploy keys from projects available to you’. All the deploy keys of all the projects you have access to are available. This project access can happen through being a direct member of the project, or through a group.</p>\n<p>Deploy keys can be shared between projects, you just need to add them to each project.</p>\n<h4 id=\"Applications\"><a href=\"#Applications\" class=\"headerlink\" title=\"Applications\"></a>Applications</h4><h5 id=\"Eclipse\"><a href=\"#Eclipse\" class=\"headerlink\" title=\"Eclipse\"></a>Eclipse</h5><p>How to add your ssh key to Eclipse: <a href=\"https://wiki.eclipse.org/EGit/User_Guide#Eclipse_SSH_Configuration\" target=\"_blank\" rel=\"noopener\">https://wiki.eclipse.org/EGit/User_Guide#Eclipse_SSH_Configuration</a></p>\n<p>Ps: copyright belongs to gitlab.com</p>\n","site":{"data":{}},"excerpt":"<h4 id=\"SSH\"><a href=\"#SSH\" class=\"headerlink\" title=\"SSH\"></a>SSH</h4><p>Git is a distributed version control system, which means you can work locally but you can also share or “push” your changes to other servers. Before you can push your changes to a GitLab server you need a secure communication channel for sharing information.</p>","more":"<p>The SSH protocol provides this security and allows you to authenticate to the GitLab remote server without supplying your username or password each time.</p>\n<p>For a more detailed explanation of how the SSH protocol works, we advise you to read <a href=\"https://www.digitalocean.com/community/tutorials/understanding-the-ssh-encryption-and-connection-process\" target=\"_blank\" rel=\"noopener\">this nice tutorial by DigitalOcean</a>.</p>\n<h4 id=\"Locating-an-existing-SSH-key-pair\"><a href=\"#Locating-an-existing-SSH-key-pair\" class=\"headerlink\" title=\"Locating an existing SSH key pair\"></a>Locating an existing SSH key pair</h4><p>Before generating a new SSH key check if your system already has one at the default location by opening a shell, or Command Prompt on Windows, and running the following command:</p>\n<h6 id=\"Windows-Command-Prompt\"><a href=\"#Windows-Command-Prompt\" class=\"headerlink\" title=\"Windows Command Prompt:\"></a>Windows Command Prompt:</h6><figure class=\"highlight taggerscript\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">type <span class=\"variable\">%userprofile%</span><span class=\"symbol\">\\.</span>ssh<span class=\"symbol\">\\i</span>d_rsa.pub</span><br></pre></td></tr></table></figure>\n<h6 id=\"GNU-Linux-macOS-PowerShell\"><a href=\"#GNU-Linux-macOS-PowerShell\" class=\"headerlink\" title=\"GNU/Linux / macOS / PowerShell:\"></a>GNU/Linux / macOS / PowerShell:</h6><figure class=\"highlight awk\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">cat ~<span class=\"regexp\">/.ssh/i</span>d_rsa.pub</span><br></pre></td></tr></table></figure>\n<p>If you see a string starting with <code>ssh-rsa</code> you already have an SSH key pair and you can skip the next step <strong>Generating a new SSH key pair</strong> and continue onto <strong>Copying your public SSH key to the clipboard</strong>.<br>If you don’t see the string or would like to generate a SSH key pair with a custom name continue onto the next step.</p>\n<h4 id=\"Generating-a-new-SSH-key-pair\"><a href=\"#Generating-a-new-SSH-key-pair\" class=\"headerlink\" title=\"Generating a new SSH key pair\"></a>Generating a new SSH key pair</h4><h5 id=\"1-To-generate-a-new-SSH-key-use-the-following-command\"><a href=\"#1-To-generate-a-new-SSH-key-use-the-following-command\" class=\"headerlink\" title=\"1. To generate a new SSH key, use the following command:\"></a>1. To generate a new SSH key, use the following command:</h5><h6 id=\"GNU-Linux-macOS\"><a href=\"#GNU-Linux-macOS\" class=\"headerlink\" title=\"GNU/Linux / macOS:\"></a>GNU/Linux / macOS:</h6><figure class=\"highlight stylus\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">ssh-keygen -t rsa -C <span class=\"string\">\"GitLab\"</span> -<span class=\"selector-tag\">b</span> <span class=\"number\">4096</span></span><br></pre></td></tr></table></figure>\n<h6 id=\"Windows\"><a href=\"#Windows\" class=\"headerlink\" title=\"Windows:\"></a>Windows:</h6><p>On Windows you will need to download <a href=\"http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html\" target=\"_blank\" rel=\"noopener\">PuttyGen</a> and follow this <a href=\"https://the.earth.li/~sgtatham/putty/0.67/htmldoc/Chapter8.html#pubkey-puttygen\" target=\"_blank\" rel=\"noopener\">documentation article</a> to generate a SSH key pair.</p>\n<h5 id=\"2-Next-you-will-be-prompted-to-input-a-file-path-to-save-your-key-pair-to\"><a href=\"#2-Next-you-will-be-prompted-to-input-a-file-path-to-save-your-key-pair-to\" class=\"headerlink\" title=\"2. Next, you will be prompted to input a file path to save your key pair to.\"></a>2. Next, you will be prompted to input a file path to save your key pair to.</h5><p>If you don’t already have an SSH key pair use the suggested path by pressing enter. Using the suggested path will allow your SSH client to automatically use the key pair with no additional configuration.</p>\n<p>If you already have a key pair with the suggested file path, you will need to input a new file path and declare what host this key pair will be used for in your <code>.ssh/config</code> file, see <strong>Working with non-default SSH key pair paths</strong> for more information.</p>\n<h5 id=\"3-Once-you-have-input-a-file-path-you-will-be-prompted-to-input-a-password-to-secure-your-SSH-key-pair-It-is-a-best-practice-to-use-a-password-for-an-SSH-key-pair-but-it-is-not-required-and-you-can-skip-creating-a-password-by-pressing-enter\"><a href=\"#3-Once-you-have-input-a-file-path-you-will-be-prompted-to-input-a-password-to-secure-your-SSH-key-pair-It-is-a-best-practice-to-use-a-password-for-an-SSH-key-pair-but-it-is-not-required-and-you-can-skip-creating-a-password-by-pressing-enter\" class=\"headerlink\" title=\"3. Once you have input a file path you will be prompted to input a password to secure your SSH key pair. It is a best practice to use a password for an SSH key pair, but it is not required and you can skip creating a password by pressing enter.\"></a>3. Once you have input a file path you will be prompted to input a password to secure your SSH key pair. It is a best practice to use a password for an SSH key pair, but it is not required and you can skip creating a password by pressing enter.</h5><blockquote>\n<ul>\n<li>Note: If you want to change the password of your key, you can use <code>ssh-keygen -p <keyname></code>.</li>\n</ul>\n</blockquote>\n<h5 id=\"4-The-next-step-is-to-copy-the-public-key-as-we-will-need-it-afterwards-To-copy-your-public-key-to-the-clipboard-use-the-appropriate-code-for-your-operating-system-below\"><a href=\"#4-The-next-step-is-to-copy-the-public-key-as-we-will-need-it-afterwards-To-copy-your-public-key-to-the-clipboard-use-the-appropriate-code-for-your-operating-system-below\" class=\"headerlink\" title=\"4. The next step is to copy the public key as we will need it afterwards. To copy your public key to the clipboard, use the appropriate code for your operating system below:\"></a>4. The next step is to copy the public key as we will need it afterwards. To copy your public key to the clipboard, use the appropriate code for your operating system below:</h5><h6 id=\"macOS\"><a href=\"#macOS\" class=\"headerlink\" title=\"macOS:\"></a>macOS:</h6><figure class=\"highlight awk\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">pbcopy < ~<span class=\"regexp\">/.ssh/i</span>d_rsa.pub</span><br></pre></td></tr></table></figure>\n<h6 id=\"GNU-Linux-requires-the-xclip-package\"><a href=\"#GNU-Linux-requires-the-xclip-package\" class=\"headerlink\" title=\"GNU/Linux (requires the xclip package):\"></a>GNU/Linux (requires the xclip package):</h6><figure class=\"highlight armasm\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"symbol\">xclip</span> -<span class=\"keyword\">sel </span>clip < ~/.ssh/id_rsa.pub</span><br></pre></td></tr></table></figure>\n<h6 id=\"Windows-Command-Line\"><a href=\"#Windows-Command-Line\" class=\"headerlink\" title=\"Windows Command Line:\"></a>Windows Command Line:</h6><figure class=\"highlight mel\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">type %userprofile%\\.ssh\\id_rsa.pub | <span class=\"keyword\">clip</span></span><br></pre></td></tr></table></figure>\n<h6 id=\"Windows-PowerShell\"><a href=\"#Windows-PowerShell\" class=\"headerlink\" title=\"Windows PowerShell:\"></a>Windows PowerShell:</h6><figure class=\"highlight stata\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">cat</span> ~/.ssh/id_rsa.pub | <span class=\"built_in\">clip</span></span><br></pre></td></tr></table></figure>\n<h5 id=\"5-The-final-step-is-to-add-your-public-SSH-key-to-GitLab\"><a href=\"#5-The-final-step-is-to-add-your-public-SSH-key-to-GitLab\" class=\"headerlink\" title=\"5. The final step is to add your public SSH key to GitLab.\"></a>5. The final step is to add your public SSH key to GitLab.</h5><p>Navigate to the ‘SSH Keys’ tab in you ‘Profile Settings’. Paste your key in the ‘Key’ section and give it a relevant ‘Title’. Use an identifiable title like ‘Work Laptop - Windows 7’ or ‘Home MacBook Pro 15’.</p>\n<p>If you manually copied your public SSH key make sure you copied the entire key starting with <code>ssh-rsa</code> and ending with your email.</p>\n<h4 id=\"Working-with-non-default-SSH-key-pair-paths\"><a href=\"#Working-with-non-default-SSH-key-pair-paths\" class=\"headerlink\" title=\"Working with non-default SSH key pair paths\"></a>Working with non-default SSH key pair paths</h4><p>If you used a non-default file path for your GitLab SSH key pair, you must configure your SSH client to find your GitLab SSH private key for connections to your GitLab server (perhaps gitlab.com).</p>\n<p>For OpenSSH clients this is configured in the <code>~/.ssh/config file</code>.Below are two example host configurations using their own key:<br><figure class=\"highlight yaml\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"comment\"># GitLab.com server</span></span><br><span class=\"line\"><span class=\"string\">Host</span> <span class=\"string\">gitlab.com</span></span><br><span class=\"line\"><span class=\"string\">RSAAuthentication</span> <span class=\"literal\">yes</span></span><br><span class=\"line\"><span class=\"string\">IdentityFile</span> <span class=\"string\">~/.ssh/config/private-key-filename-01</span></span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\"># Private GitLab server</span></span><br><span class=\"line\"><span class=\"string\">Host</span> <span class=\"string\">gitlab.company.com</span></span><br><span class=\"line\"><span class=\"string\">RSAAuthentication</span> <span class=\"literal\">yes</span></span><br><span class=\"line\"><span class=\"string\">IdentityFile</span> <span class=\"string\">~/.ssh/config/private-key-filename</span></span><br></pre></td></tr></table></figure></p>\n<p>Due to the wide variety of SSH clients and their very large number of configuration options, further explanation of these topics is beyond the scope of this document.</p>\n<p>Public SSH keys need to be unique, as they will bind to your account. Your SSH key is the only identifier you’ll have when pushing code via SSH. That’s why it needs to uniquely map to a single user.</p>\n<h4 id=\"Deploy-keys\"><a href=\"#Deploy-keys\" class=\"headerlink\" title=\"Deploy keys\"></a>Deploy keys</h4><p>Deploy keys allow read-only access to multiple projects with a single SSH key.</p>\n<p>This is really useful for cloning repositories to your Continuous Integration (CI) server. By using deploy keys, you don’t have to setup a dummy user account.</p>\n<p>If you are a project master or owner, you can add a deploy key in the project settings under the section ‘Deploy Keys’. Press the ‘New Deploy Key’ button and upload a public SSH key. After this, the machine that uses the corresponding private key has read-only access to the project.</p>\n<p>You can’t add the same deploy key twice with the ‘New Deploy Key’ option. If you want to add the same key to another project, please enable it in the list that says ‘Deploy keys from projects available to you’. All the deploy keys of all the projects you have access to are available. This project access can happen through being a direct member of the project, or through a group.</p>\n<p>Deploy keys can be shared between projects, you just need to add them to each project.</p>\n<h4 id=\"Applications\"><a href=\"#Applications\" class=\"headerlink\" title=\"Applications\"></a>Applications</h4><h5 id=\"Eclipse\"><a href=\"#Eclipse\" class=\"headerlink\" title=\"Eclipse\"></a>Eclipse</h5><p>How to add your ssh key to Eclipse: <a href=\"https://wiki.eclipse.org/EGit/User_Guide#Eclipse_SSH_Configuration\" target=\"_blank\" rel=\"noopener\">https://wiki.eclipse.org/EGit/User_Guide#Eclipse_SSH_Configuration</a></p>\n<p>Ps: copyright belongs to gitlab.com</p>"},{"title":"辨析 Sass 中的 Map 和 List","date":"2015-10-21T02:34:12.000Z","_content":"\n如果你使用过 Sass 3.3 之前的版本,那么你一定对那段时光颇有感触,那时候没有现如今这么好的条件,那时候的 Map 还只能用多重列表(lists of list)来模拟。多重列表可以实现复杂数据的嵌套定义,但却不是以键值对的形式实现的,所有当我们需要获取其中特定的某一项时就会比较麻烦。Map 这种数据类型天生就是基于键值对的形式,非常便于组织数据。\n\n自从可以使用 Map 之后,开发者们开始毫无顾忌地定义 Map 存储数据,比如断点宽度、颜色值、栅格布局等等响应式排版的细节,都被一股脑的塞进了 Map 中。\n\n那么,有了 Map 之后,我们还有必要使用 List 吗?可能某些人会觉得为了保持向后兼容应该继续使用多重列表模拟 Map,因为可能有些开发者仍然在使用老版本的 Sass 编译器,但实际上,这是多此一举了,Sass 的版本通常由 `package.json` 或者其他同类型的项目配置文件所控制,往往只需一条命令(`gem update sass`)即可更新 Sass 的版本,因此基本上无需考虑对老版本的兼容问题。\n\n<!-- more -->\n\n使用多重列表替代 Map 的优势之一就是减少代码量。下面让我们来比较一下多种列表和 Map 的语法结构以及遍历方式。\n\n## 测试表格\n\nVariable | Description\n--- | ---\n`site` | Sitewide information.\n`page` | Page specific information and custom variables set in front-matter.\n`config` | Site configuration\n`theme` | Theme configuration. Inherits from site configuration.\n`_` (single underscore) | [Lodash](http://lodash.com/) library\n`path` | Path of current page\n`url` | Full URL of current page\n`env` | Environment variables\n\n\n## 语法比较\n\n<div class=\"note\">\n <h5>测试标题</h5>\n <p>在下面的示例中,我创建了一个用于控制响应式布局的数据,该数据一共有四个断点,每一个断点都包含了 `min-width`、`max-width`、`font-size` 和 `line-height` 四个样式。</p>\n</div>\n\n#### Map 语法\n\n下面就是使用 Map 存储的数据,具体来说,该 Map 中首先存储了四个用于标识断点的 Key,相对应的是保存具体属性值得 Value。虽然这种形式可读性更高,但是总体代码量却高达 26 行 450 个字符。\n\n```sass\n$breakpoint-map: (\n small: (\n min-width: null,\n max-width: 479px,\n base-font: 16px,\n vertical-rhythm: 1.3\n ),\n medium: (\n min-width: 480px,\n max-width: 959px,\n base-font: 18px,\n vertical-rhythm: 1.414\n ),\n large: (\n min-width: 960px,\n max-width: 1099px,\n base-font: 18px,\n vertical-rhythm: 1.5\n ),\n xlarge: (\n min-width: 1100px,\n max-width: null,\n base-font: 21px,\n vertical-rhythm: 1.618\n )\n);\n```\n\n#### 多重列表语法\n\n下面的多重列表存储了和上面 Map 同样的数据,在多重列表中没有 Key-Value 的对应关系,这意味着要想找到特定的值,必须使用遍历或 `nth()` 的方式来实现了。从另一个角度来看,多种列表又比 Map 的代码量小得多,总共只有六行 180 个字符。\n\n```sass\n$breakpoint-list: (\n (small, null, 479px, 16px, 1.3),\n (medium, 480px, 959px, 18px, 1.414),\n (large, 960px, 1099px, 18px, 1.5),\n (xlarge, 1100px, null, 21px, 1.618)\n);\n```\n\n## 遍历比较\n\n<div class=\"note info\">\n <h5>测试标题</h5>\n <p>从上面简单地比较中可以粗略的看出,多种列表的代码量明显少于 Map。但是,如果我们需要遍历这些值得话,复杂度又是怎样的呢?</p>\n</div>\n\n#### 遍历 Map\n\n我们可以使用如下的代码遍历 Map:\n\n```sass\n@each $label, $map in $breakpoint-map {}\n```\n\n这里的变量 `$label` 和 `$map` 会随着对 `$breakpoint-map` 的遍历被动态地赋值,`$label` 将会被赋值为 `$breakpoint-map` 的 Key,而 `$map` 会被赋值为 `$breakpoint-map` 的 Value。为了在遍历过程中获取特定值,我们就需要使用 Sass 原生的 `map-get()` 函数,使用该函数需要传入两个参数:Map 的名字和求取的 Key,最后返回该 Map 中匹配该 Key 的 Value。\n\n具体的做法就是使用 `@each` 遍历 Map,然后使用 `map-get()` 获取特定值,最终只需要六行代码 220 个字符即可完成整个遍历:\n\n```sass\n@each $label, $map in $breakpoint-map {\n $min-width: map-get($map, min-width);\n $max-width: map-get($map, max-width);\n $base-font: map-get($map, base-font);\n $vertical-rhythm: map-get($map, vertical-rhythm);\n}\n```\n\n#### 遍历多重列表\n\n遍历多重列表不必像遍历 Map 一样动态获取到 Map 后再使用 `map-get()` 函数取特定值,直接遍历一遍即可获得特定值。\n\n因为多种列表内层的每一个列表结构相同,都有按照相同顺序排列的五个值,所以我们可以持续遍历每个值并赋值给特定的变量。无需调用 `map-get()`,直接引用这些变量即可进行赋值等裸机操作。最终遍历多重列表只使用了两行代码 100 个字符:\n\n```sass\n@each $label, $min-width, $max-width, $base-font, $vertical-rhythm in $breakpoint-list {\n}\n```\n\n## 慎用多重列表\n\n<div class=\"note warning\">\n <h5>测试标题</h5>\n <p>经过上述的比对,看起来多重列表各方面都在碾压 Map,实则不然,Sass 中添加 Map 有一条非常重要的原因就是:Key-Value 的映射关系。</p>\n</div>\n\n#### 遗漏键值\n\n如果要使用多重列表,那么就必须保证自己非常熟悉多重列表内部的每一项所代表的意义。下面我们举个例子,来看看遗漏了某些值的情况:\n\n```sass\n$breakpoint-list: (\n (small, null, 479px, 16px, 1.3),\n (medium, 480px, 959px, 18px, 1.414),\n (large, 960px, 1099px, 18px, 1.5),\n (xlarge, 1100px, 21px, 1.618)\n);\n\np {\n @each $label, $min-width, $max-width, $base-font, $vertical-rhythm in $breakpoint-list {\n @if $min-width {\n @include breakpoint( $min-width ) {\n font-size: $base-font;\n line-height: $vertical-rhythm;\n }\n } @else {\n font-size: $base-font;\n line-height: $vertical-rhythm;\n }\n }\n}\n```\n\n当我们尝试运行这段代码时,结果肯定是错误地,因为在 `$breakpoint-list` 的最后一行,`xlarge` 被赋值给了 `$label`,`1100px` 被赋值给了 `$min-width`,`21px` 被赋值给了 `$max-width`, `1.618` 被赋值给了 `$base-font`,最终导致 `$vertical-rhythm` 没有被赋值,结果就是 `font-size` 的属性值是错的,`line-height` 的属性值是空的。此外,Sass 还不会对此抛出错误,导致我们无从知晓错误所在。\n\n如果我们使用 Map 来代替这里的多重列表,那么使用 `map-get()` 函数即使遇见空值也能正确获得想要的结果。这就是值得我们慎重思考的地方:多种列表虽然简单快速,但是丧失了 Map 中的容错能力和快速取值能力。\n\n#### 查找特定列表\n\n在多重列表中查找特定列表简直就是一种折磨。如果使用 Map,那么配合 `map-get()` 函数可以快速定位到特定子 Map:\n\n```sass\n$medium-map: map-get($maps, medium);\n```\n\n但如果要获取多种列表 `medium` 列表,麻烦可就大了:\n\n```sass\n@function get-list($label) {\n @each $list in $breakpoint-list {\n @if nth($list, 1) == $label {\n @return $list;\n }\n }\n @return null;\n}\n$medium-list: get-list(medium);\n```\n\n这段代码的逻辑就是遍历整个多重列表,知道找到第一个匹配项,然后返回,如果一直没有找到匹配项,就一直遍历到末尾,然后返回 `null`。这实际上就是手工实现了 `map-get()` 的逻辑。\n\n#### 缺少原生的 Map 函数\n\nSass 提供了诸多的原生函数用于处理 Map 数据类型,但是多重列表是没法调用这些函数的,比如,使用 `map-merge()` 可以合并两个 Map,如果两个 Map 有相同的值,则取第二个 Map 的值为最终值。当然你也可以在多重列表中使用 `join()` 或 `append()` 来增加新列表,从而模拟出 `map-merge()` 的效果。\n\n另一个实用的 Map 函数就是 `map-has-key()`,对于依赖 `map-get()` 的自定义函数来说,`map-has-key()` 可以用来验证特定的 Key 是否存在。但在列表中是完全没有相似的方法。\n\n## 总结\n\n<div class=\"note unreleased\">\n <h5>Test Title</h5>\n <p>相比起列表来说,Key-Value 模型的 Map 显然更有力量,原生的 Sass Map 函数更是提供了强力的数据查找和验证工具。</p>\n</div>\n\n虽然多重列表代码量少,但并不能像 Map 一样进行错误检查或验证参数。在大多数时候,相比较多重列表而言,我相信 Map 是更好的选择。如果是为了更少的代码量和其他简单地调用,那么我偶尔会用用多重列表,但是从项目的宏观控制和数据存储方面显然更优秀。\n","source":"_posts/demo.md","raw":"title: 辨析 Sass 中的 Map 和 List\ndate: 2015-10-21 10:34:12\ntags:\n- css\n---\n\n如果你使用过 Sass 3.3 之前的版本,那么你一定对那段时光颇有感触,那时候没有现如今这么好的条件,那时候的 Map 还只能用多重列表(lists of list)来模拟。多重列表可以实现复杂数据的嵌套定义,但却不是以键值对的形式实现的,所有当我们需要获取其中特定的某一项时就会比较麻烦。Map 这种数据类型天生就是基于键值对的形式,非常便于组织数据。\n\n自从可以使用 Map 之后,开发者们开始毫无顾忌地定义 Map 存储数据,比如断点宽度、颜色值、栅格布局等等响应式排版的细节,都被一股脑的塞进了 Map 中。\n\n那么,有了 Map 之后,我们还有必要使用 List 吗?可能某些人会觉得为了保持向后兼容应该继续使用多重列表模拟 Map,因为可能有些开发者仍然在使用老版本的 Sass 编译器,但实际上,这是多此一举了,Sass 的版本通常由 `package.json` 或者其他同类型的项目配置文件所控制,往往只需一条命令(`gem update sass`)即可更新 Sass 的版本,因此基本上无需考虑对老版本的兼容问题。\n\n<!-- more -->\n\n使用多重列表替代 Map 的优势之一就是减少代码量。下面让我们来比较一下多种列表和 Map 的语法结构以及遍历方式。\n\n## 测试表格\n\nVariable | Description\n--- | ---\n`site` | Sitewide information.\n`page` | Page specific information and custom variables set in front-matter.\n`config` | Site configuration\n`theme` | Theme configuration. Inherits from site configuration.\n`_` (single underscore) | [Lodash](http://lodash.com/) library\n`path` | Path of current page\n`url` | Full URL of current page\n`env` | Environment variables\n\n\n## 语法比较\n\n<div class=\"note\">\n <h5>测试标题</h5>\n <p>在下面的示例中,我创建了一个用于控制响应式布局的数据,该数据一共有四个断点,每一个断点都包含了 `min-width`、`max-width`、`font-size` 和 `line-height` 四个样式。</p>\n</div>\n\n#### Map 语法\n\n下面就是使用 Map 存储的数据,具体来说,该 Map 中首先存储了四个用于标识断点的 Key,相对应的是保存具体属性值得 Value。虽然这种形式可读性更高,但是总体代码量却高达 26 行 450 个字符。\n\n```sass\n$breakpoint-map: (\n small: (\n min-width: null,\n max-width: 479px,\n base-font: 16px,\n vertical-rhythm: 1.3\n ),\n medium: (\n min-width: 480px,\n max-width: 959px,\n base-font: 18px,\n vertical-rhythm: 1.414\n ),\n large: (\n min-width: 960px,\n max-width: 1099px,\n base-font: 18px,\n vertical-rhythm: 1.5\n ),\n xlarge: (\n min-width: 1100px,\n max-width: null,\n base-font: 21px,\n vertical-rhythm: 1.618\n )\n);\n```\n\n#### 多重列表语法\n\n下面的多重列表存储了和上面 Map 同样的数据,在多重列表中没有 Key-Value 的对应关系,这意味着要想找到特定的值,必须使用遍历或 `nth()` 的方式来实现了。从另一个角度来看,多种列表又比 Map 的代码量小得多,总共只有六行 180 个字符。\n\n```sass\n$breakpoint-list: (\n (small, null, 479px, 16px, 1.3),\n (medium, 480px, 959px, 18px, 1.414),\n (large, 960px, 1099px, 18px, 1.5),\n (xlarge, 1100px, null, 21px, 1.618)\n);\n```\n\n## 遍历比较\n\n<div class=\"note info\">\n <h5>测试标题</h5>\n <p>从上面简单地比较中可以粗略的看出,多种列表的代码量明显少于 Map。但是,如果我们需要遍历这些值得话,复杂度又是怎样的呢?</p>\n</div>\n\n#### 遍历 Map\n\n我们可以使用如下的代码遍历 Map:\n\n```sass\n@each $label, $map in $breakpoint-map {}\n```\n\n这里的变量 `$label` 和 `$map` 会随着对 `$breakpoint-map` 的遍历被动态地赋值,`$label` 将会被赋值为 `$breakpoint-map` 的 Key,而 `$map` 会被赋值为 `$breakpoint-map` 的 Value。为了在遍历过程中获取特定值,我们就需要使用 Sass 原生的 `map-get()` 函数,使用该函数需要传入两个参数:Map 的名字和求取的 Key,最后返回该 Map 中匹配该 Key 的 Value。\n\n具体的做法就是使用 `@each` 遍历 Map,然后使用 `map-get()` 获取特定值,最终只需要六行代码 220 个字符即可完成整个遍历:\n\n```sass\n@each $label, $map in $breakpoint-map {\n $min-width: map-get($map, min-width);\n $max-width: map-get($map, max-width);\n $base-font: map-get($map, base-font);\n $vertical-rhythm: map-get($map, vertical-rhythm);\n}\n```\n\n#### 遍历多重列表\n\n遍历多重列表不必像遍历 Map 一样动态获取到 Map 后再使用 `map-get()` 函数取特定值,直接遍历一遍即可获得特定值。\n\n因为多种列表内层的每一个列表结构相同,都有按照相同顺序排列的五个值,所以我们可以持续遍历每个值并赋值给特定的变量。无需调用 `map-get()`,直接引用这些变量即可进行赋值等裸机操作。最终遍历多重列表只使用了两行代码 100 个字符:\n\n```sass\n@each $label, $min-width, $max-width, $base-font, $vertical-rhythm in $breakpoint-list {\n}\n```\n\n## 慎用多重列表\n\n<div class=\"note warning\">\n <h5>测试标题</h5>\n <p>经过上述的比对,看起来多重列表各方面都在碾压 Map,实则不然,Sass 中添加 Map 有一条非常重要的原因就是:Key-Value 的映射关系。</p>\n</div>\n\n#### 遗漏键值\n\n如果要使用多重列表,那么就必须保证自己非常熟悉多重列表内部的每一项所代表的意义。下面我们举个例子,来看看遗漏了某些值的情况:\n\n```sass\n$breakpoint-list: (\n (small, null, 479px, 16px, 1.3),\n (medium, 480px, 959px, 18px, 1.414),\n (large, 960px, 1099px, 18px, 1.5),\n (xlarge, 1100px, 21px, 1.618)\n);\n\np {\n @each $label, $min-width, $max-width, $base-font, $vertical-rhythm in $breakpoint-list {\n @if $min-width {\n @include breakpoint( $min-width ) {\n font-size: $base-font;\n line-height: $vertical-rhythm;\n }\n } @else {\n font-size: $base-font;\n line-height: $vertical-rhythm;\n }\n }\n}\n```\n\n当我们尝试运行这段代码时,结果肯定是错误地,因为在 `$breakpoint-list` 的最后一行,`xlarge` 被赋值给了 `$label`,`1100px` 被赋值给了 `$min-width`,`21px` 被赋值给了 `$max-width`, `1.618` 被赋值给了 `$base-font`,最终导致 `$vertical-rhythm` 没有被赋值,结果就是 `font-size` 的属性值是错的,`line-height` 的属性值是空的。此外,Sass 还不会对此抛出错误,导致我们无从知晓错误所在。\n\n如果我们使用 Map 来代替这里的多重列表,那么使用 `map-get()` 函数即使遇见空值也能正确获得想要的结果。这就是值得我们慎重思考的地方:多种列表虽然简单快速,但是丧失了 Map 中的容错能力和快速取值能力。\n\n#### 查找特定列表\n\n在多重列表中查找特定列表简直就是一种折磨。如果使用 Map,那么配合 `map-get()` 函数可以快速定位到特定子 Map:\n\n```sass\n$medium-map: map-get($maps, medium);\n```\n\n但如果要获取多种列表 `medium` 列表,麻烦可就大了:\n\n```sass\n@function get-list($label) {\n @each $list in $breakpoint-list {\n @if nth($list, 1) == $label {\n @return $list;\n }\n }\n @return null;\n}\n$medium-list: get-list(medium);\n```\n\n这段代码的逻辑就是遍历整个多重列表,知道找到第一个匹配项,然后返回,如果一直没有找到匹配项,就一直遍历到末尾,然后返回 `null`。这实际上就是手工实现了 `map-get()` 的逻辑。\n\n#### 缺少原生的 Map 函数\n\nSass 提供了诸多的原生函数用于处理 Map 数据类型,但是多重列表是没法调用这些函数的,比如,使用 `map-merge()` 可以合并两个 Map,如果两个 Map 有相同的值,则取第二个 Map 的值为最终值。当然你也可以在多重列表中使用 `join()` 或 `append()` 来增加新列表,从而模拟出 `map-merge()` 的效果。\n\n另一个实用的 Map 函数就是 `map-has-key()`,对于依赖 `map-get()` 的自定义函数来说,`map-has-key()` 可以用来验证特定的 Key 是否存在。但在列表中是完全没有相似的方法。\n\n## 总结\n\n<div class=\"note unreleased\">\n <h5>Test Title</h5>\n <p>相比起列表来说,Key-Value 模型的 Map 显然更有力量,原生的 Sass Map 函数更是提供了强力的数据查找和验证工具。</p>\n</div>\n\n虽然多重列表代码量少,但并不能像 Map 一样进行错误检查或验证参数。在大多数时候,相比较多重列表而言,我相信 Map 是更好的选择。如果是为了更少的代码量和其他简单地调用,那么我偶尔会用用多重列表,但是从项目的宏观控制和数据存储方面显然更优秀。\n","slug":"demo","published":1,"updated":"2019-04-08T13:41:38.046Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cjwr0llwe001mbkvldlqzqsc8","content":"<p>如果你使用过 Sass 3.3 之前的版本,那么你一定对那段时光颇有感触,那时候没有现如今这么好的条件,那时候的 Map 还只能用多重列表(lists of list)来模拟。多重列表可以实现复杂数据的嵌套定义,但却不是以键值对的形式实现的,所有当我们需要获取其中特定的某一项时就会比较麻烦。Map 这种数据类型天生就是基于键值对的形式,非常便于组织数据。</p>\n<p>自从可以使用 Map 之后,开发者们开始毫无顾忌地定义 Map 存储数据,比如断点宽度、颜色值、栅格布局等等响应式排版的细节,都被一股脑的塞进了 Map 中。</p>\n<p>那么,有了 Map 之后,我们还有必要使用 List 吗?可能某些人会觉得为了保持向后兼容应该继续使用多重列表模拟 Map,因为可能有些开发者仍然在使用老版本的 Sass 编译器,但实际上,这是多此一举了,Sass 的版本通常由 <code>package.json</code> 或者其他同类型的项目配置文件所控制,往往只需一条命令(<code>gem update sass</code>)即可更新 Sass 的版本,因此基本上无需考虑对老版本的兼容问题。</p>\n<a id=\"more\"></a>\n<p>使用多重列表替代 Map 的优势之一就是减少代码量。下面让我们来比较一下多种列表和 Map 的语法结构以及遍历方式。</p>\n<h2 id=\"测试表格\"><a href=\"#测试表格\" class=\"headerlink\" title=\"测试表格\"></a>测试表格</h2><table>\n<thead>\n<tr>\n<th>Variable</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>site</code></td>\n<td>Sitewide information.</td>\n</tr>\n<tr>\n<td><code>page</code></td>\n<td>Page specific information and custom variables set in front-matter.</td>\n</tr>\n<tr>\n<td><code>config</code></td>\n<td>Site configuration</td>\n</tr>\n<tr>\n<td><code>theme</code></td>\n<td>Theme configuration. Inherits from site configuration.</td>\n</tr>\n<tr>\n<td><code>_</code> (single underscore)</td>\n<td><a href=\"http://lodash.com/\" target=\"_blank\" rel=\"noopener\">Lodash</a> library</td>\n</tr>\n<tr>\n<td><code>path</code></td>\n<td>Path of current page</td>\n</tr>\n<tr>\n<td><code>url</code></td>\n<td>Full URL of current page</td>\n</tr>\n<tr>\n<td><code>env</code></td>\n<td>Environment variables</td>\n</tr>\n</tbody>\n</table>\n<h2 id=\"语法比较\"><a href=\"#语法比较\" class=\"headerlink\" title=\"语法比较\"></a>语法比较</h2><div class=\"note\"><br> <h5>测试标题</h5><br> <p>在下面的示例中,我创建了一个用于控制响应式布局的数据,该数据一共有四个断点,每一个断点都包含了 <code>min-width</code>、<code>max-width</code>、<code>font-size</code> 和 <code>line-height</code> 四个样式。</p><br></div>\n\n<h4 id=\"Map-语法\"><a href=\"#Map-语法\" class=\"headerlink\" title=\"Map 语法\"></a>Map 语法</h4><p>下面就是使用 Map 存储的数据,具体来说,该 Map 中首先存储了四个用于标识断点的 Key,相对应的是保存具体属性值得 Value。虽然这种形式可读性更高,但是总体代码量却高达 26 行 450 个字符。</p>\n<figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">$breakpoint-map: (</span><br><span class=\"line\"> small: (</span><br><span class=\"line\"> min-width: null,</span><br><span class=\"line\"> max-width: 479px,</span><br><span class=\"line\"> base-font: 16px,</span><br><span class=\"line\"> vertical-rhythm: 1.3</span><br><span class=\"line\"> ),</span><br><span class=\"line\"> medium: (</span><br><span class=\"line\"> min-width: 480px,</span><br><span class=\"line\"> max-width: 959px,</span><br><span class=\"line\"> base-font: 18px,</span><br><span class=\"line\"> vertical-rhythm: 1.414</span><br><span class=\"line\"> ),</span><br><span class=\"line\"> large: (</span><br><span class=\"line\"> min-width: 960px,</span><br><span class=\"line\"> max-width: 1099px,</span><br><span class=\"line\"> base-font: 18px,</span><br><span class=\"line\"> vertical-rhythm: 1.5</span><br><span class=\"line\"> ),</span><br><span class=\"line\"> xlarge: (</span><br><span class=\"line\"> min-width: 1100px,</span><br><span class=\"line\"> max-width: null,</span><br><span class=\"line\"> base-font: 21px,</span><br><span class=\"line\"> vertical-rhythm: 1.618</span><br><span class=\"line\"> )</span><br><span class=\"line\">);</span><br></pre></td></tr></table></figure>\n<h4 id=\"多重列表语法\"><a href=\"#多重列表语法\" class=\"headerlink\" title=\"多重列表语法\"></a>多重列表语法</h4><p>下面的多重列表存储了和上面 Map 同样的数据,在多重列表中没有 Key-Value 的对应关系,这意味着要想找到特定的值,必须使用遍历或 <code>nth()</code> 的方式来实现了。从另一个角度来看,多种列表又比 Map 的代码量小得多,总共只有六行 180 个字符。</p>\n<figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">$breakpoint-list: (</span><br><span class=\"line\"> (small, null, 479px, 16px, 1.3),</span><br><span class=\"line\"> (medium, 480px, 959px, 18px, 1.414),</span><br><span class=\"line\"> (large, 960px, 1099px, 18px, 1.5),</span><br><span class=\"line\"> (xlarge, 1100px, null, 21px, 1.618)</span><br><span class=\"line\">);</span><br></pre></td></tr></table></figure>\n<h2 id=\"遍历比较\"><a href=\"#遍历比较\" class=\"headerlink\" title=\"遍历比较\"></a>遍历比较</h2><div class=\"note info\"><br> <h5>测试标题</h5><br> <p>从上面简单地比较中可以粗略的看出,多种列表的代码量明显少于 Map。但是,如果我们需要遍历这些值得话,复杂度又是怎样的呢?</p><br></div>\n\n<h4 id=\"遍历-Map\"><a href=\"#遍历-Map\" class=\"headerlink\" title=\"遍历 Map\"></a>遍历 Map</h4><p>我们可以使用如下的代码遍历 Map:</p>\n<figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">@each $label, $map in $breakpoint-map {}</span><br></pre></td></tr></table></figure>\n<p>这里的变量 <code>$label</code> 和 <code>$map</code> 会随着对 <code>$breakpoint-map</code> 的遍历被动态地赋值,<code>$label</code> 将会被赋值为 <code>$breakpoint-map</code> 的 Key,而 <code>$map</code> 会被赋值为 <code>$breakpoint-map</code> 的 Value。为了在遍历过程中获取特定值,我们就需要使用 Sass 原生的 <code>map-get()</code> 函数,使用该函数需要传入两个参数:Map 的名字和求取的 Key,最后返回该 Map 中匹配该 Key 的 Value。</p>\n<p>具体的做法就是使用 <code>@each</code> 遍历 Map,然后使用 <code>map-get()</code> 获取特定值,最终只需要六行代码 220 个字符即可完成整个遍历:</p>\n<figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">@each $label, $map in $breakpoint-map {</span><br><span class=\"line\"> $min-width: map-get($map, min-width);</span><br><span class=\"line\"> $max-width: map-get($map, max-width);</span><br><span class=\"line\"> $base-font: map-get($map, base-font);</span><br><span class=\"line\"> $vertical-rhythm: map-get($map, vertical-rhythm);</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<h4 id=\"遍历多重列表\"><a href=\"#遍历多重列表\" class=\"headerlink\" title=\"遍历多重列表\"></a>遍历多重列表</h4><p>遍历多重列表不必像遍历 Map 一样动态获取到 Map 后再使用 <code>map-get()</code> 函数取特定值,直接遍历一遍即可获得特定值。</p>\n<p>因为多种列表内层的每一个列表结构相同,都有按照相同顺序排列的五个值,所以我们可以持续遍历每个值并赋值给特定的变量。无需调用 <code>map-get()</code>,直接引用这些变量即可进行赋值等裸机操作。最终遍历多重列表只使用了两行代码 100 个字符:</p>\n<figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">@each $label, $min-width, $max-width, $base-font, $vertical-rhythm in $breakpoint-list {</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<h2 id=\"慎用多重列表\"><a href=\"#慎用多重列表\" class=\"headerlink\" title=\"慎用多重列表\"></a>慎用多重列表</h2><div class=\"note warning\"><br> <h5>测试标题</h5><br> <p>经过上述的比对,看起来多重列表各方面都在碾压 Map,实则不然,Sass 中添加 Map 有一条非常重要的原因就是:Key-Value 的映射关系。</p><br></div>\n\n<h4 id=\"遗漏键值\"><a href=\"#遗漏键值\" class=\"headerlink\" title=\"遗漏键值\"></a>遗漏键值</h4><p>如果要使用多重列表,那么就必须保证自己非常熟悉多重列表内部的每一项所代表的意义。下面我们举个例子,来看看遗漏了某些值的情况:</p>\n<figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">$breakpoint-list: (</span><br><span class=\"line\"> (small, null, 479px, 16px, 1.3),</span><br><span class=\"line\"> (medium, 480px, 959px, 18px, 1.414),</span><br><span class=\"line\"> (large, 960px, 1099px, 18px, 1.5),</span><br><span class=\"line\"> (xlarge, 1100px, 21px, 1.618)</span><br><span class=\"line\">);</span><br><span class=\"line\"></span><br><span class=\"line\">p {</span><br><span class=\"line\"> @each $label, $min-width, $max-width, $base-font, $vertical-rhythm in $breakpoint-list {</span><br><span class=\"line\"> @if $min-width {</span><br><span class=\"line\"> @include breakpoint( $min-width ) {</span><br><span class=\"line\"> font-size: $base-font;</span><br><span class=\"line\"> line-height: $vertical-rhythm;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> } @else {</span><br><span class=\"line\"> font-size: $base-font;</span><br><span class=\"line\"> line-height: $vertical-rhythm;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<p>当我们尝试运行这段代码时,结果肯定是错误地,因为在 <code>$breakpoint-list</code> 的最后一行,<code>xlarge</code> 被赋值给了 <code>$label</code>,<code>1100px</code> 被赋值给了 <code>$min-width</code>,<code>21px</code> 被赋值给了 <code>$max-width</code>, <code>1.618</code> 被赋值给了 <code>$base-font</code>,最终导致 <code>$vertical-rhythm</code> 没有被赋值,结果就是 <code>font-size</code> 的属性值是错的,<code>line-height</code> 的属性值是空的。此外,Sass 还不会对此抛出错误,导致我们无从知晓错误所在。</p>\n<p>如果我们使用 Map 来代替这里的多重列表,那么使用 <code>map-get()</code> 函数即使遇见空值也能正确获得想要的结果。这就是值得我们慎重思考的地方:多种列表虽然简单快速,但是丧失了 Map 中的容错能力和快速取值能力。</p>\n<h4 id=\"查找特定列表\"><a href=\"#查找特定列表\" class=\"headerlink\" title=\"查找特定列表\"></a>查找特定列表</h4><p>在多重列表中查找特定列表简直就是一种折磨。如果使用 Map,那么配合 <code>map-get()</code> 函数可以快速定位到特定子 Map:</p>\n<figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">$medium-map: map-get($maps, medium);</span><br></pre></td></tr></table></figure>\n<p>但如果要获取多种列表 <code>medium</code> 列表,麻烦可就大了:</p>\n<figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">@function get-list($label) {</span><br><span class=\"line\"> @each $list in $breakpoint-list {</span><br><span class=\"line\"> @if nth($list, 1) == $label {</span><br><span class=\"line\"> @return $list;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"> @return null;</span><br><span class=\"line\">}</span><br><span class=\"line\">$medium-list: get-list(medium);</span><br></pre></td></tr></table></figure>\n<p>这段代码的逻辑就是遍历整个多重列表,知道找到第一个匹配项,然后返回,如果一直没有找到匹配项,就一直遍历到末尾,然后返回 <code>null</code>。这实际上就是手工实现了 <code>map-get()</code> 的逻辑。</p>\n<h4 id=\"缺少原生的-Map-函数\"><a href=\"#缺少原生的-Map-函数\" class=\"headerlink\" title=\"缺少原生的 Map 函数\"></a>缺少原生的 Map 函数</h4><p>Sass 提供了诸多的原生函数用于处理 Map 数据类型,但是多重列表是没法调用这些函数的,比如,使用 <code>map-merge()</code> 可以合并两个 Map,如果两个 Map 有相同的值,则取第二个 Map 的值为最终值。当然你也可以在多重列表中使用 <code>join()</code> 或 <code>append()</code> 来增加新列表,从而模拟出 <code>map-merge()</code> 的效果。</p>\n<p>另一个实用的 Map 函数就是 <code>map-has-key()</code>,对于依赖 <code>map-get()</code> 的自定义函数来说,<code>map-has-key()</code> 可以用来验证特定的 Key 是否存在。但在列表中是完全没有相似的方法。</p>\n<h2 id=\"总结\"><a href=\"#总结\" class=\"headerlink\" title=\"总结\"></a>总结</h2><div class=\"note unreleased\"><br> <h5>Test Title</h5><br> <p>相比起列表来说,Key-Value 模型的 Map 显然更有力量,原生的 Sass Map 函数更是提供了强力的数据查找和验证工具。</p><br></div>\n\n<p>虽然多重列表代码量少,但并不能像 Map 一样进行错误检查或验证参数。在大多数时候,相比较多重列表而言,我相信 Map 是更好的选择。如果是为了更少的代码量和其他简单地调用,那么我偶尔会用用多重列表,但是从项目的宏观控制和数据存储方面显然更优秀。</p>\n","site":{"data":{}},"excerpt":"<p>如果你使用过 Sass 3.3 之前的版本,那么你一定对那段时光颇有感触,那时候没有现如今这么好的条件,那时候的 Map 还只能用多重列表(lists of list)来模拟。多重列表可以实现复杂数据的嵌套定义,但却不是以键值对的形式实现的,所有当我们需要获取其中特定的某一项时就会比较麻烦。Map 这种数据类型天生就是基于键值对的形式,非常便于组织数据。</p>\n<p>自从可以使用 Map 之后,开发者们开始毫无顾忌地定义 Map 存储数据,比如断点宽度、颜色值、栅格布局等等响应式排版的细节,都被一股脑的塞进了 Map 中。</p>\n<p>那么,有了 Map 之后,我们还有必要使用 List 吗?可能某些人会觉得为了保持向后兼容应该继续使用多重列表模拟 Map,因为可能有些开发者仍然在使用老版本的 Sass 编译器,但实际上,这是多此一举了,Sass 的版本通常由 <code>package.json</code> 或者其他同类型的项目配置文件所控制,往往只需一条命令(<code>gem update sass</code>)即可更新 Sass 的版本,因此基本上无需考虑对老版本的兼容问题。</p>","more":"<p>使用多重列表替代 Map 的优势之一就是减少代码量。下面让我们来比较一下多种列表和 Map 的语法结构以及遍历方式。</p>\n<h2 id=\"测试表格\"><a href=\"#测试表格\" class=\"headerlink\" title=\"测试表格\"></a>测试表格</h2><table>\n<thead>\n<tr>\n<th>Variable</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>site</code></td>\n<td>Sitewide information.</td>\n</tr>\n<tr>\n<td><code>page</code></td>\n<td>Page specific information and custom variables set in front-matter.</td>\n</tr>\n<tr>\n<td><code>config</code></td>\n<td>Site configuration</td>\n</tr>\n<tr>\n<td><code>theme</code></td>\n<td>Theme configuration. Inherits from site configuration.</td>\n</tr>\n<tr>\n<td><code>_</code> (single underscore)</td>\n<td><a href=\"http://lodash.com/\" target=\"_blank\" rel=\"noopener\">Lodash</a> library</td>\n</tr>\n<tr>\n<td><code>path</code></td>\n<td>Path of current page</td>\n</tr>\n<tr>\n<td><code>url</code></td>\n<td>Full URL of current page</td>\n</tr>\n<tr>\n<td><code>env</code></td>\n<td>Environment variables</td>\n</tr>\n</tbody>\n</table>\n<h2 id=\"语法比较\"><a href=\"#语法比较\" class=\"headerlink\" title=\"语法比较\"></a>语法比较</h2><div class=\"note\"><br> <h5>测试标题</h5><br> <p>在下面的示例中,我创建了一个用于控制响应式布局的数据,该数据一共有四个断点,每一个断点都包含了 <code>min-width</code>、<code>max-width</code>、<code>font-size</code> 和 <code>line-height</code> 四个样式。</p><br></div>\n\n<h4 id=\"Map-语法\"><a href=\"#Map-语法\" class=\"headerlink\" title=\"Map 语法\"></a>Map 语法</h4><p>下面就是使用 Map 存储的数据,具体来说,该 Map 中首先存储了四个用于标识断点的 Key,相对应的是保存具体属性值得 Value。虽然这种形式可读性更高,但是总体代码量却高达 26 行 450 个字符。</p>\n<figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">$breakpoint-map: (</span><br><span class=\"line\"> small: (</span><br><span class=\"line\"> min-width: null,</span><br><span class=\"line\"> max-width: 479px,</span><br><span class=\"line\"> base-font: 16px,</span><br><span class=\"line\"> vertical-rhythm: 1.3</span><br><span class=\"line\"> ),</span><br><span class=\"line\"> medium: (</span><br><span class=\"line\"> min-width: 480px,</span><br><span class=\"line\"> max-width: 959px,</span><br><span class=\"line\"> base-font: 18px,</span><br><span class=\"line\"> vertical-rhythm: 1.414</span><br><span class=\"line\"> ),</span><br><span class=\"line\"> large: (</span><br><span class=\"line\"> min-width: 960px,</span><br><span class=\"line\"> max-width: 1099px,</span><br><span class=\"line\"> base-font: 18px,</span><br><span class=\"line\"> vertical-rhythm: 1.5</span><br><span class=\"line\"> ),</span><br><span class=\"line\"> xlarge: (</span><br><span class=\"line\"> min-width: 1100px,</span><br><span class=\"line\"> max-width: null,</span><br><span class=\"line\"> base-font: 21px,</span><br><span class=\"line\"> vertical-rhythm: 1.618</span><br><span class=\"line\"> )</span><br><span class=\"line\">);</span><br></pre></td></tr></table></figure>\n<h4 id=\"多重列表语法\"><a href=\"#多重列表语法\" class=\"headerlink\" title=\"多重列表语法\"></a>多重列表语法</h4><p>下面的多重列表存储了和上面 Map 同样的数据,在多重列表中没有 Key-Value 的对应关系,这意味着要想找到特定的值,必须使用遍历或 <code>nth()</code> 的方式来实现了。从另一个角度来看,多种列表又比 Map 的代码量小得多,总共只有六行 180 个字符。</p>\n<figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">$breakpoint-list: (</span><br><span class=\"line\"> (small, null, 479px, 16px, 1.3),</span><br><span class=\"line\"> (medium, 480px, 959px, 18px, 1.414),</span><br><span class=\"line\"> (large, 960px, 1099px, 18px, 1.5),</span><br><span class=\"line\"> (xlarge, 1100px, null, 21px, 1.618)</span><br><span class=\"line\">);</span><br></pre></td></tr></table></figure>\n<h2 id=\"遍历比较\"><a href=\"#遍历比较\" class=\"headerlink\" title=\"遍历比较\"></a>遍历比较</h2><div class=\"note info\"><br> <h5>测试标题</h5><br> <p>从上面简单地比较中可以粗略的看出,多种列表的代码量明显少于 Map。但是,如果我们需要遍历这些值得话,复杂度又是怎样的呢?</p><br></div>\n\n<h4 id=\"遍历-Map\"><a href=\"#遍历-Map\" class=\"headerlink\" title=\"遍历 Map\"></a>遍历 Map</h4><p>我们可以使用如下的代码遍历 Map:</p>\n<figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">@each $label, $map in $breakpoint-map {}</span><br></pre></td></tr></table></figure>\n<p>这里的变量 <code>$label</code> 和 <code>$map</code> 会随着对 <code>$breakpoint-map</code> 的遍历被动态地赋值,<code>$label</code> 将会被赋值为 <code>$breakpoint-map</code> 的 Key,而 <code>$map</code> 会被赋值为 <code>$breakpoint-map</code> 的 Value。为了在遍历过程中获取特定值,我们就需要使用 Sass 原生的 <code>map-get()</code> 函数,使用该函数需要传入两个参数:Map 的名字和求取的 Key,最后返回该 Map 中匹配该 Key 的 Value。</p>\n<p>具体的做法就是使用 <code>@each</code> 遍历 Map,然后使用 <code>map-get()</code> 获取特定值,最终只需要六行代码 220 个字符即可完成整个遍历:</p>\n<figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">@each $label, $map in $breakpoint-map {</span><br><span class=\"line\"> $min-width: map-get($map, min-width);</span><br><span class=\"line\"> $max-width: map-get($map, max-width);</span><br><span class=\"line\"> $base-font: map-get($map, base-font);</span><br><span class=\"line\"> $vertical-rhythm: map-get($map, vertical-rhythm);</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<h4 id=\"遍历多重列表\"><a href=\"#遍历多重列表\" class=\"headerlink\" title=\"遍历多重列表\"></a>遍历多重列表</h4><p>遍历多重列表不必像遍历 Map 一样动态获取到 Map 后再使用 <code>map-get()</code> 函数取特定值,直接遍历一遍即可获得特定值。</p>\n<p>因为多种列表内层的每一个列表结构相同,都有按照相同顺序排列的五个值,所以我们可以持续遍历每个值并赋值给特定的变量。无需调用 <code>map-get()</code>,直接引用这些变量即可进行赋值等裸机操作。最终遍历多重列表只使用了两行代码 100 个字符:</p>\n<figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">@each $label, $min-width, $max-width, $base-font, $vertical-rhythm in $breakpoint-list {</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<h2 id=\"慎用多重列表\"><a href=\"#慎用多重列表\" class=\"headerlink\" title=\"慎用多重列表\"></a>慎用多重列表</h2><div class=\"note warning\"><br> <h5>测试标题</h5><br> <p>经过上述的比对,看起来多重列表各方面都在碾压 Map,实则不然,Sass 中添加 Map 有一条非常重要的原因就是:Key-Value 的映射关系。</p><br></div>\n\n<h4 id=\"遗漏键值\"><a href=\"#遗漏键值\" class=\"headerlink\" title=\"遗漏键值\"></a>遗漏键值</h4><p>如果要使用多重列表,那么就必须保证自己非常熟悉多重列表内部的每一项所代表的意义。下面我们举个例子,来看看遗漏了某些值的情况:</p>\n<figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">$breakpoint-list: (</span><br><span class=\"line\"> (small, null, 479px, 16px, 1.3),</span><br><span class=\"line\"> (medium, 480px, 959px, 18px, 1.414),</span><br><span class=\"line\"> (large, 960px, 1099px, 18px, 1.5),</span><br><span class=\"line\"> (xlarge, 1100px, 21px, 1.618)</span><br><span class=\"line\">);</span><br><span class=\"line\"></span><br><span class=\"line\">p {</span><br><span class=\"line\"> @each $label, $min-width, $max-width, $base-font, $vertical-rhythm in $breakpoint-list {</span><br><span class=\"line\"> @if $min-width {</span><br><span class=\"line\"> @include breakpoint( $min-width ) {</span><br><span class=\"line\"> font-size: $base-font;</span><br><span class=\"line\"> line-height: $vertical-rhythm;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> } @else {</span><br><span class=\"line\"> font-size: $base-font;</span><br><span class=\"line\"> line-height: $vertical-rhythm;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\">}</span><br></pre></td></tr></table></figure>\n<p>当我们尝试运行这段代码时,结果肯定是错误地,因为在 <code>$breakpoint-list</code> 的最后一行,<code>xlarge</code> 被赋值给了 <code>$label</code>,<code>1100px</code> 被赋值给了 <code>$min-width</code>,<code>21px</code> 被赋值给了 <code>$max-width</code>, <code>1.618</code> 被赋值给了 <code>$base-font</code>,最终导致 <code>$vertical-rhythm</code> 没有被赋值,结果就是 <code>font-size</code> 的属性值是错的,<code>line-height</code> 的属性值是空的。此外,Sass 还不会对此抛出错误,导致我们无从知晓错误所在。</p>\n<p>如果我们使用 Map 来代替这里的多重列表,那么使用 <code>map-get()</code> 函数即使遇见空值也能正确获得想要的结果。这就是值得我们慎重思考的地方:多种列表虽然简单快速,但是丧失了 Map 中的容错能力和快速取值能力。</p>\n<h4 id=\"查找特定列表\"><a href=\"#查找特定列表\" class=\"headerlink\" title=\"查找特定列表\"></a>查找特定列表</h4><p>在多重列表中查找特定列表简直就是一种折磨。如果使用 Map,那么配合 <code>map-get()</code> 函数可以快速定位到特定子 Map:</p>\n<figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">$medium-map: map-get($maps, medium);</span><br></pre></td></tr></table></figure>\n<p>但如果要获取多种列表 <code>medium</code> 列表,麻烦可就大了:</p>\n<figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">@function get-list($label) {</span><br><span class=\"line\"> @each $list in $breakpoint-list {</span><br><span class=\"line\"> @if nth($list, 1) == $label {</span><br><span class=\"line\"> @return $list;</span><br><span class=\"line\"> }</span><br><span class=\"line\"> }</span><br><span class=\"line\"> @return null;</span><br><span class=\"line\">}</span><br><span class=\"line\">$medium-list: get-list(medium);</span><br></pre></td></tr></table></figure>\n<p>这段代码的逻辑就是遍历整个多重列表,知道找到第一个匹配项,然后返回,如果一直没有找到匹配项,就一直遍历到末尾,然后返回 <code>null</code>。这实际上就是手工实现了 <code>map-get()</code> 的逻辑。</p>\n<h4 id=\"缺少原生的-Map-函数\"><a href=\"#缺少原生的-Map-函数\" class=\"headerlink\" title=\"缺少原生的 Map 函数\"></a>缺少原生的 Map 函数</h4><p>Sass 提供了诸多的原生函数用于处理 Map 数据类型,但是多重列表是没法调用这些函数的,比如,使用 <code>map-merge()</code> 可以合并两个 Map,如果两个 Map 有相同的值,则取第二个 Map 的值为最终值。当然你也可以在多重列表中使用 <code>join()</code> 或 <code>append()</code> 来增加新列表,从而模拟出 <code>map-merge()</code> 的效果。</p>\n<p>另一个实用的 Map 函数就是 <code>map-has-key()</code>,对于依赖 <code>map-get()</code> 的自定义函数来说,<code>map-has-key()</code> 可以用来验证特定的 Key 是否存在。但在列表中是完全没有相似的方法。</p>\n<h2 id=\"总结\"><a href=\"#总结\" class=\"headerlink\" title=\"总结\"></a>总结</h2><div class=\"note unreleased\"><br> <h5>Test Title</h5><br> <p>相比起列表来说,Key-Value 模型的 Map 显然更有力量,原生的 Sass Map 函数更是提供了强力的数据查找和验证工具。</p><br></div>\n\n<p>虽然多重列表代码量少,但并不能像 Map 一样进行错误检查或验证参数。在大多数时候,相比较多重列表而言,我相信 Map 是更好的选择。如果是为了更少的代码量和其他简单地调用,那么我偶尔会用用多重列表,但是从项目的宏观控制和数据存储方面显然更优秀。</p>"},{"title":"【HTTP】http协议学习","date":"2019-04-09T14:08:32.000Z","_content":"\n### HTTP协议\nHTTP协议是客户端和 服务器端之间数据传输的格式规范,格式简称为“超文本传输协议”全称为“Hyper Text Transfer Protocol”。\n\n<!-- more -->\n### HTTP协议历史版本\n- `HTTP 1.0`: 默认是基于短连接,即非持久连接,采用文本数据格式传输;单个tcp连接仅能维护传输单个Web对象。\n > 无host域\n\n<br/>\n\n- `HTTP 1.1`: 默认是基于长连接,即持久连接(可配置成非持久连接),采用文本数据格式传输;单个tcp连接可以维护多个Web对象的传输。\n > HTTP 1.1可获取host域这个参数。\n > 长连接可有效减少TCP三次握手四次挥手的网络连接性能开销。\n > HTTP 1.1支持只发送header信息,不带任何body信息。可以校验B/S架构模式下,Browser是否具有访问请求权限,有则返回`status: 100`,无则返回`status: 401`,从而再依返回结果将请求的body信息发送给服务器,节约网络带宽。\n > HTTP 1.1同时也支持资源加载的断点续传。\n > 同一时间对于同一域名,请求数量有限制,超过限制会造成网络阻塞请求。\n \n<br/>\n\n- `HTTP 2.0`: 采用二进制数据格式传输;实现多路复用;进行头部压缩优化。[HTTP1.1与HTTP2.0区别Demo](https://http2.akamai.com/demo)\n > ⭐️ HTTP 2.0采用*多路复用(Multiplexing)* 用以解决*线头阻塞* 的问题。核心基于Google公司开发的基于TCP的应用层协议[SPDY非标准协议](https://zh.wikipedia.org/wiki/SPDY)\n > ⭐️ 增加\"二进制分帧层\"实现底层tcp多路复用。将多个请求在同一个TCP连接上完成,承载任意数量的双向数据流。极大的提升了传输层通信性能。\n > HTTP 2.0采用首部压缩设计的[HPACK](http://http2.github.io/http2-spec/compression.html)算法\n > HTTP 2.0的Server Push以及缓存策略\n \n<br/>\n\n- `HTTP 3.0`: 核心基于UDP传输层协议的[QUIC协议](https://zhuanlan.zhihu.com/p/32553477);提供数据传输的高可靠性及0-RTT延迟;彻底解决*线头阻塞* \n > 基于UDP传输层协议,实现高速及高可靠性的数据传输。\n > 彻底解决http1.1遗留的线头阻塞(HOL),实现不同流的数据传输相互独立传输,互不干扰。\n > 0-RTT,不必像TCP那样需要三次握手。\n > 参考:\n [QUIC协议浅析与HTTP/3.0](https://www.jianshu.com/p/bb3eeb36b479) \n [如何看待 HTTP/3 ?](https://www.zhihu.com/question/302412059/answer/533223530)\n### HTTP/2协议\n1、二进制分帧层 (Binary Framing Layer)\n2、在单个TCP连接里多路复用请求。\n3、HTTP/2的Server Push,非常重要的一个特性。\n4、HTTP Header的压缩,采用的是HPack算法。\n5、应用层的重置连接\n6、请求优先级设置\n7、流量控制\n8、HTTP/1 的几种优化可以弃用\n参考:https://blog.wangriyu.wang/2018/05-HTTP2.html\n\n### HTTP协议在OSI模型的位置\nHTTP协议位于应用层\n<p><image src=\"https://bayuefen.oss-cn-hangzhou.aliyuncs.com/blog/20190406164742398.png?x-oss-process=style/compress_high\" width=\"600\" align=\"center\"></p>\n\n### HTTP协议的method\nGET:请求一个指定资源的表示形式. 使用GET的请求应该只被用于获取数据.\n\n```\n>>> r = requests.get('https://api.github.com/events')\n```\n\nHEAD:请求一个与GET请求的响应相同的响应,但没有响应体.\n\n```\n>>> r = requests.head('http://httpbin.org/get')\n```\n\nPOST:用于将实体提交到指定的资源,通常导致状态或服务器上的副作用的更改. \n```\n>>> r = requests.post('http://httpbin.org/post', data = {'key':'value'})\n```\n\nPUT:用请求有效载荷替换目标资源的所有当前表示。\n\n```\n>>> r = requests.put('http://httpbin.org/put', data = {'key':'value'})\n```\n\nDELETE:删除指定的资源。\n\n```\n>>> r = requests.delete('http://httpbin.org/delete')\n```\n\nCONNECT:建立一个到由目标资源标识的服务器的隧道。\n\nOPTIONS:用于描述目标资源的通信选项。\n\n```\n>>> r = requests.options('http://httpbin.org/get')\n```\n\nTRACE:沿着到目标资源的路径执行一个消息环回测试。\n\nPATCH:用于对资源应用部分修改。\n\nGET和POST对比:\nGET\n1.方法用途\nGET 方法的首要目的是 获取资源\n\n2.方法特点\na) 参数可见\nGET 方法的参数是明文可见的包含在 URL 当中,所以说敏感信息不建议使用 GET 方法\n不过也正是因此,所以 GET 方法允许被保存书签\n\nb) 数据类型只允许 ASCII\nGET 方法的数据类型只允许是 ASCII 字符,所以说传递 二进制 文件就不可以用 GET 方法了哦\n\nc) 可以保存书签\n当我们访问某一个网站的频率特别高的时候,肯定添加到书签,那其实书签就是依靠 GET 方法来保存的\n\nd) 可以被缓存\nGET 方法支持缓存,当本次请求允许被缓存时,会将资源存值本地 cache ,在未过期的情况下直接取本地 cache;缓存过期后视情况而定\n\ne) 参数会保留在浏览器历史记录\n比较直观的感受就是,我们可以在浏览器的历史记录中查看到曾经搜索过的关键字信息\n\nf) 请求长度会受限于所使用的浏览器与服务器\n不同的浏览器对于 GET 请求长度的限制也是不同的,注意这是 浏览器 / 服务器(IE、Chrome、Apache、IIS等) 对于长度的限制,而不是 HTTP 协议\n\nPOST\n1.方法用途\nPOST 方法的首要目的是 提交,POST 方法一般用于添加资源\n\n2.方法特点\na) 参数不可见,也不会被保存\n所以说 POST 方法是不可以被保存书签的\n\nb) 不能收藏为书签\n理由如上\n\nc) 不可以被缓存\n我要提交的数据被缓存在本地 cache 中想想其实也是没道理的\n\nd) 不会被保存在浏览器历史中\n同样是因为参数不可见\n\ne) 不限制请求长度\n对于 POST 方法这种以 提交 为首要目的的方法,肯定是不可以限制请求长度的\n\nf) 数据类型\n不限,所以说 POST 是可以 提交文件 到服务器的\n\ng) 请求方式\nPOST 请求与 GET 请求不同,他会首先提交 HEAD 信息,待得到 100 响应后,才会再次将 DATA 提交\n\n### HTTP协议的组成\n请求报文包含三部分:\n**·**请求行(Request line):包含请求方法、URI、HTTP版本信息\n**·**请求首部字段(Request header)\n**·**请求内容实体\n响应报文包含三部分:(以豆瓣电影TOP250为例)\n>Request Headers\n\n> GET /top250 HTTP/1.1 \n\\#GET表示一个读取请求,将从服务器获得网页数据,/表示URL的路径,URL总是以/开头,/就表示首页,最后的HTTP/1.1指示采用的HTTP协议版本是1.1。\nHost: movie.douban.com\n\\#表示请求的域名是movie.douban.com\nConnection: keep-alive\n\\#表示支持长连接\nCache-Control: max-age=0\n\\#指定请求和响应遵循的缓存机制\nUpgrade-Insecure-Requests: 1\n\\#浏览器可以处理https协议\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36\n\\#发出请求的用户信息\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3\n\\#客户端希望接受的数据类型\nAccept-Encoding: gzip, deflate, br\n\\#浏览器发给服务器,声明浏览器支持的编码类型\nAccept-Language: zh-CN,zh;q=0.9\n\\#浏览器支持的语言分别是简体中文和中文,优先支持简体中文。\nCookie: bid=ZbyUzrJdS2w; __utmc=30149280; __utmc=223695111; __yadk_uid=nTiBvU6fTOaXD90dB6edYhp8urhJwCjc; viewed=\"27599884\"; gr_user_id=4aa3ad30-748f-488c-b5b3-bcff3f0456d4; douban-fav-remind=1; ll=\"118172\"; _vwo_uuid_v2=DE51C937E99B505F3A54AE46B8619B83A|80d039133d473653fabc07636080bbb8; push_noty_num=0; push_doumail_num=0; __utmv=30149280.19454; ap_v=0,6.0; _pk_ref.100001.4cf6=%5B%22%22%2C%22%22%2C1554617137%2C%22https%3A%2F%2Fwww.\nbaidu.com%2Flink%3Furl%3Dt8BO_5f8zl_78xcE_dnmyhd_1xbKVsXtXZv_yHxp72UVO88nbOSLQSA1xMPpsfX2%26wd%3\nD%26eqid%3Da8c4f714000c0677000000035ca9932e%22%5D;\n _pk_id.100001.4cf6=e94317b751ae8229.1554384156.13.1554617137.1554583408.; _pk_ses.100001.4cf6=*; __utma=30149280.106710662.1554384144.1554583409.1554617137.12; __utmb=30149280.0.10.1554617137; __utmz=30149280.1554617137.12.6.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; __utma=223695111.1166551075.1554384156.1554583409.1554617137.13; __utmb=223695111.0.10.1554617137; __utmz=223695111.1554617137.13.6.utmcsr=baidu|utmccn=(organic)|utmcmd=organic\n\\#http是无状态的,所以引入了cookie来管理服务器与客户端之间的状态\n\n响应报文包含三部分:\n**·**状态行:包含HTTP版本、状态码、状态码的原因短语\n**·**响应首部字段\n**·**响应内容实体\n> Response header\n\n> HTTP/1.1 200 OK\n\\#响应状态\nDate: Sun, 07 Apr 2019 06:05:45 GMT\n\\#生成消息的具体时间和日期\nContent-Type: text/html; charset=utf-8\n\\#服务器发送 html 文档,字符集为 UTF-8\nTransfer-Encoding: chunked\nConnection: keep-alive\nKeep-Alive: timeout=30\nVary: Accept-Encoding\n\\#服务器响应时根据请求头中的的值返回不同的内容\nX-Xss-Protection: 1; mode=block\n设置浏览器的XSS防护机制,浏览器如果检测到恶意代码,则不渲染恶意代码\nX-Douban-Mobileapp: 0\nExpires: Sun, 1 Jan 2006 01:00:00 GMT\n浏览器会在指定过期时间内使用本地缓存\nPragma: no-cache\nCache-Control: must-revalidate, no-cache, private\nX-DAE-Node: brand4\nX-DAE-App: movie\nServer: dae\nX-Content-Type-Options: nosniff\nContent-Encoding: gzip\n文档使用的 MIME 类型是 text/html,并且对内容进行了 gzip 压缩\n\n### HTTP协议的状态码\n1xx(临时响应)表示临时响应并需要请求者继续执行操作的状态代码。\n2xx (成功) 表示成功处理了请求的状态代码。\n3xx (重定向) 表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。\n4xx(请求错误) 这些状态代码表示请求可能出错,妨碍了服务器的处理。\n5xx(服务器错误) 这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。\n常见的:200 – 服务器成功返回网页 |404 – 请求的网页不存在 |503 – 服务不可用 \n\n状态码 |中文描述 \n:--------------------:|:--------------------:\n100|(继续) 请求者应当继续提出请求。 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。 \n101|(切换协议) 请求者已要求服务器切换协议,服务器已确认并准备切换。\n102|(已接受)已经接受请求,但未处理完成\n200| (成功) 服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。\n201|(已创建) 请求成功并且服务器创建了新的资源。\n202|(已接受) 服务器已接受请求,但尚未处理。\n203|(非授权信息) 服务器已成功处理了请求,但返回的信息可能来自另一来源。\n204|(无内容) 服务器成功处理了请求,但没有返回任何内容。\n205|(重置内容) 服务器成功处理了请求,但没有返回任何内容。 \n206|(部分内容) 服务器成功处理了部分 GET 请求。\n300|(多种选择) 针对请求,服务器可执行多种操作。 服务器可根据请求者 (user agent) 选择一项操作,或提供操作列表供请求者选择。 \n301|(永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。\n302|(临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。\n303|(查看其他位置) 请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。\n304|(未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。 \n305|(使用代理) 请求者只能使用代理访问请求的网页。 如果服务器返回此响应,还表示请求者应使用代理。\n307|(临时重定向) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。\n400|(错误请求) 服务器不理解请求的语法。\n401|未授权) 请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。 \n403|(禁止) 服务器拒绝请求。 \n404|(未找到) 服务器找不到请求的网页。 \n405|(方法禁用) 禁用请求中指定的方法。\n406|(不接受) 无法使用请求的内容特性响应请求的网页。 \n407|(需要代理授权) 此状态代码与 401(未授权)类似,但指定请求者应当授权使用代理。\n408|(请求超时) 服务器等候请求时发生超时。 \n409|(冲突) 服务器在完成请求时发生冲突。 服务器必须在响应中包含有关冲突的信息。 \n410|(已删除) 如果请求的资源已永久删除,服务器就会返回此响应。 \n411|(需要有效长度) 服务器不接受不含有效内容长度标头字段的请求。 \n412|(未满足前提条件) 服务器未满足请求者在请求中设置的其中一个前提条件。 \n413|(请求实体过大) 服务器无法处理请求,因为请求实体过大,超出服务器的处理能力。 \n414|(请求的 URI 过长) 请求的 URI(通常为网址)过长,服务器无法处理。 \n415|(不支持的媒体类型) 请求的格式不受请求页面的支持。 \n416|(请求范围不符合要求) 如果页面无法提供请求的范围,则服务器会返回此状态代码。 \n417|(未满足期望值) 服务器未满足”期望”请求标头字段的要求。\n500|(服务器内部错误) 服务器遇到错误,无法完成请求。 \n501|(尚未实施) 服务器不具备完成请求的功能。 例如,服务器无法识别请求方法时可能会返回此代码。\n502|(错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。\n503|(服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。 \n504|(网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。 \n505|(HTTP 版本不受支持) 服务器不支持请求中所用的 HTTP 协议版本。\n \n \n### cookies和会话\n因为HTTP是无状态的,对事物处理没有记忆能力。cookies和会话出现用于保持HTTP连接状态,\n会话在服务端,用来保存用户的会话信息;cookies在客户端,浏览器下次访问网页时会自动附带上它发送给服务器,\n服务器通过识别cookies找到对应的会话判断用户状态。\nPS:关闭浏览器不会导致会话被删除,反而没有存储到硬盘上的cookie会消失,因此为了节省存储空间需要为会话设置一个失效时间。\n","source":"_posts/http-learn-notes.md","raw":"---\ntitle: 【HTTP】http协议学习\ndate: 2019-04-09 22:08:32\ntags:\n - HTTP\n - 日记\n - 学习Vlog\n---\n\n### HTTP协议\nHTTP协议是客户端和 服务器端之间数据传输的格式规范,格式简称为“超文本传输协议”全称为“Hyper Text Transfer Protocol”。\n\n<!-- more -->\n### HTTP协议历史版本\n- `HTTP 1.0`: 默认是基于短连接,即非持久连接,采用文本数据格式传输;单个tcp连接仅能维护传输单个Web对象。\n > 无host域\n\n<br/>\n\n- `HTTP 1.1`: 默认是基于长连接,即持久连接(可配置成非持久连接),采用文本数据格式传输;单个tcp连接可以维护多个Web对象的传输。\n > HTTP 1.1可获取host域这个参数。\n > 长连接可有效减少TCP三次握手四次挥手的网络连接性能开销。\n > HTTP 1.1支持只发送header信息,不带任何body信息。可以校验B/S架构模式下,Browser是否具有访问请求权限,有则返回`status: 100`,无则返回`status: 401`,从而再依返回结果将请求的body信息发送给服务器,节约网络带宽。\n > HTTP 1.1同时也支持资源加载的断点续传。\n > 同一时间对于同一域名,请求数量有限制,超过限制会造成网络阻塞请求。\n \n<br/>\n\n- `HTTP 2.0`: 采用二进制数据格式传输;实现多路复用;进行头部压缩优化。[HTTP1.1与HTTP2.0区别Demo](https://http2.akamai.com/demo)\n > ⭐️ HTTP 2.0采用*多路复用(Multiplexing)* 用以解决*线头阻塞* 的问题。核心基于Google公司开发的基于TCP的应用层协议[SPDY非标准协议](https://zh.wikipedia.org/wiki/SPDY)\n > ⭐️ 增加\"二进制分帧层\"实现底层tcp多路复用。将多个请求在同一个TCP连接上完成,承载任意数量的双向数据流。极大的提升了传输层通信性能。\n > HTTP 2.0采用首部压缩设计的[HPACK](http://http2.github.io/http2-spec/compression.html)算法\n > HTTP 2.0的Server Push以及缓存策略\n \n<br/>\n\n- `HTTP 3.0`: 核心基于UDP传输层协议的[QUIC协议](https://zhuanlan.zhihu.com/p/32553477);提供数据传输的高可靠性及0-RTT延迟;彻底解决*线头阻塞* \n > 基于UDP传输层协议,实现高速及高可靠性的数据传输。\n > 彻底解决http1.1遗留的线头阻塞(HOL),实现不同流的数据传输相互独立传输,互不干扰。\n > 0-RTT,不必像TCP那样需要三次握手。\n > 参考:\n [QUIC协议浅析与HTTP/3.0](https://www.jianshu.com/p/bb3eeb36b479) \n [如何看待 HTTP/3 ?](https://www.zhihu.com/question/302412059/answer/533223530)\n### HTTP/2协议\n1、二进制分帧层 (Binary Framing Layer)\n2、在单个TCP连接里多路复用请求。\n3、HTTP/2的Server Push,非常重要的一个特性。\n4、HTTP Header的压缩,采用的是HPack算法。\n5、应用层的重置连接\n6、请求优先级设置\n7、流量控制\n8、HTTP/1 的几种优化可以弃用\n参考:https://blog.wangriyu.wang/2018/05-HTTP2.html\n\n### HTTP协议在OSI模型的位置\nHTTP协议位于应用层\n<p><image src=\"https://bayuefen.oss-cn-hangzhou.aliyuncs.com/blog/20190406164742398.png?x-oss-process=style/compress_high\" width=\"600\" align=\"center\"></p>\n\n### HTTP协议的method\nGET:请求一个指定资源的表示形式. 使用GET的请求应该只被用于获取数据.\n\n```\n>>> r = requests.get('https://api.github.com/events')\n```\n\nHEAD:请求一个与GET请求的响应相同的响应,但没有响应体.\n\n```\n>>> r = requests.head('http://httpbin.org/get')\n```\n\nPOST:用于将实体提交到指定的资源,通常导致状态或服务器上的副作用的更改. \n```\n>>> r = requests.post('http://httpbin.org/post', data = {'key':'value'})\n```\n\nPUT:用请求有效载荷替换目标资源的所有当前表示。\n\n```\n>>> r = requests.put('http://httpbin.org/put', data = {'key':'value'})\n```\n\nDELETE:删除指定的资源。\n\n```\n>>> r = requests.delete('http://httpbin.org/delete')\n```\n\nCONNECT:建立一个到由目标资源标识的服务器的隧道。\n\nOPTIONS:用于描述目标资源的通信选项。\n\n```\n>>> r = requests.options('http://httpbin.org/get')\n```\n\nTRACE:沿着到目标资源的路径执行一个消息环回测试。\n\nPATCH:用于对资源应用部分修改。\n\nGET和POST对比:\nGET\n1.方法用途\nGET 方法的首要目的是 获取资源\n\n2.方法特点\na) 参数可见\nGET 方法的参数是明文可见的包含在 URL 当中,所以说敏感信息不建议使用 GET 方法\n不过也正是因此,所以 GET 方法允许被保存书签\n\nb) 数据类型只允许 ASCII\nGET 方法的数据类型只允许是 ASCII 字符,所以说传递 二进制 文件就不可以用 GET 方法了哦\n\nc) 可以保存书签\n当我们访问某一个网站的频率特别高的时候,肯定添加到书签,那其实书签就是依靠 GET 方法来保存的\n\nd) 可以被缓存\nGET 方法支持缓存,当本次请求允许被缓存时,会将资源存值本地 cache ,在未过期的情况下直接取本地 cache;缓存过期后视情况而定\n\ne) 参数会保留在浏览器历史记录\n比较直观的感受就是,我们可以在浏览器的历史记录中查看到曾经搜索过的关键字信息\n\nf) 请求长度会受限于所使用的浏览器与服务器\n不同的浏览器对于 GET 请求长度的限制也是不同的,注意这是 浏览器 / 服务器(IE、Chrome、Apache、IIS等) 对于长度的限制,而不是 HTTP 协议\n\nPOST\n1.方法用途\nPOST 方法的首要目的是 提交,POST 方法一般用于添加资源\n\n2.方法特点\na) 参数不可见,也不会被保存\n所以说 POST 方法是不可以被保存书签的\n\nb) 不能收藏为书签\n理由如上\n\nc) 不可以被缓存\n我要提交的数据被缓存在本地 cache 中想想其实也是没道理的\n\nd) 不会被保存在浏览器历史中\n同样是因为参数不可见\n\ne) 不限制请求长度\n对于 POST 方法这种以 提交 为首要目的的方法,肯定是不可以限制请求长度的\n\nf) 数据类型\n不限,所以说 POST 是可以 提交文件 到服务器的\n\ng) 请求方式\nPOST 请求与 GET 请求不同,他会首先提交 HEAD 信息,待得到 100 响应后,才会再次将 DATA 提交\n\n### HTTP协议的组成\n请求报文包含三部分:\n**·**请求行(Request line):包含请求方法、URI、HTTP版本信息\n**·**请求首部字段(Request header)\n**·**请求内容实体\n响应报文包含三部分:(以豆瓣电影TOP250为例)\n>Request Headers\n\n> GET /top250 HTTP/1.1 \n\\#GET表示一个读取请求,将从服务器获得网页数据,/表示URL的路径,URL总是以/开头,/就表示首页,最后的HTTP/1.1指示采用的HTTP协议版本是1.1。\nHost: movie.douban.com\n\\#表示请求的域名是movie.douban.com\nConnection: keep-alive\n\\#表示支持长连接\nCache-Control: max-age=0\n\\#指定请求和响应遵循的缓存机制\nUpgrade-Insecure-Requests: 1\n\\#浏览器可以处理https协议\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36\n\\#发出请求的用户信息\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3\n\\#客户端希望接受的数据类型\nAccept-Encoding: gzip, deflate, br\n\\#浏览器发给服务器,声明浏览器支持的编码类型\nAccept-Language: zh-CN,zh;q=0.9\n\\#浏览器支持的语言分别是简体中文和中文,优先支持简体中文。\nCookie: bid=ZbyUzrJdS2w; __utmc=30149280; __utmc=223695111; __yadk_uid=nTiBvU6fTOaXD90dB6edYhp8urhJwCjc; viewed=\"27599884\"; gr_user_id=4aa3ad30-748f-488c-b5b3-bcff3f0456d4; douban-fav-remind=1; ll=\"118172\"; _vwo_uuid_v2=DE51C937E99B505F3A54AE46B8619B83A|80d039133d473653fabc07636080bbb8; push_noty_num=0; push_doumail_num=0; __utmv=30149280.19454; ap_v=0,6.0; _pk_ref.100001.4cf6=%5B%22%22%2C%22%22%2C1554617137%2C%22https%3A%2F%2Fwww.\nbaidu.com%2Flink%3Furl%3Dt8BO_5f8zl_78xcE_dnmyhd_1xbKVsXtXZv_yHxp72UVO88nbOSLQSA1xMPpsfX2%26wd%3\nD%26eqid%3Da8c4f714000c0677000000035ca9932e%22%5D;\n _pk_id.100001.4cf6=e94317b751ae8229.1554384156.13.1554617137.1554583408.; _pk_ses.100001.4cf6=*; __utma=30149280.106710662.1554384144.1554583409.1554617137.12; __utmb=30149280.0.10.1554617137; __utmz=30149280.1554617137.12.6.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; __utma=223695111.1166551075.1554384156.1554583409.1554617137.13; __utmb=223695111.0.10.1554617137; __utmz=223695111.1554617137.13.6.utmcsr=baidu|utmccn=(organic)|utmcmd=organic\n\\#http是无状态的,所以引入了cookie来管理服务器与客户端之间的状态\n\n响应报文包含三部分:\n**·**状态行:包含HTTP版本、状态码、状态码的原因短语\n**·**响应首部字段\n**·**响应内容实体\n> Response header\n\n> HTTP/1.1 200 OK\n\\#响应状态\nDate: Sun, 07 Apr 2019 06:05:45 GMT\n\\#生成消息的具体时间和日期\nContent-Type: text/html; charset=utf-8\n\\#服务器发送 html 文档,字符集为 UTF-8\nTransfer-Encoding: chunked\nConnection: keep-alive\nKeep-Alive: timeout=30\nVary: Accept-Encoding\n\\#服务器响应时根据请求头中的的值返回不同的内容\nX-Xss-Protection: 1; mode=block\n设置浏览器的XSS防护机制,浏览器如果检测到恶意代码,则不渲染恶意代码\nX-Douban-Mobileapp: 0\nExpires: Sun, 1 Jan 2006 01:00:00 GMT\n浏览器会在指定过期时间内使用本地缓存\nPragma: no-cache\nCache-Control: must-revalidate, no-cache, private\nX-DAE-Node: brand4\nX-DAE-App: movie\nServer: dae\nX-Content-Type-Options: nosniff\nContent-Encoding: gzip\n文档使用的 MIME 类型是 text/html,并且对内容进行了 gzip 压缩\n\n### HTTP协议的状态码\n1xx(临时响应)表示临时响应并需要请求者继续执行操作的状态代码。\n2xx (成功) 表示成功处理了请求的状态代码。\n3xx (重定向) 表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。\n4xx(请求错误) 这些状态代码表示请求可能出错,妨碍了服务器的处理。\n5xx(服务器错误) 这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。\n常见的:200 – 服务器成功返回网页 |404 – 请求的网页不存在 |503 – 服务不可用 \n\n状态码 |中文描述 \n:--------------------:|:--------------------:\n100|(继续) 请求者应当继续提出请求。 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。 \n101|(切换协议) 请求者已要求服务器切换协议,服务器已确认并准备切换。\n102|(已接受)已经接受请求,但未处理完成\n200| (成功) 服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。\n201|(已创建) 请求成功并且服务器创建了新的资源。\n202|(已接受) 服务器已接受请求,但尚未处理。\n203|(非授权信息) 服务器已成功处理了请求,但返回的信息可能来自另一来源。\n204|(无内容) 服务器成功处理了请求,但没有返回任何内容。\n205|(重置内容) 服务器成功处理了请求,但没有返回任何内容。 \n206|(部分内容) 服务器成功处理了部分 GET 请求。\n300|(多种选择) 针对请求,服务器可执行多种操作。 服务器可根据请求者 (user agent) 选择一项操作,或提供操作列表供请求者选择。 \n301|(永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。\n302|(临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。\n303|(查看其他位置) 请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。\n304|(未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。 \n305|(使用代理) 请求者只能使用代理访问请求的网页。 如果服务器返回此响应,还表示请求者应使用代理。\n307|(临时重定向) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。\n400|(错误请求) 服务器不理解请求的语法。\n401|未授权) 请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。 \n403|(禁止) 服务器拒绝请求。 \n404|(未找到) 服务器找不到请求的网页。 \n405|(方法禁用) 禁用请求中指定的方法。\n406|(不接受) 无法使用请求的内容特性响应请求的网页。 \n407|(需要代理授权) 此状态代码与 401(未授权)类似,但指定请求者应当授权使用代理。\n408|(请求超时) 服务器等候请求时发生超时。 \n409|(冲突) 服务器在完成请求时发生冲突。 服务器必须在响应中包含有关冲突的信息。 \n410|(已删除) 如果请求的资源已永久删除,服务器就会返回此响应。 \n411|(需要有效长度) 服务器不接受不含有效内容长度标头字段的请求。 \n412|(未满足前提条件) 服务器未满足请求者在请求中设置的其中一个前提条件。 \n413|(请求实体过大) 服务器无法处理请求,因为请求实体过大,超出服务器的处理能力。 \n414|(请求的 URI 过长) 请求的 URI(通常为网址)过长,服务器无法处理。 \n415|(不支持的媒体类型) 请求的格式不受请求页面的支持。 \n416|(请求范围不符合要求) 如果页面无法提供请求的范围,则服务器会返回此状态代码。 \n417|(未满足期望值) 服务器未满足”期望”请求标头字段的要求。\n500|(服务器内部错误) 服务器遇到错误,无法完成请求。 \n501|(尚未实施) 服务器不具备完成请求的功能。 例如,服务器无法识别请求方法时可能会返回此代码。\n502|(错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。\n503|(服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。 \n504|(网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。 \n505|(HTTP 版本不受支持) 服务器不支持请求中所用的 HTTP 协议版本。\n \n \n### cookies和会话\n因为HTTP是无状态的,对事物处理没有记忆能力。cookies和会话出现用于保持HTTP连接状态,\n会话在服务端,用来保存用户的会话信息;cookies在客户端,浏览器下次访问网页时会自动附带上它发送给服务器,\n服务器通过识别cookies找到对应的会话判断用户状态。\nPS:关闭浏览器不会导致会话被删除,反而没有存储到硬盘上的cookie会消失,因此为了节省存储空间需要为会话设置一个失效时间。\n","slug":"http-learn-notes","published":1,"updated":"2019-06-09T17:21:12.948Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cjwr0llwg001nbkvlx8k1a3sm","content":"<h3 id=\"HTTP协议\"><a href=\"#HTTP协议\" class=\"headerlink\" title=\"HTTP协议\"></a>HTTP协议</h3><p>HTTP协议是客户端和 服务器端之间数据传输的格式规范,格式简称为“超文本传输协议”全称为“Hyper Text Transfer Protocol”。</p>\n<a id=\"more\"></a>\n<h3 id=\"HTTP协议历史版本\"><a href=\"#HTTP协议历史版本\" class=\"headerlink\" title=\"HTTP协议历史版本\"></a>HTTP协议历史版本</h3><ul>\n<li><code>HTTP 1.0</code>: 默认是基于短连接,即非持久连接,采用文本数据格式传输;单个tcp连接仅能维护传输单个Web对象。<blockquote>\n<p>无host域</p>\n</blockquote>\n</li>\n</ul>\n<p><br></p>\n<ul>\n<li><code>HTTP 1.1</code>: 默认是基于长连接,即持久连接(可配置成非持久连接),采用文本数据格式传输;单个tcp连接可以维护多个Web对象的传输。<blockquote>\n<p>HTTP 1.1可获取host域这个参数。<br>长连接可有效减少TCP三次握手四次挥手的网络连接性能开销。<br>HTTP 1.1支持只发送header信息,不带任何body信息。可以校验B/S架构模式下,Browser是否具有访问请求权限,有则返回<code>status: 100</code>,无则返回<code>status: 401</code>,从而再依返回结果将请求的body信息发送给服务器,节约网络带宽。<br>HTTP 1.1同时也支持资源加载的断点续传。<br>同一时间对于同一域名,请求数量有限制,超过限制会造成网络阻塞请求。</p>\n</blockquote>\n</li>\n</ul>\n<p><br></p>\n<ul>\n<li><code>HTTP 2.0</code>: 采用二进制数据格式传输;实现多路复用;进行头部压缩优化。<a href=\"https://http2.akamai.com/demo\" target=\"_blank\" rel=\"noopener\">HTTP1.1与HTTP2.0区别Demo</a><blockquote>\n<p>⭐️ HTTP 2.0采用<em>多路复用(Multiplexing)</em> 用以解决<em>线头阻塞</em> 的问题。核心基于Google公司开发的基于TCP的应用层协议<a href=\"https://zh.wikipedia.org/wiki/SPDY\" target=\"_blank\" rel=\"noopener\">SPDY非标准协议</a><br>⭐️ 增加”二进制分帧层”实现底层tcp多路复用。将多个请求在同一个TCP连接上完成,承载任意数量的双向数据流。极大的提升了传输层通信性能。<br>HTTP 2.0采用首部压缩设计的<a href=\"http://http2.github.io/http2-spec/compression.html\" target=\"_blank\" rel=\"noopener\">HPACK</a>算法<br>HTTP 2.0的Server Push以及缓存策略</p>\n</blockquote>\n</li>\n</ul>\n<p><br></p>\n<ul>\n<li><code>HTTP 3.0</code>: 核心基于UDP传输层协议的<a href=\"https://zhuanlan.zhihu.com/p/32553477\" target=\"_blank\" rel=\"noopener\">QUIC协议</a>;提供数据传输的高可靠性及0-RTT延迟;彻底解决<em>线头阻塞</em> <blockquote>\n<p>基于UDP传输层协议,实现高速及高可靠性的数据传输。<br>彻底解决http1.1遗留的线头阻塞(HOL),实现不同流的数据传输相互独立传输,互不干扰。<br>0-RTT,不必像TCP那样需要三次握手。<br>参考:<br> <a href=\"https://www.jianshu.com/p/bb3eeb36b479\" target=\"_blank\" rel=\"noopener\">QUIC协议浅析与HTTP/3.0</a><br> <a href=\"https://www.zhihu.com/question/302412059/answer/533223530\" target=\"_blank\" rel=\"noopener\">如何看待 HTTP/3 ?</a></p>\n</blockquote>\n<h3 id=\"HTTP-2协议\"><a href=\"#HTTP-2协议\" class=\"headerlink\" title=\"HTTP/2协议\"></a>HTTP/2协议</h3>1、二进制分帧层 (Binary Framing Layer)<br>2、在单个TCP连接里多路复用请求。<br>3、HTTP/2的Server Push,非常重要的一个特性。<br>4、HTTP Header的压缩,采用的是HPack算法。<br>5、应用层的重置连接<br>6、请求优先级设置<br>7、流量控制<br>8、HTTP/1 的几种优化可以弃用<br>参考:<a href=\"https://blog.wangriyu.wang/2018/05-HTTP2.html\" target=\"_blank\" rel=\"noopener\">https://blog.wangriyu.wang/2018/05-HTTP2.html</a></li>\n</ul>\n<h3 id=\"HTTP协议在OSI模型的位置\"><a href=\"#HTTP协议在OSI模型的位置\" class=\"headerlink\" title=\"HTTP协议在OSI模型的位置\"></a>HTTP协议在OSI模型的位置</h3><p>HTTP协议位于应用层</p>\n<p><image src=\"https://bayuefen.oss-cn-hangzhou.aliyuncs.com/blog/20190406164742398.png?x-oss-process=style/compress_high\" width=\"600\" align=\"center\"></image></p>\n\n<h3 id=\"HTTP协议的method\"><a href=\"#HTTP协议的method\" class=\"headerlink\" title=\"HTTP协议的method\"></a>HTTP协议的method</h3><p>GET:请求一个指定资源的表示形式. 使用GET的请求应该只被用于获取数据.</p>\n<figure class=\"highlight ruby\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\">>></span>> r = requests.get(<span class=\"string\">'https://api.github.com/events'</span>)</span><br></pre></td></tr></table></figure>\n<p>HEAD:请求一个与GET请求的响应相同的响应,但没有响应体.</p>\n<figure class=\"highlight ruby\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\">>></span>> r = requests.head(<span class=\"string\">'http://httpbin.org/get'</span>)</span><br></pre></td></tr></table></figure>\n<p>POST:用于将实体提交到指定的资源,通常导致状态或服务器上的副作用的更改.<br><figure class=\"highlight ruby\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\">>></span>> r = requests.post(<span class=\"string\">'http://httpbin.org/post'</span>, data = {<span class=\"string\">'key'</span><span class=\"symbol\">:<span class=\"string\">'value'</span></span>})</span><br></pre></td></tr></table></figure></p>\n<p>PUT:用请求有效载荷替换目标资源的所有当前表示。</p>\n<figure class=\"highlight ruby\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\">>></span>> r = requests.put(<span class=\"string\">'http://httpbin.org/put'</span>, data = {<span class=\"string\">'key'</span><span class=\"symbol\">:<span class=\"string\">'value'</span></span>})</span><br></pre></td></tr></table></figure>\n<p>DELETE:删除指定的资源。</p>\n<figure class=\"highlight ruby\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\">>></span>> r = requests.delete(<span class=\"string\">'http://httpbin.org/delete'</span>)</span><br></pre></td></tr></table></figure>\n<p>CONNECT:建立一个到由目标资源标识的服务器的隧道。</p>\n<p>OPTIONS:用于描述目标资源的通信选项。</p>\n<figure class=\"highlight ruby\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\">>></span>> r = requests.options(<span class=\"string\">'http://httpbin.org/get'</span>)</span><br></pre></td></tr></table></figure>\n<p>TRACE:沿着到目标资源的路径执行一个消息环回测试。</p>\n<p>PATCH:用于对资源应用部分修改。</p>\n<p>GET和POST对比:<br>GET<br>1.方法用途<br>GET 方法的首要目的是 获取资源</p>\n<p>2.方法特点<br>a) 参数可见<br>GET 方法的参数是明文可见的包含在 URL 当中,所以说敏感信息不建议使用 GET 方法<br>不过也正是因此,所以 GET 方法允许被保存书签</p>\n<p>b) 数据类型只允许 ASCII<br>GET 方法的数据类型只允许是 ASCII 字符,所以说传递 二进制 文件就不可以用 GET 方法了哦</p>\n<p>c) 可以保存书签<br>当我们访问某一个网站的频率特别高的时候,肯定添加到书签,那其实书签就是依靠 GET 方法来保存的</p>\n<p>d) 可以被缓存<br>GET 方法支持缓存,当本次请求允许被缓存时,会将资源存值本地 cache ,在未过期的情况下直接取本地 cache;缓存过期后视情况而定</p>\n<p>e) 参数会保留在浏览器历史记录<br>比较直观的感受就是,我们可以在浏览器的历史记录中查看到曾经搜索过的关键字信息</p>\n<p>f) 请求长度会受限于所使用的浏览器与服务器<br>不同的浏览器对于 GET 请求长度的限制也是不同的,注意这是 浏览器 / 服务器(IE、Chrome、Apache、IIS等) 对于长度的限制,而不是 HTTP 协议</p>\n<p>POST<br>1.方法用途<br>POST 方法的首要目的是 提交,POST 方法一般用于添加资源</p>\n<p>2.方法特点<br>a) 参数不可见,也不会被保存<br>所以说 POST 方法是不可以被保存书签的</p>\n<p>b) 不能收藏为书签<br>理由如上</p>\n<p>c) 不可以被缓存<br>我要提交的数据被缓存在本地 cache 中想想其实也是没道理的</p>\n<p>d) 不会被保存在浏览器历史中<br>同样是因为参数不可见</p>\n<p>e) 不限制请求长度<br>对于 POST 方法这种以 提交 为首要目的的方法,肯定是不可以限制请求长度的</p>\n<p>f) 数据类型<br>不限,所以说 POST 是可以 提交文件 到服务器的</p>\n<p>g) 请求方式<br>POST 请求与 GET 请求不同,他会首先提交 HEAD 信息,待得到 100 响应后,才会再次将 DATA 提交</p>\n<h3 id=\"HTTP协议的组成\"><a href=\"#HTTP协议的组成\" class=\"headerlink\" title=\"HTTP协议的组成\"></a>HTTP协议的组成</h3><p>请求报文包含三部分:<br><strong>·</strong>请求行(Request line):包含请求方法、URI、HTTP版本信息<br><strong>·</strong>请求首部字段(Request header)<br><strong>·</strong>请求内容实体<br>响应报文包含三部分:(以豆瓣电影TOP250为例)</p>\n<blockquote>\n<p>Request Headers</p>\n</blockquote>\n<blockquote>\n<p>GET /top250 HTTP/1.1<br>#GET表示一个读取请求,将从服务器获得网页数据,/表示URL的路径,URL总是以/开头,/就表示首页,最后的HTTP/1.1指示采用的HTTP协议版本是1.1。<br>Host: movie.douban.com<br>#表示请求的域名是movie.douban.com<br>Connection: keep-alive<br>#表示支持长连接<br>Cache-Control: max-age=0<br>#指定请求和响应遵循的缓存机制<br>Upgrade-Insecure-Requests: 1<br>#浏览器可以处理https协议<br>User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36<br>#发出请求的用户信息<br>Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,<em>/</em>;q=0.8,application/signed-exchange;v=b3<br>#客户端希望接受的数据类型<br>Accept-Encoding: gzip, deflate, br<br>#浏览器发给服务器,声明浏览器支持的编码类型<br>Accept-Language: zh-CN,zh;q=0.9<br>#浏览器支持的语言分别是简体中文和中文,优先支持简体中文。<br>Cookie: bid=ZbyUzrJdS2w; <strong>utmc=30149280; </strong>utmc=223695111; <strong>yadk_uid=nTiBvU6fTOaXD90dB6edYhp8urhJwCjc; viewed=”27599884”; gr_user_id=4aa3ad30-748f-488c-b5b3-bcff3f0456d4; douban-fav-remind=1; ll=”118172”; _vwo_uuid_v2=DE51C937E99B505F3A54AE46B8619B83A|80d039133d473653fabc07636080bbb8; push_noty_num=0; push_doumail_num=0; </strong>utmv=30149280.19454; ap_v=0,6.0; _pk_ref.100001.4cf6=%5B%22%22%2C%22%22%2C1554617137%2C%22https%3A%2F%2Fwww.<br>baidu.com%2Flink%3Furl%3Dt8BO_5f8zl_78xcE_dnmyhd_1xbKVsXtXZv_yHxp72UVO88nbOSLQSA1xMPpsfX2%26wd%3<br>D%26eqid%3Da8c4f714000c0677000000035ca9932e%22%5D;<br> _pk_id.100001.4cf6=e94317b751ae8229.1554384156.13.1554617137.1554583408.; _pk_ses.100001.4cf6=*; <strong>utma=30149280.106710662.1554384144.1554583409.1554617137.12; </strong>utmb=30149280.0.10.1554617137; <strong>utmz=30149280.1554617137.12.6.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; </strong>utma=223695111.1166551075.1554384156.1554583409.1554617137.13; <strong>utmb=223695111.0.10.1554617137; </strong>utmz=223695111.1554617137.13.6.utmcsr=baidu|utmccn=(organic)|utmcmd=organic<br>#http是无状态的,所以引入了cookie来管理服务器与客户端之间的状态</p>\n</blockquote>\n<p>响应报文包含三部分:<br><strong>·</strong>状态行:包含HTTP版本、状态码、状态码的原因短语<br><strong>·</strong>响应首部字段<br><strong>·</strong>响应内容实体</p>\n<blockquote>\n<p>Response header</p>\n</blockquote>\n<blockquote>\n<p>HTTP/1.1 200 OK<br>#响应状态<br>Date: Sun, 07 Apr 2019 06:05:45 GMT<br>#生成消息的具体时间和日期<br>Content-Type: text/html; charset=utf-8<br>#服务器发送 html 文档,字符集为 UTF-8<br>Transfer-Encoding: chunked<br>Connection: keep-alive<br>Keep-Alive: timeout=30<br>Vary: Accept-Encoding<br>#服务器响应时根据请求头中的的值返回不同的内容<br>X-Xss-Protection: 1; mode=block<br>设置浏览器的XSS防护机制,浏览器如果检测到恶意代码,则不渲染恶意代码<br>X-Douban-Mobileapp: 0<br>Expires: Sun, 1 Jan 2006 01:00:00 GMT<br>浏览器会在指定过期时间内使用本地缓存<br>Pragma: no-cache<br>Cache-Control: must-revalidate, no-cache, private<br>X-DAE-Node: brand4<br>X-DAE-App: movie<br>Server: dae<br>X-Content-Type-Options: nosniff<br>Content-Encoding: gzip<br>文档使用的 MIME 类型是 text/html,并且对内容进行了 gzip 压缩</p>\n</blockquote>\n<h3 id=\"HTTP协议的状态码\"><a href=\"#HTTP协议的状态码\" class=\"headerlink\" title=\"HTTP协议的状态码\"></a>HTTP协议的状态码</h3><p>1xx(临时响应)表示临时响应并需要请求者继续执行操作的状态代码。<br>2xx (成功) 表示成功处理了请求的状态代码。<br>3xx (重定向) 表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。<br>4xx(请求错误) 这些状态代码表示请求可能出错,妨碍了服务器的处理。<br>5xx(服务器错误) 这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。<br>常见的:200 – 服务器成功返回网页 |404 – 请求的网页不存在 |503 – 服务不可用 </p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align:center\">状态码</th>\n<th style=\"text-align:center\">中文描述 </th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align:center\">100</td>\n<td style=\"text-align:center\">(继续) 请求者应当继续提出请求。 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">101</td>\n<td style=\"text-align:center\">(切换协议) 请求者已要求服务器切换协议,服务器已确认并准备切换。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">102</td>\n<td style=\"text-align:center\">(已接受)已经接受请求,但未处理完成</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">200</td>\n<td style=\"text-align:center\">(成功) 服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">201</td>\n<td style=\"text-align:center\">(已创建) 请求成功并且服务器创建了新的资源。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">202</td>\n<td style=\"text-align:center\">(已接受) 服务器已接受请求,但尚未处理。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">203</td>\n<td style=\"text-align:center\">(非授权信息) 服务器已成功处理了请求,但返回的信息可能来自另一来源。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">204</td>\n<td style=\"text-align:center\">(无内容) 服务器成功处理了请求,但没有返回任何内容。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">205</td>\n<td style=\"text-align:center\">(重置内容) 服务器成功处理了请求,但没有返回任何内容。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">206</td>\n<td style=\"text-align:center\">(部分内容) 服务器成功处理了部分 GET 请求。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">300</td>\n<td style=\"text-align:center\">(多种选择) 针对请求,服务器可执行多种操作。 服务器可根据请求者 (user agent) 选择一项操作,或提供操作列表供请求者选择。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">301</td>\n<td style=\"text-align:center\">(永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">302</td>\n<td style=\"text-align:center\">(临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">303</td>\n<td style=\"text-align:center\">(查看其他位置) 请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">304</td>\n<td style=\"text-align:center\">(未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">305</td>\n<td style=\"text-align:center\">(使用代理) 请求者只能使用代理访问请求的网页。 如果服务器返回此响应,还表示请求者应使用代理。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">307</td>\n<td style=\"text-align:center\">(临时重定向) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">400</td>\n<td style=\"text-align:center\">(错误请求) 服务器不理解请求的语法。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">401</td>\n<td style=\"text-align:center\">未授权) 请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">403</td>\n<td style=\"text-align:center\">(禁止) 服务器拒绝请求。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">404</td>\n<td style=\"text-align:center\">(未找到) 服务器找不到请求的网页。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">405</td>\n<td style=\"text-align:center\">(方法禁用) 禁用请求中指定的方法。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">406</td>\n<td style=\"text-align:center\">(不接受) 无法使用请求的内容特性响应请求的网页。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">407</td>\n<td style=\"text-align:center\">(需要代理授权) 此状态代码与 401(未授权)类似,但指定请求者应当授权使用代理。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">408</td>\n<td style=\"text-align:center\">(请求超时) 服务器等候请求时发生超时。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">409</td>\n<td style=\"text-align:center\">(冲突) 服务器在完成请求时发生冲突。 服务器必须在响应中包含有关冲突的信息。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">410</td>\n<td style=\"text-align:center\">(已删除) 如果请求的资源已永久删除,服务器就会返回此响应。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">411</td>\n<td style=\"text-align:center\">(需要有效长度) 服务器不接受不含有效内容长度标头字段的请求。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">412</td>\n<td style=\"text-align:center\">(未满足前提条件) 服务器未满足请求者在请求中设置的其中一个前提条件。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">413</td>\n<td style=\"text-align:center\">(请求实体过大) 服务器无法处理请求,因为请求实体过大,超出服务器的处理能力。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">414</td>\n<td style=\"text-align:center\">(请求的 URI 过长) 请求的 URI(通常为网址)过长,服务器无法处理。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">415</td>\n<td style=\"text-align:center\">(不支持的媒体类型) 请求的格式不受请求页面的支持。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">416</td>\n<td style=\"text-align:center\">(请求范围不符合要求) 如果页面无法提供请求的范围,则服务器会返回此状态代码。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">417</td>\n<td style=\"text-align:center\">(未满足期望值) 服务器未满足”期望”请求标头字段的要求。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">500</td>\n<td style=\"text-align:center\">(服务器内部错误) 服务器遇到错误,无法完成请求。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">501</td>\n<td style=\"text-align:center\">(尚未实施) 服务器不具备完成请求的功能。 例如,服务器无法识别请求方法时可能会返回此代码。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">502</td>\n<td style=\"text-align:center\">(错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">503</td>\n<td style=\"text-align:center\">(服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">504</td>\n<td style=\"text-align:center\">(网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">505</td>\n<td style=\"text-align:center\">(HTTP 版本不受支持) 服务器不支持请求中所用的 HTTP 协议版本。</td>\n</tr>\n</tbody>\n</table>\n<h3 id=\"cookies和会话\"><a href=\"#cookies和会话\" class=\"headerlink\" title=\"cookies和会话\"></a>cookies和会话</h3><p>因为HTTP是无状态的,对事物处理没有记忆能力。cookies和会话出现用于保持HTTP连接状态,<br>会话在服务端,用来保存用户的会话信息;cookies在客户端,浏览器下次访问网页时会自动附带上它发送给服务器,<br>服务器通过识别cookies找到对应的会话判断用户状态。<br>PS:关闭浏览器不会导致会话被删除,反而没有存储到硬盘上的cookie会消失,因此为了节省存储空间需要为会话设置一个失效时间。</p>\n","site":{"data":{}},"excerpt":"<h3 id=\"HTTP协议\"><a href=\"#HTTP协议\" class=\"headerlink\" title=\"HTTP协议\"></a>HTTP协议</h3><p>HTTP协议是客户端和 服务器端之间数据传输的格式规范,格式简称为“超文本传输协议”全称为“Hyper Text Transfer Protocol”。</p>","more":"<h3 id=\"HTTP协议历史版本\"><a href=\"#HTTP协议历史版本\" class=\"headerlink\" title=\"HTTP协议历史版本\"></a>HTTP协议历史版本</h3><ul>\n<li><code>HTTP 1.0</code>: 默认是基于短连接,即非持久连接,采用文本数据格式传输;单个tcp连接仅能维护传输单个Web对象。<blockquote>\n<p>无host域</p>\n</blockquote>\n</li>\n</ul>\n<p><br></p>\n<ul>\n<li><code>HTTP 1.1</code>: 默认是基于长连接,即持久连接(可配置成非持久连接),采用文本数据格式传输;单个tcp连接可以维护多个Web对象的传输。<blockquote>\n<p>HTTP 1.1可获取host域这个参数。<br>长连接可有效减少TCP三次握手四次挥手的网络连接性能开销。<br>HTTP 1.1支持只发送header信息,不带任何body信息。可以校验B/S架构模式下,Browser是否具有访问请求权限,有则返回<code>status: 100</code>,无则返回<code>status: 401</code>,从而再依返回结果将请求的body信息发送给服务器,节约网络带宽。<br>HTTP 1.1同时也支持资源加载的断点续传。<br>同一时间对于同一域名,请求数量有限制,超过限制会造成网络阻塞请求。</p>\n</blockquote>\n</li>\n</ul>\n<p><br></p>\n<ul>\n<li><code>HTTP 2.0</code>: 采用二进制数据格式传输;实现多路复用;进行头部压缩优化。<a href=\"https://http2.akamai.com/demo\" target=\"_blank\" rel=\"noopener\">HTTP1.1与HTTP2.0区别Demo</a><blockquote>\n<p>⭐️ HTTP 2.0采用<em>多路复用(Multiplexing)</em> 用以解决<em>线头阻塞</em> 的问题。核心基于Google公司开发的基于TCP的应用层协议<a href=\"https://zh.wikipedia.org/wiki/SPDY\" target=\"_blank\" rel=\"noopener\">SPDY非标准协议</a><br>⭐️ 增加”二进制分帧层”实现底层tcp多路复用。将多个请求在同一个TCP连接上完成,承载任意数量的双向数据流。极大的提升了传输层通信性能。<br>HTTP 2.0采用首部压缩设计的<a href=\"http://http2.github.io/http2-spec/compression.html\" target=\"_blank\" rel=\"noopener\">HPACK</a>算法<br>HTTP 2.0的Server Push以及缓存策略</p>\n</blockquote>\n</li>\n</ul>\n<p><br></p>\n<ul>\n<li><code>HTTP 3.0</code>: 核心基于UDP传输层协议的<a href=\"https://zhuanlan.zhihu.com/p/32553477\" target=\"_blank\" rel=\"noopener\">QUIC协议</a>;提供数据传输的高可靠性及0-RTT延迟;彻底解决<em>线头阻塞</em> <blockquote>\n<p>基于UDP传输层协议,实现高速及高可靠性的数据传输。<br>彻底解决http1.1遗留的线头阻塞(HOL),实现不同流的数据传输相互独立传输,互不干扰。<br>0-RTT,不必像TCP那样需要三次握手。<br>参考:<br> <a href=\"https://www.jianshu.com/p/bb3eeb36b479\" target=\"_blank\" rel=\"noopener\">QUIC协议浅析与HTTP/3.0</a><br> <a href=\"https://www.zhihu.com/question/302412059/answer/533223530\" target=\"_blank\" rel=\"noopener\">如何看待 HTTP/3 ?</a></p>\n</blockquote>\n<h3 id=\"HTTP-2协议\"><a href=\"#HTTP-2协议\" class=\"headerlink\" title=\"HTTP/2协议\"></a>HTTP/2协议</h3>1、二进制分帧层 (Binary Framing Layer)<br>2、在单个TCP连接里多路复用请求。<br>3、HTTP/2的Server Push,非常重要的一个特性。<br>4、HTTP Header的压缩,采用的是HPack算法。<br>5、应用层的重置连接<br>6、请求优先级设置<br>7、流量控制<br>8、HTTP/1 的几种优化可以弃用<br>参考:<a href=\"https://blog.wangriyu.wang/2018/05-HTTP2.html\" target=\"_blank\" rel=\"noopener\">https://blog.wangriyu.wang/2018/05-HTTP2.html</a></li>\n</ul>\n<h3 id=\"HTTP协议在OSI模型的位置\"><a href=\"#HTTP协议在OSI模型的位置\" class=\"headerlink\" title=\"HTTP协议在OSI模型的位置\"></a>HTTP协议在OSI模型的位置</h3><p>HTTP协议位于应用层</p>\n<p><image src=\"https://bayuefen.oss-cn-hangzhou.aliyuncs.com/blog/20190406164742398.png?x-oss-process=style/compress_high\" width=\"600\" align=\"center\"></image></p>\n\n<h3 id=\"HTTP协议的method\"><a href=\"#HTTP协议的method\" class=\"headerlink\" title=\"HTTP协议的method\"></a>HTTP协议的method</h3><p>GET:请求一个指定资源的表示形式. 使用GET的请求应该只被用于获取数据.</p>\n<figure class=\"highlight ruby\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\">>></span>> r = requests.get(<span class=\"string\">'https://api.github.com/events'</span>)</span><br></pre></td></tr></table></figure>\n<p>HEAD:请求一个与GET请求的响应相同的响应,但没有响应体.</p>\n<figure class=\"highlight ruby\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\">>></span>> r = requests.head(<span class=\"string\">'http://httpbin.org/get'</span>)</span><br></pre></td></tr></table></figure>\n<p>POST:用于将实体提交到指定的资源,通常导致状态或服务器上的副作用的更改.<br><figure class=\"highlight ruby\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\">>></span>> r = requests.post(<span class=\"string\">'http://httpbin.org/post'</span>, data = {<span class=\"string\">'key'</span><span class=\"symbol\">:<span class=\"string\">'value'</span></span>})</span><br></pre></td></tr></table></figure></p>\n<p>PUT:用请求有效载荷替换目标资源的所有当前表示。</p>\n<figure class=\"highlight ruby\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\">>></span>> r = requests.put(<span class=\"string\">'http://httpbin.org/put'</span>, data = {<span class=\"string\">'key'</span><span class=\"symbol\">:<span class=\"string\">'value'</span></span>})</span><br></pre></td></tr></table></figure>\n<p>DELETE:删除指定的资源。</p>\n<figure class=\"highlight ruby\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\">>></span>> r = requests.delete(<span class=\"string\">'http://httpbin.org/delete'</span>)</span><br></pre></td></tr></table></figure>\n<p>CONNECT:建立一个到由目标资源标识的服务器的隧道。</p>\n<p>OPTIONS:用于描述目标资源的通信选项。</p>\n<figure class=\"highlight ruby\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"meta\">>></span>> r = requests.options(<span class=\"string\">'http://httpbin.org/get'</span>)</span><br></pre></td></tr></table></figure>\n<p>TRACE:沿着到目标资源的路径执行一个消息环回测试。</p>\n<p>PATCH:用于对资源应用部分修改。</p>\n<p>GET和POST对比:<br>GET<br>1.方法用途<br>GET 方法的首要目的是 获取资源</p>\n<p>2.方法特点<br>a) 参数可见<br>GET 方法的参数是明文可见的包含在 URL 当中,所以说敏感信息不建议使用 GET 方法<br>不过也正是因此,所以 GET 方法允许被保存书签</p>\n<p>b) 数据类型只允许 ASCII<br>GET 方法的数据类型只允许是 ASCII 字符,所以说传递 二进制 文件就不可以用 GET 方法了哦</p>\n<p>c) 可以保存书签<br>当我们访问某一个网站的频率特别高的时候,肯定添加到书签,那其实书签就是依靠 GET 方法来保存的</p>\n<p>d) 可以被缓存<br>GET 方法支持缓存,当本次请求允许被缓存时,会将资源存值本地 cache ,在未过期的情况下直接取本地 cache;缓存过期后视情况而定</p>\n<p>e) 参数会保留在浏览器历史记录<br>比较直观的感受就是,我们可以在浏览器的历史记录中查看到曾经搜索过的关键字信息</p>\n<p>f) 请求长度会受限于所使用的浏览器与服务器<br>不同的浏览器对于 GET 请求长度的限制也是不同的,注意这是 浏览器 / 服务器(IE、Chrome、Apache、IIS等) 对于长度的限制,而不是 HTTP 协议</p>\n<p>POST<br>1.方法用途<br>POST 方法的首要目的是 提交,POST 方法一般用于添加资源</p>\n<p>2.方法特点<br>a) 参数不可见,也不会被保存<br>所以说 POST 方法是不可以被保存书签的</p>\n<p>b) 不能收藏为书签<br>理由如上</p>\n<p>c) 不可以被缓存<br>我要提交的数据被缓存在本地 cache 中想想其实也是没道理的</p>\n<p>d) 不会被保存在浏览器历史中<br>同样是因为参数不可见</p>\n<p>e) 不限制请求长度<br>对于 POST 方法这种以 提交 为首要目的的方法,肯定是不可以限制请求长度的</p>\n<p>f) 数据类型<br>不限,所以说 POST 是可以 提交文件 到服务器的</p>\n<p>g) 请求方式<br>POST 请求与 GET 请求不同,他会首先提交 HEAD 信息,待得到 100 响应后,才会再次将 DATA 提交</p>\n<h3 id=\"HTTP协议的组成\"><a href=\"#HTTP协议的组成\" class=\"headerlink\" title=\"HTTP协议的组成\"></a>HTTP协议的组成</h3><p>请求报文包含三部分:<br><strong>·</strong>请求行(Request line):包含请求方法、URI、HTTP版本信息<br><strong>·</strong>请求首部字段(Request header)<br><strong>·</strong>请求内容实体<br>响应报文包含三部分:(以豆瓣电影TOP250为例)</p>\n<blockquote>\n<p>Request Headers</p>\n</blockquote>\n<blockquote>\n<p>GET /top250 HTTP/1.1<br>#GET表示一个读取请求,将从服务器获得网页数据,/表示URL的路径,URL总是以/开头,/就表示首页,最后的HTTP/1.1指示采用的HTTP协议版本是1.1。<br>Host: movie.douban.com<br>#表示请求的域名是movie.douban.com<br>Connection: keep-alive<br>#表示支持长连接<br>Cache-Control: max-age=0<br>#指定请求和响应遵循的缓存机制<br>Upgrade-Insecure-Requests: 1<br>#浏览器可以处理https协议<br>User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36<br>#发出请求的用户信息<br>Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,<em>/</em>;q=0.8,application/signed-exchange;v=b3<br>#客户端希望接受的数据类型<br>Accept-Encoding: gzip, deflate, br<br>#浏览器发给服务器,声明浏览器支持的编码类型<br>Accept-Language: zh-CN,zh;q=0.9<br>#浏览器支持的语言分别是简体中文和中文,优先支持简体中文。<br>Cookie: bid=ZbyUzrJdS2w; <strong>utmc=30149280; </strong>utmc=223695111; <strong>yadk_uid=nTiBvU6fTOaXD90dB6edYhp8urhJwCjc; viewed=”27599884”; gr_user_id=4aa3ad30-748f-488c-b5b3-bcff3f0456d4; douban-fav-remind=1; ll=”118172”; _vwo_uuid_v2=DE51C937E99B505F3A54AE46B8619B83A|80d039133d473653fabc07636080bbb8; push_noty_num=0; push_doumail_num=0; </strong>utmv=30149280.19454; ap_v=0,6.0; _pk_ref.100001.4cf6=%5B%22%22%2C%22%22%2C1554617137%2C%22https%3A%2F%2Fwww.<br>baidu.com%2Flink%3Furl%3Dt8BO_5f8zl_78xcE_dnmyhd_1xbKVsXtXZv_yHxp72UVO88nbOSLQSA1xMPpsfX2%26wd%3<br>D%26eqid%3Da8c4f714000c0677000000035ca9932e%22%5D;<br> _pk_id.100001.4cf6=e94317b751ae8229.1554384156.13.1554617137.1554583408.; _pk_ses.100001.4cf6=*; <strong>utma=30149280.106710662.1554384144.1554583409.1554617137.12; </strong>utmb=30149280.0.10.1554617137; <strong>utmz=30149280.1554617137.12.6.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; </strong>utma=223695111.1166551075.1554384156.1554583409.1554617137.13; <strong>utmb=223695111.0.10.1554617137; </strong>utmz=223695111.1554617137.13.6.utmcsr=baidu|utmccn=(organic)|utmcmd=organic<br>#http是无状态的,所以引入了cookie来管理服务器与客户端之间的状态</p>\n</blockquote>\n<p>响应报文包含三部分:<br><strong>·</strong>状态行:包含HTTP版本、状态码、状态码的原因短语<br><strong>·</strong>响应首部字段<br><strong>·</strong>响应内容实体</p>\n<blockquote>\n<p>Response header</p>\n</blockquote>\n<blockquote>\n<p>HTTP/1.1 200 OK<br>#响应状态<br>Date: Sun, 07 Apr 2019 06:05:45 GMT<br>#生成消息的具体时间和日期<br>Content-Type: text/html; charset=utf-8<br>#服务器发送 html 文档,字符集为 UTF-8<br>Transfer-Encoding: chunked<br>Connection: keep-alive<br>Keep-Alive: timeout=30<br>Vary: Accept-Encoding<br>#服务器响应时根据请求头中的的值返回不同的内容<br>X-Xss-Protection: 1; mode=block<br>设置浏览器的XSS防护机制,浏览器如果检测到恶意代码,则不渲染恶意代码<br>X-Douban-Mobileapp: 0<br>Expires: Sun, 1 Jan 2006 01:00:00 GMT<br>浏览器会在指定过期时间内使用本地缓存<br>Pragma: no-cache<br>Cache-Control: must-revalidate, no-cache, private<br>X-DAE-Node: brand4<br>X-DAE-App: movie<br>Server: dae<br>X-Content-Type-Options: nosniff<br>Content-Encoding: gzip<br>文档使用的 MIME 类型是 text/html,并且对内容进行了 gzip 压缩</p>\n</blockquote>\n<h3 id=\"HTTP协议的状态码\"><a href=\"#HTTP协议的状态码\" class=\"headerlink\" title=\"HTTP协议的状态码\"></a>HTTP协议的状态码</h3><p>1xx(临时响应)表示临时响应并需要请求者继续执行操作的状态代码。<br>2xx (成功) 表示成功处理了请求的状态代码。<br>3xx (重定向) 表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。<br>4xx(请求错误) 这些状态代码表示请求可能出错,妨碍了服务器的处理。<br>5xx(服务器错误) 这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。<br>常见的:200 – 服务器成功返回网页 |404 – 请求的网页不存在 |503 – 服务不可用 </p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align:center\">状态码</th>\n<th style=\"text-align:center\">中文描述 </th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align:center\">100</td>\n<td style=\"text-align:center\">(继续) 请求者应当继续提出请求。 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">101</td>\n<td style=\"text-align:center\">(切换协议) 请求者已要求服务器切换协议,服务器已确认并准备切换。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">102</td>\n<td style=\"text-align:center\">(已接受)已经接受请求,但未处理完成</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">200</td>\n<td style=\"text-align:center\">(成功) 服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">201</td>\n<td style=\"text-align:center\">(已创建) 请求成功并且服务器创建了新的资源。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">202</td>\n<td style=\"text-align:center\">(已接受) 服务器已接受请求,但尚未处理。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">203</td>\n<td style=\"text-align:center\">(非授权信息) 服务器已成功处理了请求,但返回的信息可能来自另一来源。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">204</td>\n<td style=\"text-align:center\">(无内容) 服务器成功处理了请求,但没有返回任何内容。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">205</td>\n<td style=\"text-align:center\">(重置内容) 服务器成功处理了请求,但没有返回任何内容。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">206</td>\n<td style=\"text-align:center\">(部分内容) 服务器成功处理了部分 GET 请求。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">300</td>\n<td style=\"text-align:center\">(多种选择) 针对请求,服务器可执行多种操作。 服务器可根据请求者 (user agent) 选择一项操作,或提供操作列表供请求者选择。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">301</td>\n<td style=\"text-align:center\">(永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">302</td>\n<td style=\"text-align:center\">(临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">303</td>\n<td style=\"text-align:center\">(查看其他位置) 请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">304</td>\n<td style=\"text-align:center\">(未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">305</td>\n<td style=\"text-align:center\">(使用代理) 请求者只能使用代理访问请求的网页。 如果服务器返回此响应,还表示请求者应使用代理。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">307</td>\n<td style=\"text-align:center\">(临时重定向) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">400</td>\n<td style=\"text-align:center\">(错误请求) 服务器不理解请求的语法。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">401</td>\n<td style=\"text-align:center\">未授权) 请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">403</td>\n<td style=\"text-align:center\">(禁止) 服务器拒绝请求。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">404</td>\n<td style=\"text-align:center\">(未找到) 服务器找不到请求的网页。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">405</td>\n<td style=\"text-align:center\">(方法禁用) 禁用请求中指定的方法。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">406</td>\n<td style=\"text-align:center\">(不接受) 无法使用请求的内容特性响应请求的网页。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">407</td>\n<td style=\"text-align:center\">(需要代理授权) 此状态代码与 401(未授权)类似,但指定请求者应当授权使用代理。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">408</td>\n<td style=\"text-align:center\">(请求超时) 服务器等候请求时发生超时。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">409</td>\n<td style=\"text-align:center\">(冲突) 服务器在完成请求时发生冲突。 服务器必须在响应中包含有关冲突的信息。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">410</td>\n<td style=\"text-align:center\">(已删除) 如果请求的资源已永久删除,服务器就会返回此响应。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">411</td>\n<td style=\"text-align:center\">(需要有效长度) 服务器不接受不含有效内容长度标头字段的请求。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">412</td>\n<td style=\"text-align:center\">(未满足前提条件) 服务器未满足请求者在请求中设置的其中一个前提条件。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">413</td>\n<td style=\"text-align:center\">(请求实体过大) 服务器无法处理请求,因为请求实体过大,超出服务器的处理能力。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">414</td>\n<td style=\"text-align:center\">(请求的 URI 过长) 请求的 URI(通常为网址)过长,服务器无法处理。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">415</td>\n<td style=\"text-align:center\">(不支持的媒体类型) 请求的格式不受请求页面的支持。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">416</td>\n<td style=\"text-align:center\">(请求范围不符合要求) 如果页面无法提供请求的范围,则服务器会返回此状态代码。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">417</td>\n<td style=\"text-align:center\">(未满足期望值) 服务器未满足”期望”请求标头字段的要求。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">500</td>\n<td style=\"text-align:center\">(服务器内部错误) 服务器遇到错误,无法完成请求。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">501</td>\n<td style=\"text-align:center\">(尚未实施) 服务器不具备完成请求的功能。 例如,服务器无法识别请求方法时可能会返回此代码。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">502</td>\n<td style=\"text-align:center\">(错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">503</td>\n<td style=\"text-align:center\">(服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">504</td>\n<td style=\"text-align:center\">(网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。 </td>\n</tr>\n<tr>\n<td style=\"text-align:center\">505</td>\n<td style=\"text-align:center\">(HTTP 版本不受支持) 服务器不支持请求中所用的 HTTP 协议版本。</td>\n</tr>\n</tbody>\n</table>\n<h3 id=\"cookies和会话\"><a href=\"#cookies和会话\" class=\"headerlink\" title=\"cookies和会话\"></a>cookies和会话</h3><p>因为HTTP是无状态的,对事物处理没有记忆能力。cookies和会话出现用于保持HTTP连接状态,<br>会话在服务端,用来保存用户的会话信息;cookies在客户端,浏览器下次访问网页时会自动附带上它发送给服务器,<br>服务器通过识别cookies找到对应的会话判断用户状态。<br>PS:关闭浏览器不会导致会话被删除,反而没有存储到硬盘上的cookie会消失,因此为了节省存储空间需要为会话设置一个失效时间。</p>"}],"PostAsset":[],"PostCategory":[],"PostTag":[{"post_id":"cjwr0llvf0000bkvl7r7ggvwr","tag_id":"cjwr0llvl0002bkvleh4sfsfc","_id":"cjwr0llvv000dbkvle8jln9zg"},{"post_id":"cjwr0llvf0000bkvl7r7ggvwr","tag_id":"cjwr0llvq0006bkvlk3qe17fe","_id":"cjwr0llvv000ebkvldwt1cnc3"},{"post_id":"cjwr0llvf0000bkvl7r7ggvwr","tag_id":"cjwr0llvs0009bkvleweckky5","_id":"cjwr0llvx000gbkvljuwqah0j"},{"post_id":"cjwr0llvk0001bkvlicn04r5l","tag_id":"cjwr0llvv000cbkvluoo0owp5","_id":"cjwr0llvx000ibkvlg3f2uzbw"},{"post_id":"cjwr0llvk0001bkvlicn04r5l","tag_id":"cjwr0llvq0006bkvlk3qe17fe","_id":"cjwr0llvx000jbkvl6f4nhbou"},{"post_id":"cjwr0llvn0003bkvlyhrfgjfm","tag_id":"cjwr0llvx000hbkvl5kkhdofg","_id":"cjwr0llvz000obkvlz92bvk2j"},{"post_id":"cjwr0llvn0003bkvlyhrfgjfm","tag_id":"cjwr0llvy000kbkvly5afdzz4","_id":"cjwr0llvz000pbkvl1nrrjedt"},{"post_id":"cjwr0llvn0003bkvlyhrfgjfm","tag_id":"cjwr0llvy000lbkvlv4pxeivk","_id":"cjwr0llvz000rbkvl2vbjaeqq"},{"post_id":"cjwr0llvn0003bkvlyhrfgjfm","tag_id":"cjwr0llvq0006bkvlk3qe17fe","_id":"cjwr0llvz000sbkvlsba79gty"},{"post_id":"cjwr0llvo0004bkvluwexs1gi","tag_id":"cjwr0llvz000nbkvlkk335hem","_id":"cjwr0llw1000wbkvlxjy3qcve"},{"post_id":"cjwr0llvo0004bkvluwexs1gi","tag_id":"cjwr0llvz000qbkvl4duzbkt4","_id":"cjwr0llw1000xbkvl1kpf6gor"},{"post_id":"cjwr0llvo0004bkvluwexs1gi","tag_id":"cjwr0llw0000tbkvlhg1sh003","_id":"cjwr0llw1000zbkvlokh1oybt"},{"post_id":"cjwr0llvo0004bkvluwexs1gi","tag_id":"cjwr0llvq0006bkvlk3qe17fe","_id":"cjwr0llw10010bkvlsecuayzz"},{"post_id":"cjwr0llvp0005bkvlbktmg3ve","tag_id":"cjwr0llvx000hbkvl5kkhdofg","_id":"cjwr0llw20013bkvlmigganlg"},{"post_id":"cjwr0llvp0005bkvlbktmg3ve","tag_id":"cjwr0llw1000ybkvl9zvfnn7w","_id":"cjwr0llw20014bkvlngnqo37p"},{"post_id":"cjwr0llvp0005bkvlbktmg3ve","tag_id":"cjwr0llvq0006bkvlk3qe17fe","_id":"cjwr0llw30016bkvlhenq5qxu"},{"post_id":"cjwr0llvq0007bkvlxrdbmenp","tag_id":"cjwr0llvx000hbkvl5kkhdofg","_id":"cjwr0llw30018bkvlwad2u07g"},{"post_id":"cjwr0llvq0007bkvlxrdbmenp","tag_id":"cjwr0llw20015bkvlz0qbiduo","_id":"cjwr0llw40019bkvl4dxkdkvz"},{"post_id":"cjwr0llvq0007bkvlxrdbmenp","tag_id":"cjwr0llvq0006bkvlk3qe17fe","_id":"cjwr0llw4001bbkvl10vc0154"},{"post_id":"cjwr0llvr0008bkvlcazs79o9","tag_id":"cjwr0llvx000hbkvl5kkhdofg","_id":"cjwr0llw4001dbkvlqa8fmfyn"},{"post_id":"cjwr0llvr0008bkvlcazs79o9","tag_id":"cjwr0llw4001abkvl0qj8739e","_id":"cjwr0llw5001ebkvlom44sn4b"},{"post_id":"cjwr0llvr0008bkvlcazs79o9","tag_id":"cjwr0llvq0006bkvlk3qe17fe","_id":"cjwr0llw5001gbkvl77h9op1w"},{"post_id":"cjwr0llvs000abkvlh12udhhz","tag_id":"cjwr0llvq0006bkvlk3qe17fe","_id":"cjwr0llw5001hbkvlgithrq55"},{"post_id":"cjwr0llvs000abkvlh12udhhz","tag_id":"cjwr0llw4001cbkvler1s3cis","_id":"cjwr0llw6001ibkvlkvqeblwo"},{"post_id":"cjwr0llvt000bbkvltjcgwnqp","tag_id":"cjwr0llw5001fbkvl466pt313","_id":"cjwr0llw6001jbkvlu5310ga7"},{"post_id":"cjwr0llvt000bbkvltjcgwnqp","tag_id":"cjwr0llvl0002bkvleh4sfsfc","_id":"cjwr0llw6001kbkvlqn33i9mi"},{"post_id":"cjwr0llvt000bbkvltjcgwnqp","tag_id":"cjwr0llvq0006bkvlk3qe17fe","_id":"cjwr0llw6001lbkvl80cmtv3o"},{"post_id":"cjwr0llwe001mbkvldlqzqsc8","tag_id":"cjwr0llwg001obkvlfbwe0jt1","_id":"cjwr0llwi001qbkvlsfkkgu2q"},{"post_id":"cjwr0llwg001nbkvlx8k1a3sm","tag_id":"cjwr0llwh001pbkvl2rtlbnfe","_id":"cjwr0llwi001rbkvlk4p8digj"},{"post_id":"cjwr0llwg001nbkvlx8k1a3sm","tag_id":"cjwr0llvq0006bkvlk3qe17fe","_id":"cjwr0llwj001sbkvlk9tn9a7t"},{"post_id":"cjwr0llwg001nbkvlx8k1a3sm","tag_id":"cjwr0llw4001cbkvler1s3cis","_id":"cjwr0llwj001tbkvlpoe70969"}],"Tag":[{"name":"git","_id":"cjwr0llvl0002bkvleh4sfsfc"},{"name":"日记","_id":"cjwr0llvq0006bkvlk3qe17fe"},{"name":"版本控制","_id":"cjwr0llvs0009bkvleweckky5"},{"name":"Web安全","_id":"cjwr0llvv000cbkvluoo0owp5"},{"name":"Python","_id":"cjwr0llvx000hbkvl5kkhdofg"},{"name":"jenkins","_id":"cjwr0llvy000kbkvly5afdzz4"},{"name":"itchat","_id":"cjwr0llvy000lbkvlv4pxeivk"},{"name":"Node","_id":"cjwr0llvz000nbkvlkk335hem"},{"name":"MongoDB","_id":"cjwr0llvz000qbkvl4duzbkt4"},{"name":"服务部署","_id":"cjwr0llw0000tbkvlhg1sh003"},{"name":"unittest","_id":"cjwr0llw1000ybkvl9zvfnn7w"},{"name":"基础知识","_id":"cjwr0llw20015bkvlz0qbiduo"},{"name":"requests","_id":"cjwr0llw4001abkvl0qj8739e"},{"name":"学习Vlog","_id":"cjwr0llw4001cbkvler1s3cis"},{"name":"SSH","_id":"cjwr0llw5001fbkvl466pt313"},{"name":"css","_id":"cjwr0llwg001obkvlfbwe0jt1"},{"name":"HTTP","_id":"cjwr0llwh001pbkvl2rtlbnfe"}]}}