Skip to content
This repository has been archived by the owner on Mar 1, 2023. It is now read-only.

Commit

Permalink
2020/11/16 添加redis黑名单和缓存
Browse files Browse the repository at this point in the history
  • Loading branch information
xmmmmmovo committed Nov 16, 2020
1 parent 39d28b2 commit 31349a9
Show file tree
Hide file tree
Showing 16 changed files with 64 additions and 45 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
| :----: | :----------------------------------------------------------: |
| 安卓 | 1. 过于简单,没有时间查询等功能 |
| 前端 | 1. 查询方面寻找更好的显示方案 |
| 后端 | 1. 由于部分字段不能用字符串查询所以用了拼接字符串,存在注入风险 2. jwt部分没有做黑名单处理,存在信息盗用风险 3.大部分查询还未进行缓存处理,待理清业务后进行4.因为个人项目就没有写service层 |
| 后端 | 1. 由于部分字段不能用字符串查询所以用了拼接字符串,存在注入风险 2.因为个人项目就没有写service层 |
| 硬件 | 1. 缺少开关等互动装置 2. 缺少灯管等反馈装置3.RFID有时候会莫名其妙无法识别 |

## 截图
Expand Down
5 changes: 2 additions & 3 deletions server/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@
from bp.user_app import user_bp
from bp.hardware_app import hardware_bp
from cache import redis
from mqtt import connect_mqtt
from config import config
from utils.pwd_utils import bcrypt
from utils.jwt_utils import jwt_manager
from utils.jwt_utils import jwt_manager, JWT_ACCESS_TOKEN_EXPIRES
from datetime import timedelta

app = Flask(__name__)
Expand All @@ -25,7 +24,7 @@

app.config['REDIS_URL'] = config['redis']['url']
app.config['JWT_SECRET_KEY'] = config['app']['jwt_secret_key']
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(days=30)
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = JWT_ACCESS_TOKEN_EXPIRES

log = Logger()
cors = CORS(app, resources={r"/*": {"origins": "*"}})
Expand Down
2 changes: 1 addition & 1 deletion server/bp/hardware_app.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from flask import Blueprint, request
from flask_jwt_extended import jwt_required, get_jwt_claims

from model.Pagination import Pagination
from model.pagination import Pagination
from response import response_success
import uuid
from db.hardware_dao import insert_hardware, get_id_by_uuid, update_threshold_by_uuid, get_hardware_pagination, \
Expand Down
2 changes: 1 addition & 1 deletion server/bp/rfid_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from flask_jwt_extended import jwt_required, get_jwt_claims

from db.rfid_dao import get_rfid_pagination, get_rfid_pagination_by_username, count_total
from model.Pagination import Pagination
from model.pagination import Pagination
from response import response_success
from exception.custom_exceptions import DBException, ContentEmptyException, DataNotFoundException, \
UnAuthorizedException, DataNotSatisfyException, UserNotFoundException, CannotDeleteOnlineHardwareException
Expand Down
6 changes: 1 addition & 5 deletions server/bp/sensor_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from db.rfid_dao import get_rfid_pagination, get_rfid_pagination_by_username
from db.sensor_dao import count_total, get_sensor_pagination_by_username, get_sensor_pagination, get_sensor_data_hourly
from model.Pagination import Pagination
from model.pagination import Pagination
from response import response_success
from exception.custom_exceptions import DBException, ContentEmptyException, DataNotFoundException, \
UnAuthorizedException, DataNotSatisfyException, UserNotFoundException, CannotDeleteOnlineHardwareException
Expand Down Expand Up @@ -60,10 +60,6 @@ def sensor_get_data():
total = count_total(
'WHERE hardware_uuid IN (SELECT hardware_uuid FROM user_hardware WHERE user_id = (SELECT id FROM `user` WHERE username = %s))' + where_sql,
username, *args)
for i in range(len(sensor_list)):
sensor_list[i]['temperature'] = str(sensor_list[i]['temperature'])
sensor_list[i]['humidity'] = str(sensor_list[i]['humidity'])
sensor_list[i]['record_time'] = sensor_list[i]['record_time'].strftime("%m/%d/%Y, %H:%M:%S")

return response_success('success', Pagination(page, size, sensor_list, total['len']))

