diff --git a/conf/local_settings.py b/conf/local_settings.py index c46d546..057780f 100644 --- a/conf/local_settings.py +++ b/conf/local_settings.py @@ -31,10 +31,7 @@ AD_CONN_PORT = 636 # 值是:DING / WEWORK AUTH_CODE_TYPE = 'DING' -# ########## 钉钉 《如果不使用钉钉扫码,可不用配置》########## -# 钉钉接口主地址,不可修改 -DING_URL = r'https://oapi.dingtalk.com' - +# ########## 钉钉 《如果不使用钉钉,可不用配置》########## # 钉钉企业ID ,修改为自己的 DING_CORP_ID = '修改为自己的' @@ -48,7 +45,7 @@ DING_MO_APP_ID = r'修改为自己的' DING_MO_APP_SECRET = r'修改为自己的' -# ####### 企业微信《如果不使用企业微信扫码,可不用配置》 ########## +# ####### 企业微信《如果不使用企业微信,可不用配置》 ########## # 企业微信的企业ID WEWORK_CORP_ID = r'修改为自己的' # 应用的AgentId @@ -58,11 +55,9 @@ WEWORK_AGNET_SECRET = r'修改为自己的' # Redis配置 # redis的连接地址,redis://:/<数据库> -REDIS_LOCATION = r'redis://127.0.0.1:6379/1' -REDIS_PASSWORD = r'12345678' +REDIS_LOCATION = r'redis://127.0.0.1:6379' +REDIS_PASSWORD = r'修改为自己的' -# COOKIE超时时间,单位是秒,可不用修改 -TMPID_COOKIE_AGE = 300 # 主页域名,钉钉跳转等需要指定域名,格式:pwd.abc.com。 # 如果是自定义安装,请修改成自己的域名 diff --git a/pwdselfservice/__init__.py b/pwdselfservice/__init__.py index 3a351f6..0edafb8 100644 --- a/pwdselfservice/__init__.py +++ b/pwdselfservice/__init__.py @@ -1,19 +1,17 @@ -import datetime -from cryptography.fernet import Fernet +import sys + from django_redis import get_redis_connection from utils.storage.kvstorage import KvStorage -from utils.storage.memorystorage import MemoryStorage +import datetime +from traceback import format_exc + try: redis_conn = get_redis_connection() cache_storage = KvStorage(redis_conn) cache_storage.set('redis_connection', str(datetime.datetime.now())) redis_get = cache_storage.get('redis_connection') - # print("Redis连接成功,set/get测试通过--{},Token缓存将使用Redis处理".format(redis_get)) except Exception as e: - cache_storage = MemoryStorage() - print("Redis无法连接,Token缓存将使用MemoryStorage处理") - print("如果确定需要使用Redis作为缓存,请排查Redis配置,错误信息如下:") - print("Redis Exception: {}".format(e)) - -crypto_key = Fernet.generate_key() + print("请排查Redis配置,错误信息如下:") + print("Redis Exception: {}".format(format_exc())) + sys.exit(1) diff --git a/pwdselfservice/settings.py b/pwdselfservice/settings.py index 419dd16..bcf33fc 100644 --- a/pwdselfservice/settings.py +++ b/pwdselfservice/settings.py @@ -8,7 +8,6 @@ else: from conf.local_settings import REDIS_PASSWORD, REDIS_LOCATION DEBUG = False - # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -80,8 +79,10 @@ SESSION_SAVE_EVERY_REQUEST = True SESSION_COOKIE_AGE = 300 # False 会话cookie可以在用户浏览器中保持有效期。True:关闭浏览器,则Cookie失效。 SESSION_EXPIRE_AT_BROWSER_CLOSE = True -# 建议配置,阻止 javascript 对会话数据的访问,提高安全性。 -# SESSION_COOKIE_HTTPONLY= True +# session使用的存储方式 +SESSION_ENGINE = "django.contrib.sessions.backends.cache" +# 指明使用哪一个库保存session数据 +SESSION_CACHE_ALIAS = "session" INSTALLED_APPS = [ @@ -132,11 +133,19 @@ AD_ACCOUNT_DISABLE_CODE = [514, 66050] CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", - "LOCATION": REDIS_LOCATION, + "LOCATION": "{}/1".format(REDIS_LOCATION), + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.DefaultClient", + "PASSWORD": REDIS_PASSWORD, + "IGNORE_EXCEPTIONS": True, + } + }, + "session": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": "{}/3".format(REDIS_LOCATION), # 指明使用redis的3号数据库 "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "PASSWORD": REDIS_PASSWORD, - "COMPRESSOR": "django_redis.compressors.zlib.ZlibCompressor", "IGNORE_EXCEPTIONS": True, } } diff --git a/readme.md b/readme.md index e5385ff..25150dc 100644 --- a/readme.md +++ b/readme.md @@ -159,14 +159,6 @@ WEWORK_AGNET_SECRET = r'修改为自己的' REDIS_LOCATION = r'redis://127.0.0.1:6379/1' REDIS_PASSWORD = r'12345678' -# ########################## -# 执行:python3 ./utils/crypto.py 生成 -# 可自行生成后替换 -CRYPTO_KEY = b'dp8U9y7NAhCD3MoNwPzPBhBtTZ1uI_WWSdpNs6wUDgs=' - -# COOKIE 超时单位是秒,可不用修改 -TMPID_COOKIE_AGE = 300 - # 主页域名,钉钉跳转等需要指定域名,格式:pwd.abc.com。 # 如果是自定义安装,请修改成自己的域名 HOME_URL = 'PWD_SELF_SERVICE_DOMAIN' @@ -257,9 +249,6 @@ REDIS_PASSWORD = r'12345678' # 可自行生成后替换 CRYPTO_KEY = b'dp8U9y7NAhCD3MoNwPzPBhBtTZ1uI_WWSdpNs6wUDgs=' -# COOKIE 超时单位是秒,可不用修改 -TMPID_COOKIE_AGE = 300 - # 主页域名,钉钉跳转等需要指定域名,格式:pwd.abc.com。 # 如果是自定义安装,请修改成自己的域名 HOME_URL = 'PWD_SELF_SERVICE_DOMAIN' diff --git a/requestment b/requestment index 6bed0c0..b2bf104 100644 --- a/requestment +++ b/requestment @@ -2,10 +2,10 @@ Django==3.2 pycryptodome==3.10.1 attrs==21.2.0 python-dateutil==2.8.1 -alibabacloud_dingtalk==1.4.96 +dingtalk-sdk==1.3.8 cryptography==3.4.7 ldap3==2.9 django-redis==4.12.1 -feishu-python-sdk==0.1.4 -requests -uwsgi \ No newline at end of file +django-redis==4.12.1 +requests==2.28.1 +uwsgi==2.0.21 \ No newline at end of file diff --git a/resetpwd/utils.py b/resetpwd/utils.py index a39a28c..80641ed 100644 --- a/resetpwd/utils.py +++ b/resetpwd/utils.py @@ -13,7 +13,6 @@ from utils.crypto_ops import Crypto from ldap3.core.exceptions import LDAPInvalidCredentialsResult, LDAPOperationResult, LDAPExceptionError, LDAPException from django.conf import settings -from pwdselfservice import crypto_key import os APP_ENV = os.getenv('APP_ENV') if APP_ENV == 'dev': @@ -56,70 +55,6 @@ def code_2_user_info_with_oauth2(ops, request, msg_template, home_url, code): return True, user_id, user_info -def crypto_id_2_user_info(ops, request, msg_template, home_url, scan_app_tag): - """ - 能过前端提交的加密的userid来获取用户信息 - """ - try: - crypto_tmp_id = request.COOKIES.get('tmpid') - if not crypto_tmp_id: - logger.error('[异常] 请求方法:%s,请求路径:%s,未能拿到TmpID或会话己超时。' % (request.method, request.path)) - context = { - 'msg': "会话己超时,请重新验证用户信息。", - 'button_click': "window.location.href='%s'" % home_url, - 'button_display': "返回主页" - } - return False, context - # 解密 - crypto = Crypto(crypto_key) - user_id = crypto.decrypt(crypto_tmp_id) - # 通过user_id拿到用户的邮箱,并格式化为username - userid_status, user_info = ops.get_user_detail_by_user_id(user_id) - if not userid_status: - context = { - 'msg': '获取{}用户信息失败,错误信息:{}'.format(user_info, scan_app_tag), - 'button_click': "window.location.href='%s'" % home_url, - 'button_display': "返回主页" - } - return False, context - return True, user_info - except Exception as e: - return False, str(e) - - -def crypto_user_id_2_cookie(user_id): - """ - 加密userid,写入到cookie - """ - crypto = Crypto(crypto_key) - # 对user_id进行加密,因为user_id基本上固定不变的,为了防止user_id泄露而导致重复使用,进行加密后再传回。 - _id_cryto = crypto.encrypt(user_id) - # 配置cookie,通过cookie把加密后的用户user_id传到重置密码页面,并重定向到重置密码页面。 - set_cookie = HttpResponseRedirect('resetPassword') - set_cookie.set_cookie('tmpid', _id_cryto, expires=TMPID_COOKIE_AGE) - return set_cookie - - -def crypto_id_2_user_id(request, msg_template, home_url): - """ - 前端提交的加密的userid,解密出真实的userid - """ - try: - crypto_tmp_id = request.COOKIES.get('tmpid') - # 解密 - crypto = Crypto(crypto_key) - return True, crypto.decrypt(crypto_tmp_id) - except Exception as e: - logger.error('[异常] :%s' % str(e)) - logger.error('[异常] 请求方法:%s,请求路径:%s,未能拿到TmpID或会话己超时。' % (request.method, request.path)) - context = { - 'msg': "会话己超时,请重新扫码验证用户信息。", - 'button_click': "window.location.href='%s'" % home_url, - 'button_display': "返回主页" - } - return False, context - - def ops_account(ad_ops, request, msg_template, home_url, username, new_password): """ ad 账号操作,判断账号状态,重置密码或解锁账号 diff --git a/resetpwd/views.py b/resetpwd/views.py index d760918..7781f7d 100644 --- a/resetpwd/views.py +++ b/resetpwd/views.py @@ -7,7 +7,7 @@ from ldap3.core.exceptions import LDAPException import urllib.parse as url_encode from utils.format_username import format2username, get_user_is_active, get_email_from_userinfo from .form import CheckForm -from .utils import code_2_user_detail, crypto_id_2_user_info, ops_account +from .utils import code_2_user_detail, ops_account from django.conf import settings APP_ENV = os.getenv('APP_ENV') if APP_ENV == 'dev': @@ -34,13 +34,6 @@ class PARAMS(object): AUTH_APP = '微信' from utils.wework_ops import WeWorkOps ops = WeWorkOps() - else: - corp_id = None - app_id = WEWORK_CORP_ID - agent_id = WEWORK_AGENT_ID - AUTH_APP = '微信' - from utils.wework_ops import WeWorkOps - ops = WeWorkOps() scan_params = PARAMS() @@ -63,8 +56,6 @@ def index(request): return render(request, 'ding_index.v1.html', locals()) elif request.method == 'GET' and AUTH_CODE_TYPE == 'WEWORK': return render(request, 'we_index.v1.html', locals()) - elif request.method == 'GET' and AUTH_CODE_TYPE == 'FEISHU': - return render(request, 'index.v1.html', locals()) else: logger.error('[异常] 请求方法:%s,请求路径%s' % (request.method, request.path)) # @@ -123,7 +114,6 @@ def reset_password(request): :return: """ home_url = '%s://%s' % (request.scheme, HOME_URL) - # 从cookie中提取union_id,并解密,然后对当前union_id的用户进行重置密码 if request.method == 'GET': code = request.GET.get('code') if code: @@ -138,13 +128,13 @@ def reset_password(request): return render(request, msg_template, context) try: + # 用code换取用户基本信息 _status, user_id, user_info = code_2_user_detail(_ops, home_url, code) if not _status: return render(request, msg_template, user_id) - # 账号是否是激活的 + # 账号在企业微信或钉钉中是否是激活的 _, res = get_user_is_active(user_info) if not _: - # 否则账号不存在或未激活 context = { 'msg': '当前扫码的用户未激活或可能己离职,用户信息如下:%s' % user_info, 'button_click': "window.location.href='%s'" % home_url, @@ -160,7 +150,7 @@ def reset_password(request): logger.error('[异常] :%s' % str(callback_e)) return render(request, msg_template, context) - # 通过user_info拿到用户信息,并格式化为username + # 通过user_info拿到用户邮箱,并格式化为username _, email = get_email_from_userinfo(user_info) if not _: context = { @@ -181,8 +171,10 @@ def reset_password(request): # 如果邮箱能提取到,则格式化之后,提取出账号提交到前端绑定 if username: + request.session[username] = code context = { 'username': username, + 'code': code, } return render(request, 'resetPassword.v1.html', context) else: @@ -195,43 +187,25 @@ def reset_password(request): # 重置密码页面,输入新密码后点击提交 elif request.method == 'POST': - try: + username = request.POST.get('username') + code = request.POST.get('code') + if request.session.get(username) and request.session.get(username) == code: _new_password = request.POST.get('new_password').strip() - _status, user_info = crypto_id_2_user_info(_ops, request, msg_template, home_url, scan_params.AUTH_APP) - if not _status: - return render(request, msg_template, user_info) - - # 通过user_info拿到用户信息,并格式化为username - _, email = get_email_from_userinfo(user_info) - if not _: + try: + return ops_account(ad_ops=AdOps(), request=request, msg_template=msg_template, home_url=home_url, username=username, new_password=_new_password) + except Exception as reset_e: context = { - 'msg': email, + 'msg': "错误[%s],请与管理员联系." % str(reset_e), 'button_click': "window.location.href='%s'" % home_url, 'button_display': "返回主页" } + logger.error('[异常] :%s' % str(reset_e)) return render(request, msg_template, context) - - # 格式化用户名 - _, username = format2username(email) - if _ is False: - context = { - 'msg': username, - 'button_click': "window.location.href='%s'" % home_url, - 'button_display': "返回主页" - } - return render(request, msg_template, context) - return ops_account(ad_ops=AdOps(), request=request, msg_template=msg_template, home_url=home_url, username=username, new_password=_new_password) - except Exception as reset_e: - context = { - 'msg': "错误[%s],请与管理员联系." % str(reset_e), - 'button_click': "window.location.href='%s'" % home_url, - 'button_display': "返回主页" - } - logger.error('[异常] :%s' % str(reset_e)) - return render(request, msg_template, context) + finally: + del request.session[username] else: context = { - 'msg': "请从主页开始进行操作。", + 'msg': "认证已经失效,请从主页重新进行操作。", 'button_click': "window.location.href='%s'" % home_url, 'button_display': "返回主页" } @@ -245,62 +219,26 @@ def unlock_account(request): :return: """ home_url = '%s://%s' % (request.scheme, HOME_URL) - if request.method == 'GET': - _status, user_info = crypto_id_2_user_info(_ops, request, msg_template, home_url, scan_params.AUTH_APP) - if not _status: - return render(request, msg_template, user_info) - # 通过user_info拿到用户信息,并格式化为username - _, email = get_email_from_userinfo(user_info) - if not _: - context = { - 'msg': email, - 'button_click': "window.location.href='%s'" % home_url, - 'button_display': "返回主页" - } - return render(request, msg_template, context) - - _, username = format2username(email) - if _ is False: - context = { - 'msg': username, - 'button_click': "window.location.href='%s'" % home_url, - 'button_display': "返回主页" - } - return render(request, msg_template, context) - context = { - 'username': username, - } - return render(request, 'resetPassword.v1.html', context) - - elif request.method == 'POST': - _status, user_info = crypto_id_2_user_info(_ops, request, msg_template, home_url, scan_params.AUTH_APP) - if not _status: - return render(request, msg_template, user_info) - - # 通过user_info拿到用户信息,并格式化为username - _, email = get_email_from_userinfo(user_info) - if not _: - context = { - 'msg': email, - 'button_click': "window.location.href='%s'" % home_url, - 'button_display': "返回主页" - } - return render(request, msg_template, context) - - # 格式化用户名 - _, username = format2username(email) - if _ is False: - context = { - 'msg': username, - 'button_click': "window.location.href='%s'" % home_url, - 'button_display': "返回主页" - } - return render(request, msg_template, context) - return ops_account(AdOps(), request, msg_template, home_url, username, None) + if request.method == 'POST': + username = request.POST.get('username') + code = request.POST.get('code') + if request.session.get(username) and request.session.get(username) == code: + try: + return ops_account(AdOps(), request, msg_template, home_url, username, None) + except Exception as reset_e: + context = { + 'msg': "错误[%s],请与管理员联系." % str(reset_e), + 'button_click': "window.location.href='%s'" % home_url, + 'button_display': "返回主页" + } + logger.error('[异常] :%s' % str(reset_e)) + return render(request, msg_template, context) + finally: + del request.session[username] else: context = { - 'msg': "请从主页开始进行操作。", + 'msg': "认证已经失效,请从主页重新进行操作。", 'button_click': "window.location.href='%s'" % home_url, 'button_display': "返回主页" } diff --git a/static/css/login.css b/static/css/login.css index 1693c81..6a26efb 100644 --- a/static/css/login.css +++ b/static/css/login.css @@ -1,4 +1,4 @@ -a, body, button, dd, div, dl, dt, h1, h2, h3, h4, h5, h6, input, li, ol, p, td, textarea, ul { margin: 0; padding: 0; } +a, body, button, dd, div, dl, dt, h1, h2, h3, h4, h5, h6, input, li, ol, p, td, textarea, ul { margin: 0; padding: 0; } body, button, input, select, textarea { font: 9pt/1.5 tahoma,arial,Hiragino Sans GB,\5b8b\4f53,sans-serif; } button, h1, h2, h3, h4, h5, h6, input, select, textarea { font-size: 100%; } /*background-image: linear-gradient(160deg, #2f548e 20%,#043559 80%);*/ diff --git a/static/js/check.js b/static/js/check.js index 6456314..305d4f3 100644 --- a/static/js/check.js +++ b/static/js/check.js @@ -1,4 +1,4 @@ - + function BtnClick(btn, type, unsecpwd) { $(btn).click(function () { // ^(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[!@#$\%\^\&\*\(\)])[0-9a-zA-Z!@#$\%\^\&\*\(\)]{8,32}$ 要求密码了里面包含字母、数字、特殊字符。 diff --git a/templates/index.v1.html b/templates/index.v1.html index 94302a9..73ff899 100644 --- a/templates/index.v1.html +++ b/templates/index.v1.html @@ -1,4 +1,4 @@ -{% load static %} +{% load static %} diff --git a/templates/resetPassword.v1.html b/templates/resetPassword.v1.html index 59c1a63..d7f11e3 100644 --- a/templates/resetPassword.v1.html +++ b/templates/resetPassword.v1.html @@ -6,9 +6,7 @@ 自助密码平台 - -
@@ -22,6 +20,7 @@ {% csrf_token %}

重置

+

@@ -34,6 +33,7 @@

重置

新密码8至30位长度,要求包含大小写字母及数字。 +