├── .gitignore ├── LICENSE ├── Oauth ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-36.pyc │ ├── admin.cpython-36.pyc │ ├── apps.cpython-36.pyc │ ├── master.cpython-36.pyc │ ├── models.cpython-36.pyc │ └── views.cpython-36.pyc ├── admin.py ├── apps.py ├── master.py ├── migrations │ ├── __init__.py │ └── __pycache__ │ │ ├── 0001_initial.cpython-36.pyc │ │ ├── 0002_auto_20180929_1102.cpython-36.pyc │ │ ├── 0003_auto_20180929_1103.cpython-36.pyc │ │ ├── 0004_auto_20180929_1127.cpython-36.pyc │ │ └── __init__.cpython-36.pyc ├── models.py ├── tests.py └── views.py ├── README.md ├── djangOauth ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-36.pyc │ ├── settings.cpython-36.pyc │ ├── urls.cpython-36.pyc │ └── wsgi.cpython-36.pyc ├── settings.py ├── urls.py └── wsgi.py ├── manage.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .idea 3 | *.pyc 4 | Oauth/migrations/0*.py 5 | venv 6 | *.pyc 7 | djangOauth/__pycache__/__init__.cpython-36.pyc 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2 | 3 | 996 License Version 1.0 (Draft) 4 | 5 | Permission is hereby granted to any individual or legal entity 6 | obtaining a copy of this licensed work (including the source code, 7 | documentation and/or related items, hereinafter collectively referred 8 | to as the "licensed work"), free of charge, to deal with the licensed 9 | work for any purpose, including without limitation, the rights to use, 10 | reproduce, prepare derivative works of, distribute and sublicense the 11 | licensed work, subject to the following conditions: 12 | 13 | 1. The individual or the legal entity must conspicuously display, 14 | without modification, this License on each redistributed or derivative 15 | copy of the Licensed Work. 16 | 17 | 2. The individual or the legal entity must strictly comply with all 18 | applicable laws, regulations, rules and standards of the jurisdiction 19 | relating to labor and employment where the individual is physically 20 | located or where the individual was born or naturalized; or where the 21 | legal entity is registered or is operating (whichever is stricter). In 22 | case that the jurisdiction has no such laws, regulations, rules and 23 | standards or its laws, regulations, rules and standards are 24 | unenforceable, the individual or the legal entity are required to 25 | comply with Core International Labor Standards. 26 | 27 | 3. The individual or the legal entity shall not induce or force its 28 | employee(s), whether full-time or part-time, or its independent 29 | contractor(s), in any methods, to agree in oral or written form, to 30 | directly or indirectly restrict, weaken or relinquish his or her 31 | rights or remedies under such laws, regulations, rules and standards 32 | relating to labor and employment as mentioned above, no matter whether 33 | such written or oral agreement are enforceable under the laws of the 34 | said jurisdiction, nor shall such individual or the legal entity 35 | limit, in any methods, the rights of its employee(s) or independent 36 | contractor(s) from reporting or complaining to the copyright holder or 37 | relevant authorities monitoring the compliance of the license about 38 | its violation(s) of the said license. 39 | 40 | THE LICENSED WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 41 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 42 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 43 | IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, 44 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 45 | OTHERWISE, ARISING FROM, OUT OF OR IN ANY WAY CONNECTION WITH THE 46 | LICENSED WORK OR THE USE OR OTHER DEALINGS IN THE LICENSED WORK. 47 | -------------------------------------------------------------------------------- /Oauth/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Mars/djangOauth/0c50b732d3e8e4290fdfbb1a8337831643dadce9/Oauth/__init__.py -------------------------------------------------------------------------------- /Oauth/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Mars/djangOauth/0c50b732d3e8e4290fdfbb1a8337831643dadce9/Oauth/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /Oauth/__pycache__/admin.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Mars/djangOauth/0c50b732d3e8e4290fdfbb1a8337831643dadce9/Oauth/__pycache__/admin.cpython-36.pyc -------------------------------------------------------------------------------- /Oauth/__pycache__/apps.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Mars/djangOauth/0c50b732d3e8e4290fdfbb1a8337831643dadce9/Oauth/__pycache__/apps.cpython-36.pyc -------------------------------------------------------------------------------- /Oauth/__pycache__/master.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Mars/djangOauth/0c50b732d3e8e4290fdfbb1a8337831643dadce9/Oauth/__pycache__/master.cpython-36.pyc -------------------------------------------------------------------------------- /Oauth/__pycache__/models.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Mars/djangOauth/0c50b732d3e8e4290fdfbb1a8337831643dadce9/Oauth/__pycache__/models.cpython-36.pyc -------------------------------------------------------------------------------- /Oauth/__pycache__/views.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Mars/djangOauth/0c50b732d3e8e4290fdfbb1a8337831643dadce9/Oauth/__pycache__/views.cpython-36.pyc -------------------------------------------------------------------------------- /Oauth/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.contrib.auth.admin import UserAdmin 3 | from Oauth.models import NewUser 4 | from django.utils.translation import gettext, gettext_lazy as _ 5 | 6 | # Register your models here. 7 | class NewUserAdmin(UserAdmin): 8 | 9 | fieldsets = ( 10 | (None, {'fields': ('username', 'password')}), 11 | (_('Personal info'), {'fields': ('email', )}), 12 | (_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', 13 | 'groups', 'user_permissions')}), 14 | (_('Important dates'), {'fields': ('last_login', 'date_joined')}), 15 | ) 16 | 17 | list_display = ('username', 'email', 'is_staff') 18 | 19 | 20 | admin.site.register(NewUser, NewUserAdmin) -------------------------------------------------------------------------------- /Oauth/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class OauthConfig(AppConfig): 5 | name = 'Oauth' 6 | -------------------------------------------------------------------------------- /Oauth/master.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2018/9/29 11:17 4 | # @Author : MarsLiu 5 | # @Site : https://github.com/X-Mars 6 | # @File : master.py 7 | # @Software: PyCharm 8 | 9 | 10 | from rest_framework import status, serializers 11 | from rest_framework.response import Response 12 | from rest_framework_jwt.utils import jwt_decode_handler 13 | from rest_framework_jwt.views import JSONWebTokenAPIView 14 | from rest_framework_jwt.settings import api_settings 15 | from rest_framework_jwt.compat import Serializer 16 | from rest_framework_jwt.serializers import JSONWebTokenSerializer 17 | from django.contrib import auth 18 | from django.utils.translation import ugettext as _ 19 | from django.contrib.auth import get_user_model 20 | from datetime import datetime, timedelta 21 | import jwt, time, uuid 22 | 23 | 24 | jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER 25 | jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER 26 | jwt_get_username_from_payload = api_settings.JWT_PAYLOAD_GET_USERNAME_HANDLER 27 | jwt_response_payload_handler = api_settings.JWT_RESPONSE_PAYLOAD_HANDLER 28 | 29 | User = get_user_model() 30 | 31 | class NewVerificationBaseSerializer(Serializer): 32 | """ 33 | Abstract serializer used for verifying and refreshing JWTs. 34 | """ 35 | token = serializers.CharField() 36 | 37 | def validate(self, attrs): 38 | msg = 'Please define a validate method.' 39 | raise NotImplementedError(msg) 40 | 41 | def _check_payload(self, token): 42 | # Check payload valid (based off of JSONWebTokenAuthentication, 43 | # may want to refactor) 44 | try: 45 | payload = jwt_decode_handler(token) 46 | except jwt.ExpiredSignature: 47 | payload = jwt.decode(token, verify = False) 48 | orig_iat = payload.get('orig_iat') 49 | if orig_iat: 50 | # Verify expiration 51 | refresh_limit = api_settings.JWT_REFRESH_EXPIRATION_DELTA 52 | if isinstance(refresh_limit, timedelta): 53 | refresh_limit = (refresh_limit.days * 24 * 3600 + 54 | refresh_limit.seconds) 55 | expiration_timestamp = orig_iat + int(refresh_limit) 56 | now_timestamp = time.time() 57 | 58 | if now_timestamp < expiration_timestamp: 59 | return payload 60 | else: 61 | msg = _('Refresh has expired.') 62 | raise serializers.ValidationError(msg) 63 | else: 64 | msg = _('orig_iat field is required.') 65 | raise serializers.ValidationError(msg) 66 | 67 | except jwt.DecodeError: 68 | msg = _('Error decoding signature.') 69 | raise serializers.ValidationError(msg) 70 | 71 | return payload 72 | 73 | def _check_user(self, payload): 74 | username = jwt_get_username_from_payload(payload) 75 | 76 | if not username: 77 | msg = _('Invalid payload.') 78 | raise serializers.ValidationError(msg) 79 | 80 | # Make sure user exists 81 | try: 82 | user = User.objects.get_by_natural_key(username) 83 | except User.DoesNotExist: 84 | msg = _("User doesn't exist.") 85 | raise serializers.ValidationError(msg) 86 | 87 | if not user.is_active: 88 | msg = _('User account is disabled.') 89 | raise serializers.ValidationError(msg) 90 | 91 | return user 92 | 93 | class NewRefreshJSONWebTokenSerializer(NewVerificationBaseSerializer): 94 | 95 | def validate(self, attrs): 96 | token = attrs['token'] 97 | 98 | payload = self._check_payload(token=token) 99 | user = self._check_user(payload=payload) 100 | # Get and check 'orig_iat' 101 | orig_iat = payload.get('orig_iat') 102 | 103 | if orig_iat: 104 | # Verify expiration 105 | refresh_limit = api_settings.JWT_REFRESH_EXPIRATION_DELTA 106 | 107 | if isinstance(refresh_limit, timedelta): 108 | refresh_limit = (refresh_limit.days * 24 * 3600 + 109 | refresh_limit.seconds) 110 | 111 | expiration_timestamp = orig_iat + int(refresh_limit) 112 | 113 | now_timestamp = time.time() 114 | 115 | if now_timestamp > expiration_timestamp: 116 | msg = _('Refresh has expired.') 117 | raise serializers.ValidationError(msg) 118 | else: 119 | msg = _('orig_iat field is required.') 120 | raise serializers.ValidationError(msg) 121 | 122 | new_payload = jwt_payload_handler(user) 123 | new_payload['orig_iat'] = orig_iat 124 | 125 | user.jwt_secret = uuid.uuid1() 126 | user.save() 127 | 128 | return { 129 | 'token': jwt_encode_handler(new_payload), 130 | 'user': user 131 | } 132 | 133 | class NewJSONWebTokenAPIView(JSONWebTokenAPIView): 134 | 135 | def post(self, request, *args, **kwargs): 136 | serializer = self.get_serializer(data=request.data) 137 | if serializer.is_valid(): 138 | user = serializer.object.get('user') or request.user 139 | auth.login(request, user, backend='django.contrib.auth.backends.ModelBackend') 140 | try: 141 | password = request.data['password'] 142 | user.set_password(password) 143 | user.save() 144 | except KeyError: 145 | password = None 146 | 147 | token = serializer.object.get('token') 148 | response_data = jwt_response_payload_handler(token, user, request) 149 | response = Response(response_data) 150 | if api_settings.JWT_AUTH_COOKIE: 151 | expiration = (datetime.utcnow() + 152 | api_settings.JWT_EXPIRATION_DELTA) 153 | response.set_cookie(api_settings.JWT_AUTH_COOKIE, 154 | token, 155 | expires=expiration, 156 | httponly=True) 157 | return response 158 | 159 | return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) 160 | 161 | class ObtainJSONWebToken(NewJSONWebTokenAPIView): 162 | """ 163 | API View that receives a POST with a user's username and password. 164 | 165 | Returns a JSON Web Token that can be used for authenticated requests. 166 | """ 167 | serializer_class = JSONWebTokenSerializer 168 | 169 | obtain_jwt_token = ObtainJSONWebToken.as_view() 170 | 171 | class RefreshJSONWebToken(NewJSONWebTokenAPIView): 172 | """ 173 | API View that returns a refreshed token (with new expiration) based on 174 | existing token 175 | 176 | If 'orig_iat' field (original issued-at-time) is found, will first check 177 | if it's within expiration window, then copy it to the new token 178 | """ 179 | serializer_class = NewRefreshJSONWebTokenSerializer 180 | 181 | refresh_jwt_token = RefreshJSONWebToken.as_view() -------------------------------------------------------------------------------- /Oauth/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Mars/djangOauth/0c50b732d3e8e4290fdfbb1a8337831643dadce9/Oauth/migrations/__init__.py -------------------------------------------------------------------------------- /Oauth/migrations/__pycache__/0001_initial.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Mars/djangOauth/0c50b732d3e8e4290fdfbb1a8337831643dadce9/Oauth/migrations/__pycache__/0001_initial.cpython-36.pyc -------------------------------------------------------------------------------- /Oauth/migrations/__pycache__/0002_auto_20180929_1102.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Mars/djangOauth/0c50b732d3e8e4290fdfbb1a8337831643dadce9/Oauth/migrations/__pycache__/0002_auto_20180929_1102.cpython-36.pyc -------------------------------------------------------------------------------- /Oauth/migrations/__pycache__/0003_auto_20180929_1103.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Mars/djangOauth/0c50b732d3e8e4290fdfbb1a8337831643dadce9/Oauth/migrations/__pycache__/0003_auto_20180929_1103.cpython-36.pyc -------------------------------------------------------------------------------- /Oauth/migrations/__pycache__/0004_auto_20180929_1127.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Mars/djangOauth/0c50b732d3e8e4290fdfbb1a8337831643dadce9/Oauth/migrations/__pycache__/0004_auto_20180929_1127.cpython-36.pyc -------------------------------------------------------------------------------- /Oauth/migrations/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Mars/djangOauth/0c50b732d3e8e4290fdfbb1a8337831643dadce9/Oauth/migrations/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /Oauth/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import AbstractUser 3 | import uuid 4 | # Create your models here. 5 | 6 | 7 | class NewUser(AbstractUser): 8 | jwt_secret = models.UUIDField(default=uuid.uuid4()) 9 | 10 | class Meta(AbstractUser.Meta): 11 | swappable = 'AUTH_USER_MODEL' 12 | pass 13 | 14 | def jwt_get_secret_key(NewUser): 15 | return NewUser.jwt_secret -------------------------------------------------------------------------------- /Oauth/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /Oauth/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import viewsets, status 2 | from rest_framework.response import Response 3 | from rest_framework.decorators import api_view 4 | import uuid 5 | 6 | # Create your views here. 7 | 8 | # @api_view(['GET',]) 9 | # def logout(request): 10 | # request.user.jwt_secret = uuid.uuid4() 11 | # request.user.save() 12 | # return Response({'status': 'ok'}, status=status.HTTP_200_OK) 13 | 14 | class LogoutViewSet(viewsets.ViewSet): 15 | 16 | def logout(self, request): 17 | request.user.jwt_secret = uuid.uuid4() 18 | request.user.save() 19 | return Response({'status': 'ok'}, status=status.HTTP_200_OK) 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # djangOauth 2 | 3 | ### 项目介绍 4 | 5 | 1. 基于 django-rest-framework-jwt 6 | 2. 基于 django-auth-ldap 7 | 3. 实现 用户名的统一认证 8 | 4. 您可以直接使用该项目,或者基于该项目二次开发。(比如实现运维账号的通知管理) 9 | 10 | ### 准备做(按优先级排序) 11 | 1. 企业微信扫码登录(本地已经正常使用,近期将提交代码) 12 | 2. 添加ldap组同步 13 | 14 | ### 开发环境 15 | 16 | Djnago >= 2 17 | Python >= 3 18 | Mysql = 5.7.16 19 | 20 | ### 工作方式 21 | 22 | 1. 第三方应用发起带有“用户名、密码”的post请求(/api/login/),djangOauth 验证用户名、密码后,返回jwt token 23 | 2. 第三方应用解析 jwt token 验证有效性,并保存至cookies,每页刷新页面 验证一次。 24 | 3. 如若token过期,则发起(/api/token-refresh/) token刷新接口,获取新的token。 25 | 4. 当启用LDAP验证方式时,将会自动在数据库创建对应用户 26 | 27 | 28 | ### 配置说明 29 | 30 | 1. **JWT_EXPIRATION_DELTA** : 控制token过期时间; 31 | 2. **JWT_REFRESH_EXPIRATION_DELTA** : token过期多长时间内,可以通过过期token获取新token 32 | 3. 后端验证方式 设置 33 | ``` 34 | AUTHENTICATION_BACKENDS = [ 35 | 'django_auth_ldap.backend.LDAPBackend', 36 | 'django.contrib.auth.backends.ModelBackend' 37 | ] 38 | ``` 39 | 第一个 **LDAPBackend** 验证失败,则继续使用 **ModelBackend** 进行验证 40 | 41 | 4. ldap 服务器配置 42 | ``` 43 | AUTH_LDAP_SERVER_URI = 'ldap://openldap.com' 44 | 45 | AUTH_LDAP_BIND_DN = 'cn=Manager,dc=xwjrops,dc=cn' 46 | AUTH_LDAP_BIND_PASSWORD = 'password' 47 | ``` 48 | **ldap 服务器的地址** 以及 **管理员账号密码** 49 | 50 | 5. 用户ou路径 51 | ``` 52 | AUTH_LDAP_USER_SEARCH = LDAPSearch( 53 | 'ou=Technology,dc=xwjrops,dc=cn', 54 | ldap.SCOPE_SUBTREE, 55 | '(uid=%(user)s)', 56 | ) 57 | ``` 58 | 59 | 6. 数据库字段对应的ldap字段 60 | 61 | ``` 62 | AUTH_LDAP_USER_ATTR_MAP = { 63 | 'first_name': 'givenName', 64 | 'last_name': 'sn', 65 | 'email': 'mail', 66 | } 67 | ``` 68 | 69 | ### 安装方法 70 | 71 | 1. 安装mysql,并新建数据库 **djangOauth**(略) 72 | 2. 下载代码 73 | ```shell 74 | git clone https://github.com/X-Mars/djangOauth.git 75 | ``` 76 | 3. 安装模块 77 | ```shell 78 | pip3 install -r requirements.txt 79 | ``` 80 | 4. 启动服务 81 | ```shell 82 | python3 djangOauth/manage.py runserver 0.0.0.0 8000 83 | ``` 84 | 5. 后台访问 85 | ```url 86 | http://ip:8000/admin 87 | ``` 88 | 89 | ### 接口信息 90 | 91 | ```url 92 | http://ip:8000/api/doc/ 93 | ``` 94 | 95 | License 96 | --- 97 | [996ICU License](LICENSE) 98 | -------------------------------------------------------------------------------- /djangOauth/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Mars/djangOauth/0c50b732d3e8e4290fdfbb1a8337831643dadce9/djangOauth/__init__.py -------------------------------------------------------------------------------- /djangOauth/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Mars/djangOauth/0c50b732d3e8e4290fdfbb1a8337831643dadce9/djangOauth/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /djangOauth/__pycache__/settings.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Mars/djangOauth/0c50b732d3e8e4290fdfbb1a8337831643dadce9/djangOauth/__pycache__/settings.cpython-36.pyc -------------------------------------------------------------------------------- /djangOauth/__pycache__/urls.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Mars/djangOauth/0c50b732d3e8e4290fdfbb1a8337831643dadce9/djangOauth/__pycache__/urls.cpython-36.pyc -------------------------------------------------------------------------------- /djangOauth/__pycache__/wsgi.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/X-Mars/djangOauth/0c50b732d3e8e4290fdfbb1a8337831643dadce9/djangOauth/__pycache__/wsgi.cpython-36.pyc -------------------------------------------------------------------------------- /djangOauth/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for djangOauth project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.0.7. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.0/ref/settings/ 11 | """ 12 | 13 | import os 14 | import datetime 15 | import ldap 16 | from django_auth_ldap.config import LDAPSearch, GroupOfNamesType, PosixGroupType, LDAPGroupType 17 | 18 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 19 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 20 | 21 | 22 | # Quick-start development settings - unsuitable for production 23 | # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ 24 | 25 | # SECURITY WARNING: keep the secret key used in production secret! 26 | SECRET_KEY = 'n-27eivqi$o283p&u48ck@xr8_-eaai&#&5zcvz^v+y@i312*n' 27 | 28 | # SECURITY WARNING: don't run with debug turned on in production! 29 | DEBUG = True 30 | 31 | ALLOWED_HOSTS = ['*'] 32 | 33 | 34 | # Application definition 35 | 36 | INSTALLED_APPS = [ 37 | 'django.contrib.admin', 38 | 'django.contrib.auth', 39 | 'django.contrib.contenttypes', 40 | 'django.contrib.sessions', 41 | 'django.contrib.messages', 42 | 'django.contrib.staticfiles', 43 | 'Oauth.apps.OauthConfig', 44 | 'rest_framework', 45 | 'rest_framework_swagger', 46 | ] 47 | 48 | MIDDLEWARE = [ 49 | 'django.middleware.security.SecurityMiddleware', 50 | 'django.contrib.sessions.middleware.SessionMiddleware', 51 | 'django.middleware.common.CommonMiddleware', 52 | 'django.middleware.csrf.CsrfViewMiddleware', 53 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 54 | 'django.contrib.messages.middleware.MessageMiddleware', 55 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 56 | ] 57 | 58 | ROOT_URLCONF = 'djangOauth.urls' 59 | 60 | TEMPLATES = [ 61 | { 62 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 63 | 'DIRS': [os.path.join(BASE_DIR, 'templates')] 64 | , 65 | 'APP_DIRS': True, 66 | 'OPTIONS': { 67 | 'context_processors': [ 68 | 'django.template.context_processors.debug', 69 | 'django.template.context_processors.request', 70 | 'django.contrib.auth.context_processors.auth', 71 | 'django.contrib.messages.context_processors.messages', 72 | ], 73 | }, 74 | }, 75 | ] 76 | 77 | WSGI_APPLICATION = 'djangOauth.wsgi.application' 78 | 79 | 80 | # Database 81 | # https://docs.djangoproject.com/en/2.0/ref/settings/#databases 82 | 83 | DATABASES = { 84 | 'default': { 85 | 'ENGINE': 'django.db.backends.mysql', 86 | 'NAME': 'djangOauth', 87 | 'USER': 'root', 88 | 'PASSWORD': '19900501', 89 | 'HOST': '127.0.0.1', 90 | 'port': '3306', 91 | } 92 | } 93 | 94 | 95 | # Password validation 96 | # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators 97 | 98 | AUTH_PASSWORD_VALIDATORS = [ 99 | { 100 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 101 | }, 102 | { 103 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 104 | }, 105 | { 106 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 107 | }, 108 | { 109 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 110 | }, 111 | ] 112 | 113 | 114 | # Internationalization 115 | # https://docs.djangoproject.com/en/2.0/topics/i18n/ 116 | 117 | LANGUAGE_CODE = 'zh-hans' 118 | 119 | TIME_ZONE = 'Asia/Shanghai' 120 | 121 | USE_I18N = True 122 | 123 | USE_L10N = True 124 | 125 | USE_TZ = True 126 | 127 | 128 | # Static files (CSS, JavaScript, Images) 129 | # https://docs.djangoproject.com/en/2.0/howto/static-files/ 130 | 131 | STATIC_URL = '/static/' 132 | 133 | AUTH_USER_MODEL = 'Oauth.NewUser' 134 | 135 | 136 | REST_FRAMEWORK = { 137 | # Use Django's standard `django.contrib.auth` permissions, 138 | # or allow read-only access for unauthenticated users. 139 | 'UNAUTHENTICATED_USER': False, 140 | 'DEFAULT_MODEL_SERIALIZER_CLASS': 141 | 'rest_framework.serializers.HyperlinkedModelSerializer', 142 | 'DEFAULT_PERMISSION_CLASSES': [ 143 | 'rest_framework.permissions.IsAuthenticated', 144 | ], 145 | 'DEFAULT_AUTHENTICATION_CLASSES': [ 146 | 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', 147 | 'rest_framework.authentication.SessionAuthentication', 148 | 'rest_framework.authentication.BasicAuthentication', 149 | ], 150 | } 151 | 152 | CSRF_COOKIE_NAME = "csrftoken" 153 | 154 | JWT_AUTH = { 155 | 'JWT_SECRET_KEY': SECRET_KEY, 156 | 'JWT_ALGORITHM': "HS256", 157 | 'JWT_GET_USER_SECRET_KEY': 'Oauth.models.jwt_get_secret_key', 158 | 'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=7200), 159 | 'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(seconds=600), 160 | 'JWT_ALLOW_REFRESH': True, 161 | } 162 | 163 | AUTHENTICATION_BACKENDS = [ 164 | 'django_auth_ldap.backend.LDAPBackend', 165 | 'django.contrib.auth.backends.ModelBackend' 166 | ] 167 | 168 | AUTH_LDAP_SERVER_URI = 'ldap://openldap.com' 169 | 170 | AUTH_LDAP_BIND_DN = 'cn=Manager,dc=xwjrops,dc=cn' 171 | AUTH_LDAP_BIND_PASSWORD = 'password' 172 | 173 | AUTH_LDAP_USER_SEARCH = LDAPSearch( 174 | 'ou=Technology,dc=xwjrops,dc=cn', 175 | ldap.SCOPE_SUBTREE, 176 | '(uid=%(user)s)', 177 | ) 178 | 179 | # AUTH_LDAP_GROUP_SEARCH = LDAPSearch( 180 | # 'ou=xwjroauth,ou=Groups,dc=xwjrops,dc=cn', 181 | # ldap.SCOPE_SUBTREE, 182 | # '(objectClass=posixGroup)', 183 | # ) 184 | # 185 | # AUTH_LDAP_GROUP_TYPE = PosixGroupType(name_attr='cn') 186 | # 187 | # AUTH_LDAP_REQUIRE_GROUP = 'cn=enabled,ou=xwjroauth,ou=Groups,dc=xwjrops,dc=cn' 188 | # AUTH_LDAP_DENY_GROUP = 'cn=disabled,ou=xwjroauth,ou=Groups,dc=xwjrops,dc=cn' 189 | 190 | # AUTH_LDAP_FIND_GROUP_PERMS = True 191 | # AUTH_LDAP_MIRROR_GROUPS = True 192 | 193 | AUTH_LDAP_USER_ATTR_MAP = { 194 | 'first_name': 'givenName', 195 | 'last_name': 'sn', 196 | 'email': 'mail', 197 | } 198 | 199 | # AUTH_LDAP_USER_FLAGS_BY_GROUP = { 200 | # 'is_active': 'cn=active,ou=xwjroauth,ou=Groups,dc=xwjrops,dc=cn', 201 | # 'is_staff': 'cn=staff,ou=xwjroauth,ou=Groups,dc=xwjrops,dc=cn', 202 | # 'is_superuser': 'cn=superuser,ou=xwjroauth,ou=Groups,dc=xwjrops,dc=cn', 203 | # } 204 | -------------------------------------------------------------------------------- /djangOauth/urls.py: -------------------------------------------------------------------------------- 1 | """djangOauth URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.0/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path 18 | from Oauth import views as oauth_views 19 | from Oauth.master import obtain_jwt_token as new_obtain_jwt_token 20 | from Oauth.master import refresh_jwt_token as new_refresh_jwt_token 21 | from rest_framework_swagger.views import get_swagger_view 22 | 23 | schema_view = get_swagger_view(title='djangOauth API') 24 | 25 | 26 | urlpatterns = [ 27 | path('admin/', admin.site.urls), 28 | path('api/login/', new_obtain_jwt_token), 29 | path('api/token-refresh/', new_refresh_jwt_token), 30 | path('api/logout/', oauth_views.LogoutViewSet.as_view({'get':'logout'})), 31 | path('api/doc/', schema_view), 32 | ] 33 | -------------------------------------------------------------------------------- /djangOauth/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for djangOauth project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangOauth.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangOauth.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2022.12.7 2 | chardet==3.0.4 3 | coreapi==2.3.3 4 | coreschema==0.0.4 5 | Django==2.2.13 6 | django-auth-ldap==1.7.0 7 | django-rest-swagger==2.2.0 8 | djangorestframework==3.11.2 9 | djangorestframework-jwt==1.11.0 10 | idna==2.7 11 | itypes==1.1.0 12 | Jinja2==2.10 13 | MarkupSafe==1.0 14 | mysqlclient==1.3.13 15 | openapi-codec==1.3.2 16 | pyasn1==0.4.4 17 | pyasn1-modules==0.2.2 18 | PyJWT==2.4.0 19 | python-ldap==3.1.0 20 | pytz==2018.5 21 | requests==2.20.1 22 | simplejson==3.16.0 23 | uritemplate==3.0.0 24 | urllib3==1.24.2 25 | --------------------------------------------------------------------------------