提交 978f7fa2a3737508c283610453f1d682becb358a
Merge branch 'master' of http://gitlab.ctune.cn/weijunh/DMapManager
正在显示
12 个修改的文件
包含
402 行增加
和
110 行删除
1 | +from datetime import datetime | ||
1 | from logging import error | 2 | from logging import error |
2 | from flasgger import swag_from | 3 | from flasgger import swag_from |
3 | from app.util import BlueprintApi | 4 | from app.util import BlueprintApi |
@@ -6,7 +7,7 @@ from .models import * | @@ -6,7 +7,7 @@ from .models import * | ||
6 | from .oauth2 import authorization, generate_user_info, require_oauth | 7 | from .oauth2 import authorization, generate_user_info, require_oauth |
7 | from authlib.oauth2 import OAuth2Error | 8 | from authlib.oauth2 import OAuth2Error |
8 | from authlib.integrations.flask_oauth2 import current_token | 9 | from authlib.integrations.flask_oauth2 import current_token |
9 | -from . import user_create, client_create, client_query, user_query, user_update, user_delete | 10 | +from . import user_create, client_create, client_query, user_query, user_update, user_delete, auth_log_query |
10 | import configure | 11 | import configure |
11 | from app.decorators.auth_decorator import auth_decorator | 12 | from app.decorators.auth_decorator import auth_decorator |
12 | import time | 13 | import time |
@@ -15,7 +16,8 @@ from app.util.component.StructurePrint import StructurePrint | @@ -15,7 +16,8 @@ from app.util.component.StructurePrint import StructurePrint | ||
15 | import traceback | 16 | import traceback |
16 | # from oauthlib import oauth2 | 17 | # from oauthlib import oauth2 |
17 | import requests | 18 | import requests |
18 | -from app.modules.auth.models import OAuth2Token, User, db | 19 | +from app.modules.auth.models import OAuth2Token, User, db, OAuthLog |
20 | +from app.util.enum.AuthEnum import AuthEnum, OriginEnum, OperateEnum | ||
19 | 21 | ||
20 | 22 | ||
21 | def current_user(): | 23 | def current_user(): |
@@ -35,6 +37,16 @@ def split_by_crlf(s): | @@ -35,6 +37,16 @@ def split_by_crlf(s): | ||
35 | return [v for v in s.splitlines() if v] | 37 | return [v for v in s.splitlines() if v] |
36 | 38 | ||
37 | 39 | ||
40 | +def getRedirectUrl(request): # 获取重定向地址 | ||
41 | + # 获取头部信息 | ||
42 | + X_Forwarded_Proto = request.headers.get("X-Forwarded-Proto") # 协议 | ||
43 | + X_Forwarded_Host = request.headers.get("X-Forwarded-Host") # host | ||
44 | + if not X_Forwarded_Proto == None and not X_Forwarded_Host == None: | ||
45 | + return X_Forwarded_Proto+"://"+X_Forwarded_Host | ||
46 | + else: | ||
47 | + return request.host_url.rstrip("/") | ||
48 | + | ||
49 | + | ||
38 | class DataManager(BlueprintApi): | 50 | class DataManager(BlueprintApi): |
39 | bp = Blueprint("Auth", __name__, url_prefix="/auth") | 51 | bp = Blueprint("Auth", __name__, url_prefix="/auth") |
40 | 52 | ||
@@ -45,7 +57,7 @@ class DataManager(BlueprintApi): | @@ -45,7 +57,7 @@ class DataManager(BlueprintApi): | ||
45 | request2 = authorization.create_oauth2_request(request) | 57 | request2 = authorization.create_oauth2_request(request) |
46 | grant2 = authorization.get_authorization_grant(request=request2) | 58 | grant2 = authorization.get_authorization_grant(request=request2) |
47 | redirect_uri = grant2.validate_authorization_request() | 59 | redirect_uri = grant2.validate_authorization_request() |
48 | - session["redirect_uri"] = redirect_uri | 60 | + session["redirect_uri"] = redirect_uri # 记录跳转重定向地址 |
49 | if request.method == "GET": | 61 | if request.method == "GET": |
50 | # 没有登录,跳转到登录界面 | 62 | # 没有登录,跳转到登录界面 |
51 | try: | 63 | try: |
@@ -71,8 +83,11 @@ class DataManager(BlueprintApi): | @@ -71,8 +83,11 @@ class DataManager(BlueprintApi): | ||
71 | crypt_pwd = request.form.get("password") | 83 | crypt_pwd = request.form.get("password") |
72 | # password = SM3.encode(crypt_pwd) | 84 | # password = SM3.encode(crypt_pwd) |
73 | password = SM3.encode(AESHelper.decode(crypt_pwd)) | 85 | password = SM3.encode(AESHelper.decode(crypt_pwd)) |
86 | + | ||
87 | + # 仅支持dmap平台保留用户登录 | ||
88 | + origin_type = OriginEnum.Dmap.name.lower() | ||
74 | user = User.query.filter_by( | 89 | user = User.query.filter_by( |
75 | - username=username, password=password).first() | 90 | + username=username, password=password, origin=origin_type).first() |
76 | if not user: | 91 | if not user: |
77 | error = "账号或密码不正确" | 92 | error = "账号或密码不正确" |
78 | flash(error) | 93 | flash(error) |
@@ -83,6 +98,17 @@ class DataManager(BlueprintApi): | @@ -83,6 +98,17 @@ class DataManager(BlueprintApi): | ||
83 | if user: | 98 | if user: |
84 | session["id"] = user.id | 99 | session["id"] = user.id |
85 | grant_user = user | 100 | grant_user = user |
101 | + | ||
102 | + # 日志 | ||
103 | + log = OAuthLog(user_id=user.id, username=user.username, | ||
104 | + auth_type=AuthEnum.Other.name.lower(), | ||
105 | + message="认证成功", create_time=datetime.now(), | ||
106 | + operate_type=OperateEnum.Login, | ||
107 | + displayname=user.displayname, ip=request.remote_addr | ||
108 | + ) | ||
109 | + db.session.add(log) | ||
110 | + db.session.commit() | ||
111 | + | ||
86 | return authorization.create_authorization_response(request=request, grant_user=grant_user) | 112 | return authorization.create_authorization_response(request=request, grant_user=grant_user) |
87 | 113 | ||
88 | # try: | 114 | # try: |
@@ -116,16 +142,34 @@ class DataManager(BlueprintApi): | @@ -116,16 +142,34 @@ class DataManager(BlueprintApi): | ||
116 | def logout(): | 142 | def logout(): |
117 | try: | 143 | try: |
118 | request2 = authorization.create_oauth2_request(request) | 144 | request2 = authorization.create_oauth2_request(request) |
119 | - grant1 = authorization.get_authorization_grant(request=request2) | 145 | + grant1 = authorization.get_authorization_grant( |
146 | + request=request2) | ||
120 | redirect_uri = grant1.validate_authorization_request() | 147 | redirect_uri = grant1.validate_authorization_request() |
121 | access_token = request.args.get("accesstoken") | 148 | access_token = request.args.get("accesstoken") |
122 | - accesstoken = OAuth2Token.query.filter_by( | ||
123 | - access_token=access_token).first() | ||
124 | - accesstoken.revoked = True | ||
125 | - db.session.commit() | ||
126 | - remove_user() | 149 | + |
150 | + if not access_token == None: | ||
151 | + accesstoken = OAuth2Token.query.filter_by( | ||
152 | + access_token=access_token).one_or_none() | ||
153 | + if not accesstoken == None: | ||
154 | + accesstoken.revoked = True | ||
155 | + db.session.commit() | ||
156 | + if current_user() != None: | ||
157 | + remove_user() | ||
158 | + | ||
159 | + user = User.query.get(accesstoken.user_id) | ||
160 | + # 日志 | ||
161 | + if user != None: | ||
162 | + log = OAuthLog(user_id=user.id, username=user.username, | ||
163 | + auth_type=AuthEnum.Other.name.lower(), | ||
164 | + message="注销成功", create_time=datetime.now(), | ||
165 | + operate_type=OperateEnum.Logout, token=access_token, | ||
166 | + displayname=user.displayname, ip=request.remote_addr | ||
167 | + ) | ||
168 | + db.session.add(log) | ||
169 | + db.session.commit() | ||
170 | + | ||
127 | except OAuth2Error as error: | 171 | except OAuth2Error as error: |
128 | - return jsonify(dict(error.get_body())) | 172 | + StructurePrint().print(error.__str__()+":" + traceback.format_exc(), "error") |
129 | return redirect(redirect_uri) | 173 | return redirect(redirect_uri) |
130 | 174 | ||
131 | """接口""" | 175 | """接口""" |
@@ -207,15 +251,15 @@ class DataManager(BlueprintApi): | @@ -207,15 +251,15 @@ class DataManager(BlueprintApi): | ||
207 | except Exception as e: | 251 | except Exception as e: |
208 | StructurePrint().print(e.__str__()+":" + traceback.format_exc(), "error") | 252 | StructurePrint().print(e.__str__()+":" + traceback.format_exc(), "error") |
209 | 253 | ||
210 | - @staticmethod | ||
211 | - @bp.route("/translate", methods=["GET"]) | ||
212 | - def translate(): | ||
213 | - password = ['esri@123', 'admin', 'DMap@123', 'passwd'] | ||
214 | - result = {} | ||
215 | - for p in password: | ||
216 | - new_pwd = SM3.encode(p) | ||
217 | - result[p] = new_pwd | ||
218 | - return result | 254 | + # @staticmethod |
255 | + # @bp.route("/translate", methods=["GET"]) | ||
256 | + # def translate(): | ||
257 | + # password = ['esri@123', 'admin', 'DMap@123', 'passwd','dci112..'] | ||
258 | + # result = {} | ||
259 | + # for p in password: | ||
260 | + # new_pwd = SM3.encode(p) | ||
261 | + # result[p] = new_pwd | ||
262 | + # return result | ||
219 | 263 | ||
220 | ''' | 264 | ''' |
221 | 三方登录:OA | 265 | 三方登录:OA |
@@ -226,8 +270,9 @@ class DataManager(BlueprintApi): | @@ -226,8 +270,9 @@ class DataManager(BlueprintApi): | ||
226 | client = oauth2.WebApplicationClient( | 270 | client = oauth2.WebApplicationClient( |
227 | configure.OA["client_id"]) | 271 | configure.OA["client_id"]) |
228 | state = client.state_generator() | 272 | state = client.state_generator() |
273 | + StructurePrint().print(request.headers, "info") | ||
229 | auth_uri = client.prepare_request_uri( | 274 | auth_uri = client.prepare_request_uri( |
230 | - configure.OA["authorization_endpoint"], configure.OA["redirect_uri"], configure.OA["scope"], state) | 275 | + configure.OA["authorization_endpoint"], getRedirectUrl(request) + configure.OA["redirect_uri"], configure.OA["scope"], state) |
231 | session["oauth_state"] = state | 276 | session["oauth_state"] = state |
232 | return redirect(auth_uri) | 277 | return redirect(auth_uri) |
233 | 278 | ||
@@ -237,78 +282,111 @@ class DataManager(BlueprintApi): | @@ -237,78 +282,111 @@ class DataManager(BlueprintApi): | ||
237 | @staticmethod | 282 | @staticmethod |
238 | @bp.route("/oa/callback", methods=["GET"]) | 283 | @bp.route("/oa/callback", methods=["GET"]) |
239 | def oa_callback(): | 284 | def oa_callback(): |
285 | + try: | ||
286 | + auth_default_redirect_uri = configure.auth_default_redirect_uri | ||
287 | + client = oauth2.WebApplicationClient( | ||
288 | + configure.OA["client_id"]) | ||
289 | + | ||
290 | + # 获取code | ||
291 | + code = client.parse_request_uri_response( | ||
292 | + request.url, session["oauth_state"]).get("code") | ||
293 | + | ||
294 | + if code == None: | ||
295 | + return "登录失败" | ||
296 | + | ||
297 | + # 获取token | ||
298 | + body = client.prepare_request_body( | ||
299 | + code, redirect_uri=getRedirectUrl(request) + configure.OA["redirect_uri"], client_secret=configure.OA["client_secret"]) | ||
300 | + | ||
301 | + r = requests.post(configure.OA["token_endpoint"], body, headers={ | ||
302 | + "Content-Type": "application/x-www-form-urlencoded"}) | ||
303 | + | ||
304 | + tokeninfo = r.json() | ||
305 | + access_token = tokeninfo.get("access_token") | ||
306 | + id_token = tokeninfo.get("id_token") | ||
307 | + | ||
308 | + origin_type = "dci_oa" # 三方登录标识 | ||
309 | + if access_token: | ||
310 | + # 获取用户信息 | ||
311 | + userinfo_url = configure.OA["userinfo_endpoint"] | ||
312 | + user_request = requests.get(userinfo_url, headers={ | ||
313 | + "Authorization": "Bearer %s" % access_token}) | ||
314 | + userinfo = user_request.json() | ||
315 | + user_name = userinfo.get("user_name") | ||
316 | + display_name = userinfo.get("displayname") | ||
317 | + display_name = display_name.split( | ||
318 | + )[-1] if display_name != None else user_name | ||
319 | + | ||
320 | + # 默认关联dmap用户 | ||
321 | + try: | ||
322 | + user = User.query.filter_by( | ||
323 | + username=user_name, origin=origin_type).first() | ||
324 | + except error as e: | ||
325 | + user = None | ||
326 | + | ||
327 | + # 用户不存在,创建用户 | ||
240 | 328 | ||
241 | - client = oauth2.WebApplicationClient( | ||
242 | - configure.OA["client_id"]) | ||
243 | - | ||
244 | - # 获取code | ||
245 | - code = client.parse_request_uri_response( | ||
246 | - request.url, session["oauth_state"]).get("code") | ||
247 | - | ||
248 | - if code == None: | ||
249 | - return "登录失败" | ||
250 | - | ||
251 | - # 获取token | ||
252 | - body = client.prepare_request_body( | ||
253 | - code, redirect_uri=configure.OA["redirect_uri"], client_secret=configure.OA["client_secret"]) | ||
254 | - | ||
255 | - r = requests.post(configure.OA["token_endpoint"], body, headers={ | ||
256 | - "Content-Type": "application/x-www-form-urlencoded"}) | ||
257 | - | ||
258 | - tokeninfo = r.json() | ||
259 | - access_token = tokeninfo.get("access_token") | ||
260 | - | ||
261 | - if access_token: | ||
262 | - # 获取用户信息 | ||
263 | - userinfo_url = configure.OA["userinfo_endpoint"] | ||
264 | - user_request = requests.get(userinfo_url, headers={ | ||
265 | - "Authorization": "Bearer %s" % access_token}) | ||
266 | - userinfo = user_request.json() | ||
267 | - user_name = userinfo.get("user_name") | ||
268 | - display_name = userinfo.get("displayname") | ||
269 | - | ||
270 | - # 默认关联dmap用户 | ||
271 | - try: | ||
272 | - user = User.query.filter_by( | ||
273 | - username=user_name).first() | ||
274 | - except error as e: | ||
275 | - user = None | 329 | + if not user: |
330 | + user = User(username=user_name, password=SM3.encode('DMap@123'), role='dataman', | ||
331 | + phone='', company='', position='', email='', displayname=display_name, | ||
332 | + origin=origin_type, | ||
333 | + create_time=time.strftime( | ||
334 | + "%Y-%m-%d %H:%M:%S", time.localtime()), | ||
335 | + update_time=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) | ||
336 | + db.session.add(user) | ||
337 | + db.session.commit() | ||
338 | + | ||
339 | + session["id"] = user.id | ||
340 | + | ||
341 | + # dmap token授权 | ||
342 | + # 存入数据库 | ||
343 | + token = OAuth2Token( | ||
344 | + client_id=configure.OA["client_id"], | ||
345 | + token_type=tokeninfo.get("token_type"), | ||
346 | + access_token=access_token, | ||
347 | + scope=tokeninfo.get("scope"), | ||
348 | + expires_in=tokeninfo.get("expires_in"), | ||
349 | + user_id=user.id | ||
350 | + ) | ||
351 | + | ||
352 | + db.session.add(token) | ||
353 | + db.session.commit() | ||
276 | 354 | ||
277 | - # 用户不存在,创建用户 | ||
278 | - if not user: | ||
279 | - user = User(username=user_name, password=SM3.encode('DMap@123'), role='dataman', | ||
280 | - phone='', company='', position='', email='', | ||
281 | - create_time=time.strftime( | ||
282 | - "%Y-%m-%d %H:%M:%S", time.localtime()), | ||
283 | - update_time=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) | ||
284 | - db.session.add(user) | 355 | + redirect_uri = session["redirect_uri"] if "redirect_uri" in session else auth_default_redirect_uri |
356 | + | ||
357 | + #session["id_token"] = id_token | ||
358 | + response = make_response(redirect(redirect_uri)) | ||
359 | + response.set_cookie('accessToken', access_token, | ||
360 | + max_age=configure.expiretime) | ||
361 | + response.set_cookie('id_token', id_token, | ||
362 | + max_age=configure.expiretime) | ||
363 | + | ||
364 | + log = OAuthLog(user_id=user.id, username=user_name, | ||
365 | + auth_type=AuthEnum.Other.name.lower(), | ||
366 | + message="三方认证成功", create_time=datetime.now(), | ||
367 | + operate_type=OperateEnum.Login, token=access_token, | ||
368 | + displayname=display_name, ip=request.remote_addr) | ||
369 | + db.session.add(log) | ||
285 | db.session.commit() | 370 | db.session.commit() |
286 | 371 | ||
287 | - # dmap token授权 | ||
288 | - session["id"] = user.id | 372 | + return response |
373 | + else: | ||
374 | + raise Exception("缺少access_token") | ||
289 | 375 | ||
290 | - # 存入数据库 | ||
291 | - token = OAuth2Token( | ||
292 | - client_id=configure.OA["client_id"], | ||
293 | - token_type=tokeninfo.get("token_type"), | ||
294 | - access_token=access_token, | ||
295 | - scope=tokeninfo.get("scope"), | ||
296 | - expires_in=tokeninfo.get("expires_in"), | ||
297 | - user_id=user.id | ||
298 | - ) | ||
299 | - db.session.add(token) | ||
300 | - db.session.commit() | ||
301 | - redirect_uri = "" | ||
302 | - try: | ||
303 | - redirect_uri = session["redirect_uri"] | ||
304 | - if not redirect_uri: | ||
305 | - redirect_uri = '/' | ||
306 | - except: | ||
307 | - redirect_uri = "/" | ||
308 | - | ||
309 | - response = make_response(redirect(redirect_uri)) | ||
310 | - response.set_cookie('accessToken', access_token, max_age=604_800) | ||
311 | - | ||
312 | - return response | ||
313 | - else: | ||
314 | - return redirect('/') | 376 | + except Exception as e: |
377 | + StructurePrint().print(e.__str__()+":" + traceback.format_exc(), "error") | ||
378 | + pop_list = ["id", "redirect_uri"] | ||
379 | + for p in pop_list: | ||
380 | + if p in session: | ||
381 | + session.pop(p) | ||
382 | + return redirect(auth_default_redirect_uri) | ||
383 | + | ||
384 | + @staticmethod | ||
385 | + @bp.route("/logs", methods=["GET"]) | ||
386 | + @swag_from(auth_log_query.Api.api_doc) | ||
387 | + @auth_decorator(configure.UserPermission) | ||
388 | + def authLog(): | ||
389 | + ''' | ||
390 | + 登录日志 | ||
391 | + ''' | ||
392 | + return auth_log_query.Api().result |
app/modules/auth/auth_log_query.py
0 → 100644
1 | +# coding=utf-8 | ||
2 | +# author: qianyingz | ||
3 | +# createtime: 2022/03/09 | ||
4 | +# email: qianyingz@chinadci.com | ||
5 | +from tokenize import String | ||
6 | +from numpy import number | ||
7 | +from sqlalchemy import or_ | ||
8 | +from .models import * | ||
9 | +from app.util.component.ApiTemplate import ApiTemplate | ||
10 | +import json | ||
11 | + | ||
12 | + | ||
13 | +class Api(ApiTemplate): | ||
14 | + api_name = "登录日志" | ||
15 | + | ||
16 | + def para_check(self): | ||
17 | + pass | ||
18 | + | ||
19 | + def process(self): | ||
20 | + # 返回结果 | ||
21 | + res = {} | ||
22 | + res["result"] = False | ||
23 | + try: | ||
24 | + # 业务逻辑 | ||
25 | + page_index = int(self.para.get("page_index", "0")) | ||
26 | + page_size = int(self.para.get("page_size", "1000")) | ||
27 | + # name = self.para.get("name") | ||
28 | + order_by = self.para.get("order_by") | ||
29 | + filter_param = self.para.get("filter_param", "") | ||
30 | + filter_param_json = {} if filter_param == "" else json.loads( | ||
31 | + filter_param) | ||
32 | + kw = filter_param_json["kw"] if "kw" in filter_param_json else None | ||
33 | + type = filter_param_json["type"] if "type" in filter_param_json else None | ||
34 | + | ||
35 | + log_query = OAuthLog.query | ||
36 | + | ||
37 | + if kw != None: | ||
38 | + if isinstance(kw, str): | ||
39 | + filter_username_query = log_query.filter(OAuthLog.username.like( | ||
40 | + "%" + kw + "%")) | ||
41 | + filter_display_query = log_query.filter( | ||
42 | + OAuthLog.displayname.like("%" + kw + "%")) | ||
43 | + log_query = filter_username_query.union( | ||
44 | + filter_display_query) | ||
45 | + | ||
46 | + if type != None: | ||
47 | + if isinstance(type, (str,int)): | ||
48 | + log_query = log_query.filter(OAuthLog.operate_type == type) | ||
49 | + elif isinstance(type, list): | ||
50 | + log_query = log_query.filter(OAuthLog.operate_type.in_(type)) | ||
51 | + | ||
52 | + log_query = log_query.order_by(OAuthLog.create_time.desc()) | ||
53 | + | ||
54 | + count = log_query.count() | ||
55 | + logs = log_query.limit(page_size).offset( | ||
56 | + page_index*page_size).all() | ||
57 | + | ||
58 | + res["data"] = {"count": count, | ||
59 | + "list": list(map(lambda t: | ||
60 | + {"id": t.id, "username": t.username, "ip": t.ip, | ||
61 | + "message": t.message, "create_time": t.create_time.strftime("%Y-%m-%d %H:%M:%S"), | ||
62 | + "operate_type": t.operate_type, | ||
63 | + "auth_type": t.auth_type, | ||
64 | + "displayname": t.displayname}, logs))} | ||
65 | + | ||
66 | + # # 获取集合 | ||
67 | + # userLinq = User.query.order_by(User.id.desc()) | ||
68 | + # if name: | ||
69 | + # userLinq = userLinq.filter( | ||
70 | + # User.username.like("%" + name + "%")) | ||
71 | + # tmp_count = userLinq.count() | ||
72 | + # tmp_list = userLinq.limit(page_size).offset( | ||
73 | + # page_index * page_size).all() | ||
74 | + # res["data"] = { | ||
75 | + # "count": tmp_count, | ||
76 | + # "list": list(map(lambda t: | ||
77 | + # {"guid": t.id, "username": t.username, | ||
78 | + # "role": t.role, "display_name": t.display_name, | ||
79 | + # "status": t.status}, | ||
80 | + # tmp_list))} | ||
81 | + | ||
82 | + res["result"] = True | ||
83 | + | ||
84 | + except Exception as e: | ||
85 | + raise e | ||
86 | + return res | ||
87 | + | ||
88 | + api_doc = { | ||
89 | + "tags": ["登录日志"], | ||
90 | + "parameters": [ | ||
91 | + {"name": "page_index", | ||
92 | + "in": "query", | ||
93 | + "type": "int", | ||
94 | + "description": "当前页", | ||
95 | + "default": 0}, | ||
96 | + {"name": "page_size", | ||
97 | + "in": "query", | ||
98 | + "type": "int", | ||
99 | + "description": "条数", | ||
100 | + "default": 1000}, | ||
101 | + {"name": "order_by", | ||
102 | + "in": "query", | ||
103 | + "type": "string", | ||
104 | + "description": "排序"}, | ||
105 | + {"name": "filter_param", | ||
106 | + "in": "query", | ||
107 | + "type": "json", | ||
108 | + "description": "过滤"} | ||
109 | + ], | ||
110 | + "responses": { | ||
111 | + 200: { | ||
112 | + "schema": { | ||
113 | + "properties": { | ||
114 | + } | ||
115 | + } | ||
116 | + } | ||
117 | + } | ||
118 | + } |
1 | -from flask_sqlalchemy import sqlalchemy | ||
2 | -from sqlalchemy import Column, Integer, Text, Time, ForeignKey | 1 | +from sqlalchemy import Column, Integer, Text, Time, ForeignKey, DateTime |
3 | from app.models import db | 2 | from app.models import db |
4 | from authlib.integrations.sqla_oauth2 import ( | 3 | from authlib.integrations.sqla_oauth2 import ( |
5 | OAuth2ClientMixin, | 4 | OAuth2ClientMixin, |
@@ -7,6 +6,7 @@ from authlib.integrations.sqla_oauth2 import ( | @@ -7,6 +6,7 @@ from authlib.integrations.sqla_oauth2 import ( | ||
7 | OAuth2AuthorizationCodeMixin | 6 | OAuth2AuthorizationCodeMixin |
8 | ) | 7 | ) |
9 | from sqlalchemy.orm import relationship | 8 | from sqlalchemy.orm import relationship |
9 | +from app.util.enum.AuthEnum import OriginEnum,UserStatusEnum | ||
10 | 10 | ||
11 | 11 | ||
12 | class User (db.Model): | 12 | class User (db.Model): |
@@ -16,17 +16,18 @@ class User (db.Model): | @@ -16,17 +16,18 @@ class User (db.Model): | ||
16 | __tablename__ = "dmap_user" | 16 | __tablename__ = "dmap_user" |
17 | id = Column(Integer, primary_key=True) | 17 | id = Column(Integer, primary_key=True) |
18 | username = Column(Text) | 18 | username = Column(Text) |
19 | - | ||
20 | password = Column(Text) | 19 | password = Column(Text) |
21 | company = Column(Text) | 20 | company = Column(Text) |
22 | position = Column(Text) | 21 | position = Column(Text) |
23 | phone = Column(Text) | 22 | phone = Column(Text) |
24 | email = Column(Text) | 23 | email = Column(Text) |
25 | - create_time = Column(Time) | ||
26 | - update_time = Column(Time) | 24 | + create_time = Column(DateTime) |
25 | + update_time = Column(DateTime) | ||
27 | role = Column(Text) | 26 | role = Column(Text) |
28 | - #display_name = Column(Text, nullable=True) # 昵称 | ||
29 | - #origin = Column(Text, default="dmap") # 用户来源,默认dmap平台用户 | 27 | + displayname = Column(Text, nullable=True) # 昵称 |
28 | + # 用户来源,默认dmap平台用户 | ||
29 | + origin = Column(Text, default=OriginEnum.Dmap.name.lower()) | ||
30 | + status = Column(Integer, default=UserStatusEnum.Active) # 1:激活,2:冻结,0:注销 | ||
30 | 31 | ||
31 | def __str__(self): | 32 | def __str__(self): |
32 | return self.username | 33 | return self.username |
@@ -68,3 +69,26 @@ class OAuth2Token(db.Model, OAuth2TokenMixin): | @@ -68,3 +69,26 @@ class OAuth2Token(db.Model, OAuth2TokenMixin): | ||
68 | Integer, ForeignKey('dmap_user.id', ondelete='CASCADE')) | 69 | Integer, ForeignKey('dmap_user.id', ondelete='CASCADE')) |
69 | # name = Column(Text) | 70 | # name = Column(Text) |
70 | user = relationship('User') | 71 | user = relationship('User') |
72 | + | ||
73 | + | ||
74 | +''' | ||
75 | +认证日志 | ||
76 | +''' | ||
77 | + | ||
78 | +# 认证日志 | ||
79 | + | ||
80 | + | ||
81 | +class OAuthLog(db.Model): | ||
82 | + __tablename__ = "dmap_oauth_log" | ||
83 | + | ||
84 | + id = Column(Integer, primary_key=True) | ||
85 | + user_id = Column(Text, nullable=False) | ||
86 | + username = Column(Text) # 用户输入账号 | ||
87 | + displayname=Column(Text) # 昵称 | ||
88 | + ip = Column(Text) | ||
89 | + # 登录方式:password,三方登录 | ||
90 | + auth_type = Column(Text) | ||
91 | + message = Column(Text) # 登录返回提示 | ||
92 | + create_time = Column(DateTime) # 记录创建时间 | ||
93 | + operate_type = Column(Integer, nullable=False) # 操作类型,登录,注销 | ||
94 | + token = Column(Text) |
@@ -51,7 +51,7 @@ def exists_nonce(nonce, req): | @@ -51,7 +51,7 @@ def exists_nonce(nonce, req): | ||
51 | 51 | ||
52 | 52 | ||
53 | def generate_user_info(user, scope): | 53 | def generate_user_info(user, scope): |
54 | - return UserInfo(sub=str(user.id), name=user.username, role=user.role, company=user.company) | 54 | + return UserInfo(sub=str(user.id), name=user.username, role=user.role, company=user.company, display_name=user.displayname, status=user.status) |
55 | 55 | ||
56 | 56 | ||
57 | def create_authorization_code(client, grant_user, request): | 57 | def create_authorization_code(client, grant_user, request): |
@@ -27,18 +27,19 @@ class Api(ApiTemplate): | @@ -27,18 +27,19 @@ class Api(ApiTemplate): | ||
27 | res["result"] = False | 27 | res["result"] = False |
28 | try: | 28 | try: |
29 | # 业务逻辑 | 29 | # 业务逻辑 |
30 | - username = AESHelper.decode( self.para.get("username", '')) | 30 | + username = AESHelper.decode(self.para.get("username", '')) |
31 | password = SM3.encode(AESHelper.decode(self.para.get("pwd", ''))) | 31 | password = SM3.encode(AESHelper.decode(self.para.get("pwd", ''))) |
32 | role = AESHelper.decode(self.para.get("role", '')) | 32 | role = AESHelper.decode(self.para.get("role", '')) |
33 | company = AESHelper.decode(self.para.get("company", '')) | 33 | company = AESHelper.decode(self.para.get("company", '')) |
34 | position = AESHelper.decode(self.para.get("position", '')) | 34 | position = AESHelper.decode(self.para.get("position", '')) |
35 | email = AESHelper.decode(self.para.get("email", '')) | 35 | email = AESHelper.decode(self.para.get("email", '')) |
36 | phone = AESHelper.decode(self.para.get("phone", '')) | 36 | phone = AESHelper.decode(self.para.get("phone", '')) |
37 | + displayname = username | ||
37 | # 是否重名 | 38 | # 是否重名 |
38 | if(User.query.filter_by(username=username).one_or_none()): | 39 | if(User.query.filter_by(username=username).one_or_none()): |
39 | res["msg"] = "username 已存在" | 40 | res["msg"] = "username 已存在" |
40 | else: | 41 | else: |
41 | - user = User(username=username, password=password, role=role, | 42 | + user = User(username=username, displayname=displayname, password=password, role=role, |
42 | phone=phone, company=company, position=position, email=email, | 43 | phone=phone, company=company, position=position, email=email, |
43 | create_time=time.strftime( | 44 | create_time=time.strftime( |
44 | "%Y-%m-%d %H:%M:%S", time.localtime()), | 45 | "%Y-%m-%d %H:%M:%S", time.localtime()), |
@@ -20,7 +20,6 @@ class Api(ApiTemplate): | @@ -20,7 +20,6 @@ class Api(ApiTemplate): | ||
20 | res["result"] = False | 20 | res["result"] = False |
21 | try: | 21 | try: |
22 | # 业务逻辑 | 22 | # 业务逻辑 |
23 | - pass | ||
24 | page_index = int(self.para.get("page_index", "0")) | 23 | page_index = int(self.para.get("page_index", "0")) |
25 | page_size = int(self.para.get("page_size", "1000")) | 24 | page_size = int(self.para.get("page_size", "1000")) |
26 | name = self.para.get("name") | 25 | name = self.para.get("name") |
@@ -31,7 +30,8 @@ class Api(ApiTemplate): | @@ -31,7 +30,8 @@ class Api(ApiTemplate): | ||
31 | res["data"] = {"guid": tmp_user.id, "username": tmp_user.username, | 30 | res["data"] = {"guid": tmp_user.id, "username": tmp_user.username, |
32 | "role": tmp_user.role, "company": tmp_user.company, | 31 | "role": tmp_user.role, "company": tmp_user.company, |
33 | "position": tmp_user.position, "email": tmp_user.email, | 32 | "position": tmp_user.position, "email": tmp_user.email, |
34 | - "phone": tmp_user.phone} | 33 | + "phone": tmp_user.phone, "display_name": tmp_user.displayname, |
34 | + "status": tmp_user.status} | ||
35 | else: | 35 | else: |
36 | # 获取集合 | 36 | # 获取集合 |
37 | userLinq = User.query.order_by(User.id.desc()) | 37 | userLinq = User.query.order_by(User.id.desc()) |
@@ -45,7 +45,8 @@ class Api(ApiTemplate): | @@ -45,7 +45,8 @@ class Api(ApiTemplate): | ||
45 | "count": tmp_count, | 45 | "count": tmp_count, |
46 | "list": list(map(lambda t: | 46 | "list": list(map(lambda t: |
47 | {"guid": t.id, "username": t.username, | 47 | {"guid": t.id, "username": t.username, |
48 | - "role": t.role}, | 48 | + "role": t.role, "display_name": t.displayname, |
49 | + "status": t.status}, | ||
49 | tmp_list))} | 50 | tmp_list))} |
50 | res["result"] = True | 51 | res["result"] = True |
51 | 52 |
@@ -55,7 +55,7 @@ class Api(ApiTemplate): | @@ -55,7 +55,7 @@ class Api(ApiTemplate): | ||
55 | {"name": "database_guid", | 55 | {"name": "database_guid", |
56 | "in": "formData", | 56 | "in": "formData", |
57 | "type": "string", | 57 | "type": "string", |
58 | - "description": "数据库guid", "": "true"}, | 58 | + "description": "数据库guid", "required": "true"}, |
59 | 59 | ||
60 | ], | 60 | ], |
61 | "responses":{ | 61 | "responses":{ |
@@ -238,4 +238,28 @@ button { | @@ -238,4 +238,28 @@ button { | ||
238 | -webkit-box-sizing: border-box; | 238 | -webkit-box-sizing: border-box; |
239 | -moz-box-sizing: border-box; | 239 | -moz-box-sizing: border-box; |
240 | box-sizing: border-box; | 240 | box-sizing: border-box; |
241 | +} | ||
242 | + | ||
243 | +.other-mod { | ||
244 | + margin-top: 35px; | ||
245 | +} | ||
246 | + | ||
247 | +.other-mod .other-tit { | ||
248 | + color: #999; | ||
249 | + display: inline-block; | ||
250 | + margin-right: 20px; | ||
251 | +} | ||
252 | + | ||
253 | +.other-mod .other-link { | ||
254 | + text-align: right; | ||
255 | + margin-left: 20px; | ||
256 | + float: right; | ||
257 | +} | ||
258 | + | ||
259 | +.other-mod .other-link a { | ||
260 | + color: #545454; | ||
261 | +} | ||
262 | + | ||
263 | +.other-mod .other-link:hover a { | ||
264 | + color: #3081c3; | ||
241 | } | 265 | } |
@@ -76,8 +76,13 @@ | @@ -76,8 +76,13 @@ | ||
76 | 立即登录 | 76 | 立即登录 |
77 | </button> | 77 | </button> |
78 | </div> | 78 | </div> |
79 | - <div>——或者——</div> | ||
80 | - <a href="/auth/oa"> 城信所统一用户认证 </a> | 79 | + |
80 | + <div class="form-group other-mod"> | ||
81 | + <div class="other-tit"><span>其他登录方式</span></div> | ||
82 | + <div class="other-link"> | ||
83 | + <a href="/auth/oa">城信所统一用户认证</a> | ||
84 | + </div> | ||
85 | + </div> | ||
81 | </form> | 86 | </form> |
82 | </div> | 87 | </div> |
83 | <div class="clear"></div> | 88 | <div class="clear"></div> |
app/util/enum/AuthEnum.py
0 → 100644
1 | +from enum import Enum,IntEnum | ||
2 | + | ||
3 | +''' | ||
4 | +认证方式 | ||
5 | +''' | ||
6 | + | ||
7 | + | ||
8 | +class AuthEnum(IntEnum): | ||
9 | + Password = 1 # 账号密码登录,包括三方的账号密码登录 | ||
10 | + Other = 3 # 三方登录 | ||
11 | + | ||
12 | + | ||
13 | +''' | ||
14 | +用户来源 | ||
15 | +''' | ||
16 | + | ||
17 | + | ||
18 | +class OriginEnum(IntEnum): | ||
19 | + Dmap = 1 | ||
20 | + | ||
21 | + | ||
22 | +'''用户状态''' | ||
23 | + | ||
24 | + | ||
25 | +class UserStatusEnum(IntEnum): | ||
26 | + Cancellation = 0, # 注销 | ||
27 | + Active = 1, # 活跃 | ||
28 | + Freeze = 2 # 冻结 | ||
29 | + | ||
30 | + | ||
31 | +class OperateEnum(IntEnum): | ||
32 | + Login = 1, # 登录 | ||
33 | + Logout = 2 # 注销 |
@@ -27,13 +27,17 @@ ServicePermission = ['admin', 'dataman', 'publisher'] | @@ -27,13 +27,17 @@ ServicePermission = ['admin', 'dataman', 'publisher'] | ||
27 | 27 | ||
28 | log_level = logging.INFO | 28 | log_level = logging.INFO |
29 | 29 | ||
30 | +expiretime = 604_800 # 7天 | ||
30 | 31 | ||
31 | OA = { | 32 | OA = { |
32 | "client_id": "dmap", | 33 | "client_id": "dmap", |
33 | "client_secret": "secret", | 34 | "client_secret": "secret", |
34 | "scope": "openid profile", | 35 | "scope": "openid profile", |
35 | - "redirect_uri": "http://localhost:8841/auth/oa/callback", | 36 | + "redirect_uri": "/auth/oa/callback", |
36 | "authorization_endpoint": "https://login.chinadci.com/netsso/connect/authorize", | 37 | "authorization_endpoint": "https://login.chinadci.com/netsso/connect/authorize", |
37 | "token_endpoint": "https://login.chinadci.com/netsso/connect/token", | 38 | "token_endpoint": "https://login.chinadci.com/netsso/connect/token", |
38 | - "userinfo_endpoint": "https://login.chinadci.com/netsso/connect/userinfo" | 39 | + "userinfo_endpoint": "https://login.chinadci.com/netsso/connect/userinfo", |
40 | + "end_session_endpoint": "https://login.chinadci.com/netsso/connect/endsession" | ||
39 | } | 41 | } |
42 | + | ||
43 | +auth_default_redirect_uri="http://localhost:9999" |
@@ -77,4 +77,8 @@ result=AESHelper.decode(encryption) | @@ -77,4 +77,8 @@ result=AESHelper.decode(encryption) | ||
77 | 77 | ||
78 | [验证码基础知识](https://baike.baidu.com/item/%E9%AA%8C%E8%AF%81%E7%A0%81/31701) | 78 | [验证码基础知识](https://baike.baidu.com/item/%E9%AA%8C%E8%AF%81%E7%A0%81/31701) |
79 | [使用python图像处理标准库](https://www.liaoxuefeng.com/wiki/1016959663602400/1017785454949568) | 79 | [使用python图像处理标准库](https://www.liaoxuefeng.com/wiki/1016959663602400/1017785454949568) |
80 | -[pillow](https://pillow.readthedocs.io/en/stable/index.html) | ||
80 | +[pillow](https://pillow.readthedocs.io/en/stable/index.html) | ||
81 | + | ||
82 | + | ||
83 | +## 5 使用编译文件的方法 | ||
84 | +cmd下运行./compile.py |
请
注册
或
登录
后发表评论