Commit c323519dd4538463109e4b588e0f727fb269575a
1 parent
b6c4f3cc38
Exists in
de
m
Showing
6 changed files
with
264 additions
and
14 deletions
Show diff stats
decrators.py
... | ... | @@ -0,0 +1,111 @@ |
1 | +# coding: utf-8 | |
2 | + | |
3 | +import django.http | |
4 | +from functools import wraps | |
5 | +from django.conf import settings | |
6 | +from django.utils.decorators import available_attrs | |
7 | +from django.shortcuts import resolve_url | |
8 | +from django.utils.six.moves.urllib.parse import urlparse | |
9 | + | |
10 | +def weapp_django_logout(request, response): | |
11 | + assert isinstance(request, django.http.HttpRequest) | |
12 | + assert isinstance(response, django.http.HttpResponse) | |
13 | + # 请求统一认证登出 | |
14 | + response.delete_cookie('pt') | |
15 | + response.delete_cookie('pu') | |
16 | + response.delete_cookie('username') | |
17 | + return response | |
18 | + | |
19 | +def weapp_logout(func=weapp_django_logout): | |
20 | + """ | |
21 | + Django登出视图函数装饰器 | |
22 | + :param func: 处理登出逻辑的函数,接受request和response两个参数 | |
23 | + :return: | |
24 | + """ | |
25 | + return auth_decorator_factory(func) | |
26 | + | |
27 | + | |
28 | +def auth_decorator_factory(func=None): | |
29 | + """ | |
30 | + 登录登出装饰器工厂函数,允许用户自定义外部登录登出函数 | |
31 | + :param func: | |
32 | + :return: | |
33 | + """ | |
34 | + | |
35 | + def decorator(view_func): | |
36 | + @wraps(view_func) | |
37 | + def _wrapped_view(request, *args, **kwargs): | |
38 | + response = view_func(request, *args, **kwargs) | |
39 | + if func: | |
40 | + func(request, response) | |
41 | + return response | |
42 | + | |
43 | + return _wrapped_view | |
44 | + | |
45 | + return decorator | |
46 | + | |
47 | +import json | |
48 | + | |
49 | +from django.http import JsonResponse | |
50 | + | |
51 | + | |
52 | +class QCCRJsonResponse(JsonResponse): | |
53 | + """ | |
54 | + 封装了返回码和信息的JsonResponse类 | |
55 | + """ | |
56 | + def __init__(self, obj=None, code=200, msg="success", status=200, **kwargs): | |
57 | + data = {"code": code, "msg": msg, "result": json.loads(obj) if isinstance(obj, (str, unicode)) else obj} | |
58 | + super(QCCRJsonResponse, self).__init__(data=data, status=status, **kwargs) | |
59 | + | |
60 | + | |
61 | +class QCCRErrorResponse(QCCRJsonResponse): | |
62 | + """ | |
63 | + 请求错误 | |
64 | + """ | |
65 | + def __init__(self, obj=None, code=400, msg="failure", status=400, **kwargs): | |
66 | + super(QCCRErrorResponse, self).__init__(obj=obj, code=code, msg=msg, status=status, **kwargs) | |
67 | + | |
68 | +def user_passes_test(test_func, login_url=None, | |
69 | + redirect_field_name=getattr(settings, "QCCR_REDIRECT_FIELD_NAME", 'redirect')): | |
70 | + """ | |
71 | + 本装饰器通过test_func函数,检查views是否通过相应的检测,未通过则跳转到登录页面;test_func函数接受user对象作为参数,通过则返回True | |
72 | + """ | |
73 | + | |
74 | + def decorator(view_func): | |
75 | + @wraps(view_func, assigned=available_attrs(view_func)) | |
76 | + def _wrapped_view(request, *args, **kwargs): | |
77 | + if test_func(request.user): | |
78 | + return view_func(request, *args, **kwargs) | |
79 | + # 如果未通过检查,且为ajax请求,返回403 | |
80 | + if request.is_ajax(): | |
81 | + return QCCRErrorResponse(code=403, msg="用户身份检查失败", status=403) | |
82 | + # 非ajax请求,跳转到登录页面 | |
83 | + path = request.build_absolute_uri() | |
84 | + resolved_login_url = resolve_url(login_url or getattr(settings, "QCCR_LOGIN_URL", '/login/')) | |
85 | + login_scheme, login_netloc = urlparse(resolved_login_url)[:2] | |
86 | + current_scheme, current_netloc = urlparse(path)[:2] | |
87 | + if ((not login_scheme or login_scheme == current_scheme) and | |
88 | + (not login_netloc or login_netloc == current_netloc)): | |
89 | + path = request.get_full_path() | |
90 | + from django.contrib.auth.views import redirect_to_login | |
91 | + return redirect_to_login(path, resolved_login_url, redirect_field_name) | |
92 | + | |
93 | + return _wrapped_view | |
94 | + | |
95 | + return decorator | |
96 | + | |
97 | + | |
98 | +def weapp_login_required(function=None, | |
99 | + redirect_field_name=getattr(settings, "QCCR_REDIRECT_FIELD_NAME", 'redirect'), | |
100 | + login_url=None): | |
101 | + """ | |
102 | + 登录状态检查装饰器,如果未登录,则直接跳转到登录页面 | |
103 | + """ | |
104 | + actual_decorator = user_passes_test( | |
105 | + lambda u: u.is_authenticated(), | |
106 | + login_url=login_url, | |
107 | + redirect_field_name=redirect_field_name | |
108 | + ) | |
109 | + if function: | |
110 | + return actual_decorator(function) | |
111 | + return actual_decorator | |
0 | 112 | \ No newline at end of file | ... | ... |
forum/forms/user.py
1 | 1 | # coding: utf-8 |
2 | 2 | |
3 | +import requests | |
3 | 4 | from django import forms |
4 | 5 | from django.contrib.auth import authenticate |
5 | 6 | from django.conf import settings |
... | ... | @@ -85,6 +86,9 @@ class LoginForm(forms.Form): |
85 | 86 | |
86 | 87 | def __init__(self, *args, **kwargs): |
87 | 88 | self.user_cache = None |
89 | + self.pu = '' | |
90 | + self.pt = '' | |
91 | + self.username = '' | |
88 | 92 | super(LoginForm, self).__init__(*args, **kwargs) |
89 | 93 | |
90 | 94 | def clean(self): |
... | ... | @@ -92,15 +96,32 @@ class LoginForm(forms.Form): |
92 | 96 | password = self.cleaned_data.get('password') |
93 | 97 | |
94 | 98 | if username and password: |
95 | - self.user_cache = authenticate(username=username, password=password) | |
96 | - if self.user_cache is None: | |
97 | - raise forms.ValidationError(u'用户名或者密码不正确') | |
98 | - elif not self.user_cache.is_active: | |
99 | - raise forms.ValidationError(u'用户已被锁定,请联系管理员解锁') | |
99 | + post_params = { | |
100 | + 'comefrom': 2, | |
101 | + 'user_name': username, | |
102 | + 'password': password, | |
103 | + } | |
104 | + resp = requests.post(settings.AUTH_DOMAIN, data=post_params, verify=False) | |
105 | + if resp.status_code == 200: | |
106 | + rst = resp.json() | |
107 | + if rst.get('status') == 1: | |
108 | + data = rst.get('data') | |
109 | + self.pt = data.get('token') | |
110 | + self.pu = data.get('suid') | |
111 | + self.username = username | |
112 | + else: | |
113 | + raise forms.ValidationError(u'用户名或者密码不正确') | |
114 | + else: | |
115 | + raise forms.ValidationError(u'连接登录中心失败') | |
116 | + | |
100 | 117 | return self.cleaned_data |
101 | 118 | |
102 | 119 | def get_user(self): |
103 | - return self.user_cache | |
120 | + return { | |
121 | + 'pt': self.pt, | |
122 | + 'pu': self.pu, | |
123 | + 'username': self.username, | |
124 | + } | |
104 | 125 | |
105 | 126 | |
106 | 127 | class RegisterForm(forms.ModelForm): | ... | ... |
forum/views/user.py
1 | 1 | # coding: utf-8 |
2 | 2 | |
3 | +import requests | |
3 | 4 | import os, uuid, copy, urllib |
4 | 5 | from PIL import Image |
5 | 6 | from django.shortcuts import render_to_response, redirect |
... | ... | @@ -128,13 +129,14 @@ def post_login(request): |
128 | 129 | if not form.is_valid(): |
129 | 130 | return get_login(request, errors=form.errors) |
130 | 131 | |
131 | - user = form.get_user() | |
132 | - auth.login(request, user) | |
133 | - | |
134 | - if user.is_staff: | |
135 | - return redirect(request.REQUEST.get('next', '/manage/admin/')) | |
132 | + user_info = form.get_user() | |
133 | + pt = user_info.get('pt') | |
134 | + pu = user_info.get('pu') | |
135 | + t = redirect(request.REQUEST.get('next', '/')) | |
136 | + t.set_cookie('pt', pt, 864000) | |
137 | + t.set_cookie('pu', pu, 864000) | |
136 | 138 | |
137 | - return redirect(request.REQUEST.get('next', '/')) | |
139 | + return t | |
138 | 140 | |
139 | 141 | |
140 | 142 | def get_logout(request): | ... | ... |
middlewares/__init__.py
middlewares/session_middleware.py
... | ... | @@ -0,0 +1,115 @@ |
1 | +# coding: utf-8 | |
2 | + | |
3 | +import requests | |
4 | +from django.conf import settings | |
5 | +from django.core.cache import caches | |
6 | +from django.contrib.auth import get_user_model | |
7 | +from django.contrib.auth.models import AnonymousUser | |
8 | + | |
9 | + | |
10 | +class SessionWithoutLocalUserMiddleware(object): | |
11 | + """ | |
12 | + 统一权限(认证)中间件,Django系统本地不保存用户的情况使用 | |
13 | + """ | |
14 | + | |
15 | + def __init__(self): | |
16 | + self.cache_alias = settings.CACHE_MIDDLEWARE_ALIAS | |
17 | + self.cache = caches[self.cache_alias] | |
18 | + self.UserModel = get_user_model() | |
19 | + | |
20 | + def process_request(self, request): | |
21 | + if hasattr(request, "user") and getattr(request.user, "is_superuser", False): | |
22 | + # 对于Django系统的admin用户,这里不做任何处理 | |
23 | + pass | |
24 | + else: | |
25 | + pt = request.COOKIES.get('pt') | |
26 | + pu = request.COOKIES.get('pu') | |
27 | + username = request.COOKIES.get('username') | |
28 | + if pt and pu: | |
29 | + # 查询session状态成功的情况,构造QCCRUser | |
30 | + user = XYTUser(username, pu, pt) | |
31 | + request.user = user | |
32 | + else: | |
33 | + # 拿不到统一认证的session,将当前用户设为匿名用户 | |
34 | + request.user = AnonymousUser() | |
35 | + | |
36 | + | |
37 | +class Manager(object): | |
38 | + | |
39 | + def __init__(self): | |
40 | + self.auth_domain = 'https://api.xiuyetang.com/sys/user/login' | |
41 | + | |
42 | + | |
43 | +class XYTUser(object): | |
44 | + id = None | |
45 | + pk = None | |
46 | + username = '' | |
47 | + sessionId = '' | |
48 | + accountNo = '' | |
49 | + employeeName = '' | |
50 | + employeeId = 0 | |
51 | + employeeNo = '' | |
52 | + employeeTel = '' | |
53 | + deptIds = '' | |
54 | + email = '' | |
55 | + entryTime = '' | |
56 | + uid = '' | |
57 | + is_staff = False | |
58 | + is_active = False | |
59 | + is_superuser = False | |
60 | + _groups = '' | |
61 | + _user_permissions = '' | |
62 | + | |
63 | + def __init__(self, username, pu, pt): | |
64 | + self.username = username | |
65 | + self.id = pu | |
66 | + self.pk = pu | |
67 | + self.sessionId = pt | |
68 | + | |
69 | + def __str__(self): | |
70 | + return self.username | |
71 | + | |
72 | + def __eq__(self, other): | |
73 | + return self.username == other.username | |
74 | + | |
75 | + def __ne__(self, other): | |
76 | + return not self.__eq__(other) | |
77 | + | |
78 | + def __hash__(self): | |
79 | + return hash(self.username) | |
80 | + | |
81 | + def save(self): | |
82 | + raise NotImplementedError("Django doesn't provide a DB representation for QCCRUser. User info in LDAP.") | |
83 | + | |
84 | + def delete(self): | |
85 | + raise NotImplementedError("Django doesn't provide a DB representation for QCCRUser. User info in LDAP.") | |
86 | + | |
87 | + def set_password(self, raw_password): | |
88 | + raise NotImplementedError("Django doesn't provide a DB representation for QCCRUser. Password in LDAP.") | |
89 | + | |
90 | + def check_password(self, raw_password): | |
91 | + raise NotImplementedError("Django doesn't provide a DB representation for QCCRUser. Password in LDAP.") | |
92 | + | |
93 | + def _get_groups(self): | |
94 | + return self._groups | |
95 | + | |
96 | + groups = property(_get_groups) | |
97 | + | |
98 | + def _get_user_permissions(self): | |
99 | + return self._user_permissions | |
100 | + | |
101 | + user_permissions = property(_get_user_permissions) | |
102 | + | |
103 | + def get_group_permissions(self, obj=None): | |
104 | + return set() | |
105 | + | |
106 | + @property | |
107 | + def is_anonymous(self): | |
108 | + return lambda: False | |
109 | + | |
110 | + @property | |
111 | + def is_authenticated(self): | |
112 | + return lambda: True | |
113 | + | |
114 | + def get_username(self): | |
115 | + return self.username | |
0 | 116 | \ No newline at end of file | ... | ... |
xp/settings.py
... | ... | @@ -16,7 +16,7 @@ DATABASES = { |
16 | 16 | 'NAME': 'forum', # Or path to database file if using sqlite3. |
17 | 17 | # The following settings are not used with sqlite3: |
18 | 18 | 'USER': 'root', |
19 | - 'PASSWORD': 'nineteen', | |
19 | + 'PASSWORD': 'zzc', | |
20 | 20 | 'HOST': '127.0.0.1', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP. |
21 | 21 | 'PORT': '3306', # Set to empty string for default. |
22 | 22 | } |
... | ... | @@ -183,12 +183,13 @@ LOGGING = { |
183 | 183 | # } |
184 | 184 | |
185 | 185 | # SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 使用memcached存储session |
186 | +AUTH_DOMAIN = 'https://api.xiuyetang.com/service/user/login' | |
186 | 187 | |
187 | 188 | # 自定义User类 |
188 | 189 | AUTH_USER_MODEL = 'forum.ForumUser' |
189 | 190 | |
190 | 191 | # 用户认证BackEnds |
191 | -# AUTHENTICATION_BACKENDS = ('forum.backends.EmailAuthBackend',) | |
192 | +AUTHENTICATION_BACKENDS = ('forum.backends.EmailAuthBackend',) | |
192 | 193 | |
193 | 194 | # 默认登陆uri |
194 | 195 | LOGIN_URL = '/login/' | ... | ... |