完成登陸之後,就會進入後臺管理系統的主界面,由於這個是小項目,因此導航菜單所有固化在HTML中,不能修改。通常後臺還會有一個歡迎頁或關鍵數據展現的主頁面,小項目也沒有多大的必要,因此登陸後直接進入公司介紹編輯頁面。javascript
首先咱們來看一下公司介紹頁面內容html
看上去功能好像很簡單,其實咱們要處理的東西仍是挺多的。前端
從頁面上看,咱們須要有一個記錄讀取的接口,來獲取公司介紹的內容,並展現在頁面上。固然如今數據庫裏面沒有記錄存在,因此咱們還須要向數據庫的信息表(infomation)中插入一條公司介紹的記錄,這樣好直接進行編輯(由於公司介紹不會有不少條記錄,通常定了後就不會再改變,因此只須要在數據庫的信息表裏插入一條就能夠了)java
另外,從界面上看,咱們還須要有一個上傳文件的接口,能夠上傳圖片和文件;還須要一個更新公司介紹內容的接口。python
還有須要修改幾個地方,有上傳文件,確定須要有下載的接口,因此須要增長一個下載的路由(python與其餘語言不同的地方是,全部訪問都必須經過路由,因此上傳的或放在目錄中的文件須要統必定義一個接口來處理,否則用戶訪問不了,雖然有點麻煩,但這樣處理也安全不少,用戶上傳任何含有木馬或程序的文件,它也沒法在服務器端執行);nginx配置文件也須要修改一下,增長下載路徑規則,這樣就能夠直接經過nginx訪問下載路徑了。nginx
向數據庫中添加公司介紹記錄web
運行pgAdmin連上數據庫,而後按第4章的作法,打開sql查詢分析器,運行下面代碼添加一條數據庫記錄sql
INSERT INTO infomation(id, title) VALUES (1, '公司介紹');
添加公司介紹記錄讀取接口數據庫
1 @get('/api/about/') 2 def callback(): 3 """ 4 獲取指定記錄 5 """ 6 sql = """select * from infomation where id = 1""" 7 # 讀取記錄 8 result = db_helper.read(sql) 9 if result: 10 # 直接輸出json 11 return web_helper.return_msg(0, '成功', result[0]) 12 else: 13 return web_helper.return_msg(-1, "查詢失敗")
由於公司介紹id添加後不會再改變,因此sql語句直接綁死id爲1,另外,執行數據庫查詢之後,返回的是列表,因此返回記錄時要加上序號:result[0]json
啓動debug(對main.py點擊右鍵=》debug),將用戶登陸判斷那兩行註釋掉(否則直接訪問會返回-404,「您的登陸已失效,請從新登陸」提示),在瀏覽器輸入:http://127.0.0.1:9090/api/about/就能夠看到返回結果(結果的中文字符是unicode編碼,須要用站長工具轉換一下才能夠轉爲下載效果)
{"msg": "成功", "data": {"content": "", "front_cover_img": "", "id": 1, "title": "公司介紹", "add_time": "2017-10-31 14:17:45"}, "state": 0}
添加公司介紹內容修改接口
1 @put('/api/about/') 2 def callback(): 3 """ 4 修改記錄 5 """ 6 front_cover_img = web_helper.get_form('front_cover_img', '圖片') 7 content = web_helper.get_form('content', '內容', is_check_special_char=False) 8 # 防sql注入攻擊處理 9 content = string_helper.filter_str(content, "'") 10 # 防xss攻擊處理 11 content = string_helper.clear_xss(content) 12 13 # 更新記錄 14 sql = """update infomation set front_cover_img=%s, content=%s where id=1""" 15 vars = (front_cover_img, content,) 16 # 寫入數據庫 17 db_helper.write(sql, vars) 18 19 # 直接輸出json 20 return web_helper.return_msg(0, '成功')
由於公司介紹只須要一條記錄就夠了,前面使用手動方式向數據庫添加記錄,因此代碼中咱們就不須要寫添加的方法
修改記錄使用put方式接收:@put('/api/about/')
從界面圖片中能夠看到,有文章標題、首頁圖片和文章內容,由於標題不須要進行修改,因此咱們修改接口只須要處理剩下兩項就能夠了。
由於提交的內容含有HTML代碼,因此使用web_helper.get_form提取值時,須要使用is_check_special_char參數,設置爲不檢查特殊符號,否則會接收不了。另外接收到參數值之後,咱們須要對它進行防sql注入和防xss處理。
clear_xss()函數是string_helper包新增的清除xss攻擊標籤用的,它會過濾掉xss的攻擊代碼。詳細代碼以下:
def clear_xss(html): """ 清除xss攻擊標籤 :param html: 要處理的html :return: """ tags = ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code', 'em', 'i', 'li', 'ol', 'strong', 'ul'] tags.extend( ['div', 'p', 'hr', 'br', 'pre', 'code', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'del', 'dl', 'img', 'sub', 'sup', 'u', 'table', 'thead', 'tr', 'th', 'td', 'tbody', 'dd', 'caption', 'blockquote', 'section']) attributes = {'*': ['class', 'id'], 'a': ['href', 'title', 'target'], 'img': ['src', 'style', 'width', 'height']} return bleach.linkify(bleach.clean(html, tags=tags, attributes=attributes))
clear_xss()函數中咱們使用了bleach這個庫(須要安裝:pip install bleach),它是一個基於白名單、經過轉義或去除標籤和屬性的方式,來對HTML文本淨化的python庫。
咱們在string_helper_test.py這個測試單元中添加一個測試用例,來測試一下這個函數的使用效果
def test_clear_xss(self): print('-----test_clear_xss------') print(string_helper.clear_xss('<script src="javascript:alert(1);">abc</script>')) print(string_helper.clear_xss('<iframe src="javascript:alert(1);">abc</iframe>')) print(string_helper.clear_xss('<div style="width:0;height:0;background:url(javascript:document.body.onload = function(){alert(/XSS/);};">div</div>')) print(string_helper.clear_xss('<img src = "#"/**/onerror = alert(/XSS/)>')) print(string_helper.clear_xss('<img src = j ava script:al er t(/XSS/)>')) print(string_helper.clear_xss("""<img src = j ava script :a ler t(/xss/)>""")) print(string_helper.clear_xss('<img src="javacript:alert(\'abc\')"></img>')) print(string_helper.clear_xss('<img src="https://www.baidu.com/img/baidu_jgylogo3.gif"></img>')) print(string_helper.clear_xss('<p src="javascript:alert(1);">abc</p>')) print(string_helper.clear_xss("""<input type="text" value="琅琊榜" onclick="javascript:alert('handsome boy')">""")) print(string_helper.clear_xss('<p onclick="javascript:alert("handsome boy")>abc</p>')) print(string_helper.clear_xss('<a href="javascript:alert(1);">abc</a>')) print(string_helper.clear_xss('<a href="/api/">abc</a>')) print(string_helper.clear_xss('<a href="http://www.baidu.com">abc</a>')) print(string_helper.clear_xss('<marquee onstart="alert(/XSS/)">文字</marquee>')) print(string_helper.clear_xss('<div style="" onmouseenter="alert(/XSS/)">文字</div>')) print(string_helper.clear_xss('<li style = "TEST:e-xpression(alert(/XSS/))"></li>')) print(string_helper.clear_xss('<input id = 1 type = "text" value="" <script>alert(/XSS/)</script>"/>')) print(string_helper.clear_xss('<base href="http://www.labsecurity.org"/>')) print(string_helper.clear_xss('<div id="x">alert%28document.cookie%29%3B</div>')) print(string_helper.clear_xss('<limited_xss_point>eval(unescape(x.innerHTML));</limited_xss_point>'))
執行後輸出結果:
------ini------ -----test_clear_xss------ <script src="javascript:alert(1);">abc</script> <iframe src="javascript:alert(1);">abc</iframe> <div>div</div> <img src="#"> <img src="j"> <img src="j"> <img> <img src="https://www.baidu.com/img/baidu_jgylogo3.gif"> <p>abc</p> <input onclick="javascript:alert('handsome boy')" type="text" value="琅琊榜"> <p>abc</p> <a>abc</a> <a href="/api/" rel="nofollow">abc</a> <a href="http://www.baidu.com" rel="nofollow">abc</a> <marquee onstart="alert(/XSS/)">文字</marquee> <div>文字</div> <li></li> <input <script="" id="1" type="text" value="">alert(/XSS/)"/> <base href="<a href="http://www.labsecurity.org" rel="nofollow">http://www.labsecurity.org</a>"> <div id="x">alert%28document.cookie%29%3B</div> <limited_xss_point>eval(unescape(x.innerHTML));</limited_xss_point> ------clear------
能夠看到,對於富文本編輯器提交的代碼,bleach基本知足了咱們的防範xss攻擊的處理需求
添加上傳接口(PS:咱們使用的文本編輯器是百度的ueditor,由於它沒有python的上傳處理代碼,因此咱們須要動手編輯上傳接口,以及html上也要進行對應的修改)
#!/usr/bin/evn python # coding=utf-8 import os from bottle import post, request from common import datetime_helper, random_helper, log_helper @post('/api/files/') def callback(): """ 修改記錄 """ # 初始化輸出值 result = { "state": "FAIL", "url": "", "title": "上傳失敗", "original": "" } # 獲取上傳文件 try: # upfile爲前端HTML上傳控件名稱 upload = request.files.get('upfile') # 若是沒有讀取到上傳文件或上傳文件的方式不正確,則返回上傳失敗狀態 if not upload: return result # 取出文件的名字和後綴 name, ext = os.path.splitext(upload.filename) # 給上傳的文件重命名,默認上傳的是圖片 if ext and ext != '': file_name = datetime_helper.to_number() + random_helper.get_string(5) + ext else: file_name = datetime_helper.to_number() + random_helper.get_string(5) + '.jpg' upload.filename = file_name # 設置文件存儲的相對路徑 filepath = '/upload/' + datetime_helper.to_number('%Y%m%d') + '/' # 組合成服務器端存儲絕對路徑 upload_path = os.getcwd() + filepath # 若是目錄不存在,則建立目錄 if not os.path.exists(upload_path): os.mkdir(upload_path) # 保存文件 upload.save(upload_path + upload.filename, overwrite=True) # 設置輸出參數(返回相對路徑給客戶端) result['title'] = result['original'] = upload.filename result['url'] = filepath + upload.filename result['state'] = 'SUCCESS' except Exception as e: log_helper.error('上傳失敗:' + str(e.args)) # 直接輸出json return result
PS:這裏只作了上傳文件處理,沒有上傳成功之後存儲到數據庫中統一管理,若是前端反覆上傳,會形成服務器存儲不少多餘文件的問題,你們能夠本身發揮想象與動手能力,看看怎麼解決這個問題。對於這個問題會在第二部分統一處理。
添加上傳文件存儲文件夾:直接在項目的要目錄下建立upload文件夾
修改main.py文件配置,並建立文件下載路由
導入的bottle庫添加response, static_file這兩個包,response用於設置輸出文件類型爲二進制數據傳輸格式,這樣設置後,上傳的各類類型文件均可如下載;static_file是使用安全的方式讀取文件並輸出到客戶端
from bottle import default_app, get, run, request, hook, route, response, static_file
在第26行插入下面代碼,初始化上傳文件存儲路徑
# 定義upload爲上傳文件存儲路徑 upload_path = os.path.join(program_path, 'upload')
添加下載文件訪問路由,設置後只要放在upload目錄下的文件均可以直接經過瀏覽器下載
@get('/upload/<filepath:path>') def upload_static(filepath): """設置靜態內容路由""" response.add_header('Content-Type', 'application/octet-stream') return static_file(filepath, root=upload_path)
作完以上設置,上傳與更新就沒有問題了,上傳的圖片直接使用http://127.0.0.1:9090/upload/xxx.jpg方式就能夠訪問了,若是想要使用81端口,也就是經過nginx訪問,那就須要再配置一下
打開nginx配置文件 :E:\Service\nginx-1.11.5\conf\nginx.conf
將location ~* ^/(index|api)/ 修改成 location ~* ^/(index|api|upload)/
而後在windows任務管理器(鍵盤同時按Ctrl+Alt+Del鍵,點擊啓動任務管理器),找到nginx_service.exe,右鍵=》結束進程樹
從新打開服務(控制面板=》全部控制面板項=》管理工具=》服務),啓動nginx_service服務
前端頁面相關修改
向/lib/ueditor/1.4.3/目錄中添加python文件夾,將添加config.json這個配置項
修改/lib/ueditor/1.4.3/ueditor.config.js 配置項中 服務器統一請求接口路徑 爲 /api/files/
本文對應的源碼包裏有ueditor編輯器最新代碼(剛剛去百度下載的),去掉了多餘的文件,你們可直接刪除lib目錄裏的ueditor這個文件夾,使用源碼包裏的替換上去就能夠了
前端頁面的javascript腳本添加了ueditor編輯器初始化、文件上傳和表單提交等功能,可直接替換about_edit.html文件,具體你們本身研究一下。
最終效果:
另外,聯繫咱們的功能與公司介紹差很少,在這裏留一下做業給你們本身嘗試作一個聯繫咱們編輯頁面出來,下一篇會給聯繫咱們編輯頁面源碼給你們
版權聲明:本文原創發表於 博客園,做者爲 AllEmpty 本文歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然視爲侵權。
python開發QQ羣:669058475(本羣已滿)、733466321(能夠加2羣) 做者博客:http://www.cnblogs.com/EmptyFS/