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/' | ... | ... |