Expand Down
12 changes: 8 additions & 4 deletions server/bp/user_app.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
from flask import Blueprint, request

from cache import set_value
from db.user_dao import get_user_pagination, count_total, count_total_role, get_role_pagination, get_all_users, \
get_all_role
from exception.custom_exceptions import ContentEmptyException, DBException, DataNotFoundException, \
PasswordErrorException, DataNotSatisfyException, UnAuthorizedException, AdminNameErrorException
from model.Pagination import Pagination
from model.pagination import Pagination
from response import response_success
from db import user_dao
from flask_jwt_extended import create_access_token, jwt_required, get_jwt_claims
from flask_jwt_extended import create_access_token, jwt_required, get_jwt_claims, get_raw_jwt, get_jti
from model import user
from flask_loguru import logger

from utils.jwt_utils import permission_required
from utils.jwt_utils import permission_required, JWT_ACCESS_TOKEN_EXPIRES

user_bp = Blueprint('user_app', __name__, url_prefix='/user')

Expand Down Expand Up @@ -59,6 +60,8 @@ def login():
roles = list(map(lambda x: x['name'], user_dao.select_roles_by_userid(data['id'])))

token = create_access_token(identity=user.User(data['username'], roles))
access_jti = get_jti(encoded_token=token)
set_value(access_jti, "false", JWT_ACCESS_TOKEN_EXPIRES * 1.2)

return response_success('success', {'token': token})

Expand Down Expand Up @@ -100,7 +103,8 @@ def change_password():
@user_bp.route("/logout", methods=['DELETE'])
@jwt_required
def logout():
# TODO: 为了安全考虑的redis退出登录 但是为了api易于测试暂时不写
jti = get_raw_jwt()['jti']
set_value(jti, 'true', JWT_ACCESS_TOKEN_EXPIRES * 1.2)
return response_success('success logout', None)


Expand Down
19 changes: 10 additions & 9 deletions server/cache/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,32 @@
from flask_loguru import logger
from json import loads, dumps

from cache.encoder import ComplexEncoder

redis = FlaskRedis()


def get_value(key):
return redis.get(key)


def set_value(key, value):
return redis.set(key, value)
def set_value(key, value, timeout):
return redis.set(key, value, ex=timeout)


def redis_cache(key, timeout):
def __redis_cache(func):
def warpper(*args, **kw):
rel_key = key + str(args) + str(kw)
# 判断缓存是否存在
logger.info('check key: %s' % key)
cache_msg = redis.get(key)
logger.info('check key: %s' % rel_key)
cache_msg = redis.get(rel_key)
if cache_msg is None:
# 若不存在则执行获取数据的方法
# 注意返回数据的类型(字符串,数字,字典,列表均可)
cache_msg = func(*args, **kw)
redis.set(key, dumps(cache_msg), ex=timeout)
else:
cache_msg = loads(cache_msg)
return cache_msg
cache_msg = dumps(func(*args, **kw), cls=ComplexEncoder)
redis.set(rel_key, cache_msg, ex=timeout)
return loads(cache_msg)

return warpper

Expand Down
12 changes: 12 additions & 0 deletions server/cache/encoder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import json
from datetime import date, datetime


class ComplexEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(obj, date):
return obj.strftime('%Y-%m-%d')
else:
return json.JSONEncoder.default(self, obj)
4 changes: 2 additions & 2 deletions server/config.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
mysql:
host: db
port: mx/58MFuLJ6^%.q
port: 3306
user: root
password:
password: mx/58MFuLJ6^%.q
db: greenhouse
charset: utf8

Expand Down
5 changes: 3 additions & 2 deletions server/db/hardware_dao.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from cache import redis_cache
from db import MysqlOp
from .user_dao import select_user_by_username
from flask_loguru import logger
Expand Down Expand Up @@ -30,13 +31,13 @@ def update_threshold_by_uuid(uuid, temperature_limit, humidity_limit):
return MysqlOp().op_sql('UPDATE hardware SET temperature_limit = %s, humidity_limit = %s WHERE uuid = %s',
(temperature_limit, humidity_limit, uuid))


@redis_cache("get_hardware_pagination", 25)
def get_hardware_pagination(page, size, ordered, where_sql, *args):
logger.info('get_hardware_pagination')
return MysqlOp().select_all(f"SELECT * FROM hardware {where_sql} ORDER BY {ordered} ASC LIMIT %s OFFSET %s",
(*args, size, (page - 1) * size))


