Skip to content

Commit d711683

Browse files
committed
louplus 第6周挑战示例代码
1 parent eebd227 commit d711683

6 files changed

Lines changed: 230 additions & 0 deletions

File tree

19-fix-auth/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
本挑战的参考代码需要配合 rmon 项目的完整项目代码工作, rmon 项目完整代码可以在 楼+课程中找到下载地址。

19-fix-auth/auth.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
""" rmon.views.auth
2+
3+
实现用户认证登录功能
4+
"""
5+
6+
from datetime import datetime
7+
from flask import request
8+
9+
from rmon.models import User
10+
from rmon.common.errors import AuthenticationError
11+
from rmon.common.rest import RestView
12+
13+
14+
15+
class AuthView(RestView):
16+
"""认证视图控制器
17+
"""
18+
19+
def post(self):
20+
"""登录认证用户
21+
22+
用户可以使用昵称或者邮箱进行登录,登录成功后返回用于后续认证的 token
23+
"""
24+
25+
data = request.get_json()
26+
if data is None:
27+
raise AuthenticationError(403, 'user name or password required')
28+
29+
name = data.get('name')
30+
password = data.get('password')
31+
32+
if not name or not password:
33+
raise AuthenticationError(403, 'user name or password required')
34+
35+
# FIXME 只有管理员用户允许登录管理后台
36+
user = User.authenticate(name, password)
37+
if not user.is_admin:
38+
raise AuthenticationError(403, 'user name or password required')
39+
40+
user.login_at = datetime.utcnow()
41+
user.save()
42+
return {'ok': True, 'token': user.generate_token()}
43+
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import re
2+
import os
3+
from wechatpy.messages import TextMessage
4+
from wechatpy import create_reply
5+
from qqwry import QQwry
6+
7+
class CommandHandler:
8+
command = ''
9+
10+
def check_match(self, message):
11+
"""检查消息是否匹配命令模式
12+
"""
13+
if not isinstance(message, TextMessage):
14+
return False
15+
if not message.content.strip().lower().startswith(self.command):
16+
return False
17+
return True
18+
19+
20+
class IPLocationHandler(CommandHandler):
21+
command = 'ip'
22+
23+
def __init__(self):
24+
file = os.environ.get('QQWRY_DAT', 'qqwry.dat')
25+
self.q = QQwry()
26+
self.q.load_file(file)
27+
28+
def handle(self, message):
29+
if not self.check_match(message):
30+
return
31+
parts = message.content.strip().split()
32+
if len(parts) == 1 or len(parts) > 2:
33+
return create_reply('IP地址无效', message)
34+
ip = parts[1]
35+
pattern = r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'
36+
if not re.match(pattern, ip):
37+
return create_reply('IP地址无效', message)
38+
result = self.q.lookup(ip)
39+
if result is None:
40+
return create_reply('未找到', message)
41+
else:
42+
return create_reply(result[0], message)

21-refresh-token/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
本挑战的参考代码需要配合 rmon 项目的完整项目代码工作, rmon 项目完整代码可以在 楼+课程中找到下载地址。

21-refresh-token/auth.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
""" rmon.views.auth
2+
3+
实现用户认证登录功能
4+
"""
5+
6+
from datetime import datetime
7+
from flask import request, g
8+
9+
from rmon.models import User
10+
from rmon.common.errors import AuthenticationError
11+
from rmon.common.rest import RestView
12+
13+
from .decorators import TokenAuthenticate
14+
15+
16+
class AuthView(RestView):
17+
"""认证视图控制器
18+
"""
19+
20+
def post(self):
21+
"""登录认证用户
22+
23+
用户可以使用昵称或者邮箱进行登录,登录成功后返回用于后续认证的 token
24+
"""
25+
26+
# FIXME 没有处理 data 为 None 的情况
27+
data = request.get_json()
28+
if data is None:
29+
raise AuthenticationError(403, 'user name or password required')
30+
31+
name = data.get('name')
32+
password = data.get('password')
33+
34+
if not name or not password:
35+
raise AuthenticationError(403, 'user name or password required')
36+
37+
# FIXME 只有管理员用户允许登录管理后台
38+
user = User.authenticate(name, password)
39+
if not user.is_admin:
40+
raise AuthenticationError(403, 'user name or password required')
41+
42+
user.login_at = datetime.utcnow()
43+
user.save()
44+
return {'ok': True, 'token': user.generate_token()}
45+
46+
47+
class RefreshTokenView(RestView):
48+
49+
method_decorators = (TokenAuthenticate(admin=False, verify_exp=False), )
50+
51+
def get(self):
52+
return {'ok': True, 'token': g.user.generate_token()}

21-refresh-token/decorators.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
""" rmon.views.decorators
2+
3+
该模块实现了视图控制器装饰器
4+
"""
5+
6+
from functools import wraps
7+
from flask import g, request
8+
9+
from rmon.common.errors import RestError, AuthenticationError
10+
from rmon.models import User
11+
12+
13+
class ObjectMustBeExist:
14+
"""该装饰器确保操作的对象必须存在
15+
"""
16+
17+
def __init__(self, object_class):
18+
"""
19+
Args:
20+
object_class (class): 数据库对象
21+
"""
22+
23+
self.object_class = object_class
24+
25+
def __call__(self, func):
26+
"""装饰器实现
27+
"""
28+
@wraps(func)
29+
def wrapper(*args, **kwargs):
30+
"""
31+
Args:
32+
object_id (int): SQLAlchemy object id
33+
"""
34+
35+
object_id = kwargs.get('object_id')
36+
if object_id is None:
37+
raise RestError(404, 'object not exist')
38+
39+
obj = self.object_class.query.get(object_id)
40+
if obj is None:
41+
raise RestError(404, 'object not exist')
42+
43+
g.instance = obj
44+
return func(*args, **kwargs)
45+
46+
return wrapper
47+
48+
49+
class TokenAuthenticate:
50+
"""通过 jwt 认证用户
51+
52+
验证 HTTP Authorization 头所包含的 token
53+
"""
54+
55+
def __init__(self, admin=True, verify_exp=True):
56+
"""
57+
Args:
58+
admin(bool): 是否需要验证管理员权限
59+
"""
60+
self.admin = admin
61+
self.verify_exp = verify_exp
62+
63+
def __call__(self, func):
64+
"""装饰器实现
65+
"""
66+
@wraps(func)
67+
def wrapper(*args, **kwargs):
68+
69+
pack = request.headers.get('Authorization', None)
70+
if pack is None:
71+
raise AuthenticationError(401, 'token not found')
72+
parts = pack.split()
73+
# Authorization 头部值必须为 'jwt <token_value>' 这种形式
74+
if parts[0].lower() != 'jwt':
75+
raise AuthenticationError(401, 'invalid token header')
76+
elif len(parts) == 1:
77+
raise AuthenticationError(401, 'token missing')
78+
elif len(parts) > 2:
79+
raise AuthenticationError(401, 'invalid token')
80+
token = parts[1]
81+
user = User.verify_token(token, verify_exp=self.verify_exp)
82+
83+
# 如果需要验证是否是管理员
84+
if self.admin and not user.is_admin:
85+
raise AuthenticationError(403, 'no permission')
86+
87+
# 将当前用户存入到 g 对象中
88+
g.user = user
89+
return func(*args, **kwargs)
90+
return wrapper
91+

0 commit comments

Comments
 (0)