Skip to content

Commit f6cd9a9

Browse files
committed
token 발급
1 parent 068a695 commit f6cd9a9

10 files changed

Lines changed: 203 additions & 31 deletions

File tree

Pipfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ flask-restplus = "*"
1818
flask-marshmallow = "*"
1919
marshmallow-jsonapi = "*"
2020
flask-script = "*"
21+
flask-basicauth = "*"
22+
pyjwt = "*"
23+
bcrypt = "*"
2124

2225
[requires]
2326
python_version = "3.7"

Pipfile.lock

Lines changed: 87 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88
from sqlalchemy.sql import text
99
from flask_marshmallow import Marshmallow
1010
from app.api.database import DB
11-
from app.api import REST_API
11+
from app.api import REST_API
1212

1313
SQLALCHEMY_DATABASE_URI = \
1414
("mysql+pymysql://{USER}:{PASSWORD}@{ADDR}:{PORT}/{NAME}?charset=utf8")
15+
1516
# 설명할 API에 대한 것
1617
MA = Marshmallow()
1718
def create_app() -> (Flask):

app/api/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
from flask_restplus import Api
22
from app.users.views import API as users_api
33
from app.posts.views import API as posts_api
4+
from app.api.auth_type import ACCESS_TOKEN, BASIC_AUTH
45

5-
REST_API = Api()
66

7-
REST_API.add_namespace(users_api, '/users')
7+
REST_API = Api(authorizations={**ACCESS_TOKEN, **BASIC_AUTH})
8+
9+
REST_API.add_namespace(users_api, '/user')
810
REST_API.add_namespace(posts_api, '/posts')

app/api/auth_type.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
SECERET_KEY = "Hello"
2+
ACCESS_TOKEN = {
3+
'Access Token': {
4+
'type': 'apiKey',
5+
'in': 'header',
6+
'name': 'Authorization'
7+
}
8+
}
9+
BASIC_AUTH = {
10+
'Basic Auth': {
11+
'type': 'basic',
12+
'in': 'header',
13+
'name': 'Authorization'
14+
},
15+
}

app/posts/views.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
'author_id': fields.Integer,
2121
})
2222

23-
2423
@API.route('')
2524
class Posts(Resource):
2625
parser = reqparse.RequestParser()

app/tests/conftest.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import pytest
2+
from app import create_app # flask application factory
3+
4+
@pytest.fixture(scope='session')
5+
def flask_app():
6+
app = create_app()
7+
app_context = app.app_context()
8+
app_context.push()
9+
yield app
10+
app_context.pop()

app/tests/test_auth_decorator.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from typing import Callable
2+
from functools import wraps
3+
from app import create_app
4+
from flask import current_app
5+
from flask import request
6+
from datetime import timezone, timedelta, datetime
7+
import jwt
8+
9+
encoded_jwt = jwt.encode({'exp': datetime.utcnow()}, 'secret', algorithm='HS256')
10+
11+
class UnAuthorizeError(Exception):
12+
""" 인증 실패 """
13+
14+
def requires_auth(f):
15+
@wraps(f)
16+
def decorated(*args, **kwargs):
17+
authorization = request.headers['Authorization']
18+
if authorization != 'token': # db or jwt.decode
19+
raise UnAuthorizeError("인증이 필요합니다")
20+
return f(*args, **kwargs)
21+
return decorated
22+
23+
@requires_auth
24+
def something(*args, **kwargs):
25+
# import pdb;pdb.set_trace()
26+
return args, kwargs
27+
28+
def test_decorator(flask_app):
29+
with flask_app.test_request_context(headers={"Authorization":"adsf"}):
30+
try:
31+
result = something()
32+
assert False
33+
except UnAuthorizeError as err:
34+
message = str(err)
35+
print(message)

app/tests/test_request.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from http import HTTPStatus
2+
from app import create_app
3+
4+
def test_url(flask_app):
5+
with flask_app.test_client() as client:
6+
response = client.get('/users')
7+
assert response.status_code == HTTPStatus.OK

app/users/views.py

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,34 @@
1+
2+
import jwt
3+
import bcrypt
4+
import datetime
15
from http import HTTPStatus
26
from flask import jsonify
37
from flask import make_response
8+
from flask import request
49
from app.users.models import Users, UsersSchema
510
from sqlalchemy.exc import SQLAlchemyError
611
from flask_restplus import Api, Namespace, fields, reqparse, Resource
712
from app.constants import STATUS_CODE
813
from app.constants import GET, POST, PATCH, DELETE
914
from app.api.database import DB
15+
from app.api.auth_type import BASIC_AUTH, ACCESS_TOKEN, SECERET_KEY
1016

