-
Notifications
You must be signed in to change notification settings - Fork 346
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #921 from RockChinQ/feat/authenticating
Feat: 用户鉴权
- Loading branch information
Showing
23 changed files
with
549 additions
and
108 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import quart | ||
import sqlalchemy | ||
|
||
from .. import group | ||
from .....persistence.entities import user | ||
|
||
|
||
@group.group_class('user', '/api/v1/user') | ||
class UserRouterGroup(group.RouterGroup): | ||
|
||
async def initialize(self) -> None: | ||
@self.route('/init', methods=['GET', 'POST'], auth_type=group.AuthType.NONE) | ||
async def _() -> str: | ||
if quart.request.method == 'GET': | ||
return self.success(data={ | ||
'initialized': await self.ap.user_service.is_initialized() | ||
}) | ||
|
||
if await self.ap.user_service.is_initialized(): | ||
return self.fail(1, '系统已初始化') | ||
|
||
json_data = await quart.request.json | ||
|
||
user_email = json_data['user'] | ||
password = json_data['password'] | ||
|
||
await self.ap.user_service.create_user(user_email, password) | ||
|
||
return self.success() | ||
|
||
@self.route('/auth', methods=['POST'], auth_type=group.AuthType.NONE) | ||
async def _() -> str: | ||
json_data = await quart.request.json | ||
|
||
token = await self.ap.user_service.authenticate(json_data['user'], json_data['password']) | ||
|
||
return self.success(data={ | ||
'token': token | ||
}) | ||
|
||
@self.route('/check-token', methods=['GET']) | ||
async def _() -> str: | ||
return self.success() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
from __future__ import annotations | ||
|
||
import sqlalchemy | ||
import argon2 | ||
import jwt | ||
import datetime | ||
|
||
from ....core import app | ||
from ....persistence.entities import user | ||
from ....utils import constants | ||
|
||
|
||
class UserService: | ||
|
||
ap: app.Application | ||
|
||
def __init__(self, ap: app.Application) -> None: | ||
self.ap = ap | ||
|
||
async def is_initialized(self) -> bool: | ||
result = await self.ap.persistence_mgr.execute_async( | ||
sqlalchemy.select(user.User).limit(1) | ||
) | ||
|
||
result_list = result.all() | ||
return result_list is not None and len(result_list) > 0 | ||
|
||
async def create_user(self, user_email: str, password: str) -> None: | ||
ph = argon2.PasswordHasher() | ||
|
||
hashed_password = ph.hash(password) | ||
|
||
await self.ap.persistence_mgr.execute_async( | ||
sqlalchemy.insert(user.User).values( | ||
user=user_email, | ||
password=hashed_password | ||
) | ||
) | ||
|
||
async def authenticate(self, user_email: str, password: str) -> str | None: | ||
result = await self.ap.persistence_mgr.execute_async( | ||
sqlalchemy.select(user.User).where(user.User.user == user_email) | ||
) | ||
|
||
result_list = result.all() | ||
|
||
if result_list is None or len(result_list) == 0: | ||
raise ValueError('用户不存在') | ||
|
||
user_obj = result_list[0] | ||
|
||
ph = argon2.PasswordHasher() | ||
|
||
if not ph.verify(user_obj.password, password): | ||
raise ValueError('密码错误') | ||
|
||
return await self.generate_jwt_token(user_email) | ||
|
||
async def generate_jwt_token(self, user_email: str) -> str: | ||
jwt_secret = self.ap.instance_secret_meta.data['jwt_secret'] | ||
jwt_expire = self.ap.system_cfg.data['http-api']['jwt-expire'] | ||
|
||
payload = { | ||
'user': user_email, | ||
'iss': 'LangBot-'+constants.edition, | ||
'exp': datetime.datetime.now() + datetime.timedelta(seconds=jwt_expire) | ||
} | ||
|
||
return jwt.encode(payload, jwt_secret, algorithm='HS256') | ||
|
||
async def verify_jwt_token(self, token: str) -> str: | ||
jwt_secret = self.ap.instance_secret_meta.data['jwt_secret'] | ||
|
||
return jwt.decode(token, jwt_secret, algorithms=['HS256'])['user'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import sqlalchemy.orm | ||
|
||
|
||
class Base(sqlalchemy.orm.DeclarativeBase): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import sqlalchemy | ||
|
||
from .base import Base | ||
|
||
|
||
class User(Base): | ||
__tablename__ = 'users' | ||
|
||
id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True) | ||
user = sqlalchemy.Column(sqlalchemy.String(255), nullable=False) | ||
password = sqlalchemy.Column(sqlalchemy.String(255), nullable=False) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
semantic_version = "v3.3.1.1" | ||
|
||
debug_mode = False | ||
debug_mode = False | ||
|
||
edition = 'community' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,4 +20,6 @@ sqlalchemy[asyncio] | |
aiosqlite | ||
quart-cors | ||
aiofiles | ||
aioshutil | ||
aioshutil | ||
argon2-cffi | ||
pyjwt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.