完成登錄以后,就會進入后台管理系統的主界面,因為這個是小項目,所以導航菜單全部固化在HTML中,不能修改。一般后台還會有一個歡迎頁或關鍵數據展示的主頁面,小項目也沒有多大的必要,所以登錄后直接進入公司介紹編輯頁面。
首先我們來看一下公司介紹頁面內容
看上去功能好像很簡單,其實我們要處理的東西還是挺多的。
從頁面上看,我們需要有一個記錄讀取的接口,來獲取公司介紹的內容,並展示在頁面上。當然現在數據庫里面沒有記錄存在,所以我們還需要向數據庫的信息表(infomation)中插入一條公司介紹的記錄,這樣好直接進行編輯(因為公司介紹不會有很多條記錄,一般定了后就不會再改變,所以只需要在數據庫的信息表里插入一條就可以了)
另外,從界面上看,我們還需要有一個上傳文件的接口,可以上傳圖片和文件;還需要一個更新公司介紹內容的接口。
還有需要修改幾個地方,有上傳文件,肯定需要有下載的接口,所以需要增加一個下載的路由(python與其他語言不一樣的地方是,所有訪問都必須通過路由,所以上傳的或放在目錄中的文件需要統一定義一個接口來處理,不然用戶訪問不了,雖然有點麻煩,但這樣處理也安全很多,用戶上傳任何含有木馬或程序的文件,它也無法在服務器端執行);nginx配置文件也需要修改一下,增加下載路徑規則,這樣就可以直接通過nginx訪問下載路徑了。
向數據庫中添加公司介紹記錄
運行pgAdmin連上數據庫,然后按第4章的做法,打開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]
啟動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/