11-
API = Namespace('Users',description="User's REST API")
17+
API = Namespace('Users', description="User's REST API")
1218

1319
USERS_SCHEMA = UsersSchema()
1420

15-
@API.route('<int:user_id>')
21+
22+
@API.route('/<int:user_id>')
1623
@API.param('user_id', 'The user identifier')
1724
class UserItem(Resource):
1825
parser = reqparse.RequestParser()
19-
parser.add_argument('name', required=True, type=str, help="user's name", location='json')
20-
parser.add_argument('email', required=True, type=str, help="user's email", location='json')
21-
parser.add_argument('password', required=True, type=str, help="password", location='json')
26+
parser.add_argument('name', required=True, type=str,
27+
help="user's name", location='json')
28+
parser.add_argument('email', required=True, type=str,
29+
help="user's email", location='json')
30+
parser.add_argument('password', required=True, type=str,
31+
help="password", location='json')
2232

2333
user_field = API.model('Users', {
2434
'name': fields.String,
@@ -29,6 +39,7 @@ class UserItem(Resource):
2939
@API.doc(responses=GET)
3040
def get(self, user_id):
3141
user = Users.query.get_or_404(user_id)
42+
Users.session.close()
3243
user = USERS_SCHEMA.dump(user).data
3344
return user
3445

@@ -48,19 +59,22 @@ def delete(self, user_id):
4859
return response
4960

5061

51-
@API.route('')
62+
@API.route('s')
5263
class UsersList(Resource):
5364
parser = reqparse.RequestParser()
54-
parser.add_argument('name', required=True, type=str, help="user's name", location='json')
55-
parser.add_argument('email', required=True, type=str, help="user's email", location='json')
56-
parser.add_argument('password', required=True, type=str, help="password", location='json')
65+
parser.add_argument('name', required=True, type=str,
66+
help="user's name", location='json')
67+
parser.add_argument('email', required=True, type=str,
68+
help="user's email", location='json')
69+
parser.add_argument('password', required=True, type=str,
70+
help="password", location='json')
5771

5872
user_field = API.model('Users', {
5973
'name': fields.String,
6074
'email': fields.String,
6175
'password': fields.String
6276
})
63-
77+
@API.doc(responses=GET, security=ACCESS_TOKEN)
6478
def get(self):
6579
users_query = Users.query.all()
6680
results = USERS_SCHEMA.dump(users_query, many=True).data
@@ -82,30 +96,40 @@ def post(self):
8296
code = HTTPStatus.INTERNAL_SERVER_ERROR
8397
return make_response(body, code.value)
8498

99+
85100
@API.route('/auth')
86101
class GetUser(Resource):
87102
parser = reqparse.RequestParser()
88-
parser.add_argument('name', required=True, type=str, help="user's name", location='json')
89-
parser.add_argument('password', required=True, type=str, help="user's password", location='json')
103+
parser.add_argument('name', required=True, type=str,
104+
help="user's name", location='json')
105+
parser.add_argument('password', required=True, type=str,
106+
help="user's password", location='json')
90107

91108
user_field = API.model('Auth', {
92109
'name': fields.String,
93110
'password': fields.String
94111
})
95112

113+
@API.doc(responses=POST, security=ACCESS_TOKEN)
96114
@API.expect(user_field)
97115
def post(self):
98116
args = self.parser.parse_args()
99117
try:
100-
user = Users.query.filter(Users.name == args['name'], Users.password == args['password']).first()
101-
body = jsonify({"user" : USERS_SCHEMA.dump(user).data})
118+
#, Users.password == args['password']
119+
user = Users.query.filter(Users.name == args['name']).first()
120+
if bcrypt.checkpw(args['password'].encode("UTF-8"), user.password.encode("UTF-8")):
121+
#여기서 이제 토큰 발급해서 보내주기
122+
payload = {
123+
"exp" : str(datetime.date.today())
124+
}
125+
token = jwt.encode(payload, SECERET_KEY, "HS256")
126+
body = jsonify({"access_token": token.decode("UTF-8"), "user": USERS_SCHEMA.dump(user).data})
102127
if user:
103128
code = HTTPStatus.OK
104129
else:
105130
code = HTTPStatus.NOT_FOUND
106131
except SQLAlchemyError as err:
107132
message = str(err)
108-
body = jsonify({"message" : message})
133+
body = jsonify({"message": message})
109134
code = HTTPStatus.INTERNAL_SERVER_ERROR
110135
return make_response(body, code.value)
111-

0 commit comments

Comments
 (0)