@redis_cache("get_hardware_pagination_by_username", 25)
def get_hardware_pagination_by_username(username, page, size, ordered, where_sql, *args):
logger.info('get_hardware_pagination_by_username')
return MysqlOp().select_all(
Expand Down
5 changes: 3 additions & 2 deletions server/db/rfid_dao.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from cache import redis_cache
from db import MysqlOp
from flask_loguru import logger


@redis_cache("get_rfid_pagination", 25)
def get_rfid_pagination(page, size, ordered, where_sql, *args):
logger.info('get_sensor_pagination')
return MysqlOp().select_all(f"SELECT username, log_time, hardware_uuid, `name` FROM RFID_log "
Expand All @@ -10,7 +11,7 @@ def get_rfid_pagination(page, size, ordered, where_sql, *args):
f"{where_sql} ORDER BY {ordered} ASC LIMIT %s OFFSET %s",
(*args, size, (page - 1) * size))


@redis_cache("get_rfid_pagination_by_username", 25)
def get_rfid_pagination_by_username(username, page, size, ordered, where_sql, *args):
logger.info('get_hardware_pagination_by_username')
return MysqlOp().select_all(
Expand Down
7 changes: 4 additions & 3 deletions server/db/sensor_dao.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from db import MysqlOp
from flask_loguru import logger
from cache import redis_cache


@redis_cache("get_sensor_pagination", 25)
def get_sensor_pagination(page, size, ordered, where_sql, *args):
logger.info('get_sensor_pagination')
return MysqlOp().select_all(
Expand All @@ -10,7 +11,7 @@ def get_sensor_pagination(page, size, ordered, where_sql, *args):
f" {where_sql} ORDER BY {ordered} ASC LIMIT %s OFFSET %s",
(*args, size, (page - 1) * size))


@redis_cache("get_sensor_pagination_by_username", 25)
def get_sensor_pagination_by_username(username, page, size, ordered, where_sql, *args):
logger.info('get_hardware_pagination_by_username')
return MysqlOp().select_all(
Expand All @@ -25,7 +26,7 @@ def get_sensor_pagination_by_username(username, page, size, ordered, where_sql,
def count_total(where_sql, *args):
return MysqlOp().select_one(f'SELECT COUNT(`id`) as len from sensor_data {where_sql}', (*args,))


@redis_cache("get_sensor_data_hourly", 60)
def get_sensor_data_hourly(uuid):
return MysqlOp().select_all(
'SELECT temperature, humidity, record_time FROM sensor_data WHERE hardware_uuid = %s AND id > ((SELECT MAX(id) FROM sensor_data) - 100)',
Expand Down
11 changes: 0 additions & 11 deletions server/model/hardware.py

This file was deleted.

File renamed without changes.
3 changes: 2 additions & 1 deletion server/mqtt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,5 @@ def connect_mqtt():
return mqtt_client


mqtt_client: mqtt.Client = connect_mqtt()
# mqtt_client: mqtt.Client = connect_mqtt()
mqtt_client: mqtt.Client = None
14 changes: 14 additions & 0 deletions server/utils/jwt_utils.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
from datetime import timedelta
from typing import List

from flask_jwt_extended import JWTManager, verify_jwt_in_request, get_jwt_claims
from functools import wraps
from exception.custom_exceptions import UnAuthorizedException, UserNotFoundException, InsufficientPermissions
from model.user import User
from response import response_fail_exception
from cache import get_value, set_value

jwt_manager = JWTManager()

JWT_ACCESS_TOKEN_EXPIRES = timedelta(days=30)


@jwt_manager.user_claims_loader
def add_claims_to_access_token(user: User):
Expand All @@ -28,6 +33,15 @@ def custom_unauthorized_loader(e):
return response_fail_exception(UnAuthorizedException())


@jwt_manager.token_in_blacklist_loader
def check_if_token_is_revoked(decrypted_token):
jti = decrypted_token['jti']
entry = get_value(jti)
if entry is None:
return True
return entry == 'true'


def permission_required(permission: List[str]):
def _permission_required(func):
@wraps(func)
Expand Down

0 comments on commit 31349a9

Please sign in to comment.