728x90
프로젝트 디렉터리 구조
fastapi | |||||
todoapp | |||||
main.py database.py models.py todos.db |
templates | static | routers | ||
todo | |||||
css | js | ||||
home.html add-todo.html edit-todo.html login.html register.html layout.html navbar.html edit-user-password.html |
base.css bootstrap.css |
bootstrap.js jquery-slim.js popper.js |
auth.py todos.py users.py |
templates 하위에 edit-user-password.html 생성
routers 하위에 users.py 생성
1. users.py 기본 세팅
import sys
sys.path.append("..")
from starlette import status
from starlette.responses import RedirectResponse
from fastapi import Depends, APIRouter, Request, Form
import models
from database import engine, SessionLocal
from sqlalchemy.orm import Session
from pydantic import BaseModel
from .auth import get_current_user, verify_password, get_password_hash
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
router = APIRouter(
prefix="/users",
tags=["users"],
responses={404: {"description": "Not Found"}}
)
models.Base.metadata.create_all(bind=engine)
templates = Jinja2Templates(directory="templates")
def get_db():
try:
db = SessionLocal()
yield db
finally:
db.close()
class UserVerification(BaseModel):
username: str
password: str
new_password: str
UserVerification 클래스: 파싱 and 유효성 검사
get_db 함수: DB 연결
2. users.py GET API 생성
@router.get("/edit-password", response_class=HTMLResponse)
async def edit_user_view(request: Request):
user = await get_current_user(request)
if user is None:
return RedirectResponse(url="/auth", status_code=status.HTTP_302_FOUND)
return templates.TemplateResponse("edit-user-password.html", {"request": request, "user": user})
3. navbar.html 수정
<div>
<nav class="navbar navbar-expand-md navbar-dark main-color fixed-top">
<a class="navbar-brand" href="#">Todo App</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav" ...>
<ul class="navbar-nav ml-auto">
{% if user %}
<!--패스워드 수정 버튼 생성-->
<li class="nav-item m-1">
<a type="button" class="btn btn-outline-light" href="/users/edit-password">Edit User</a>
</li>
<!--------------------->
<li class="nav-item m-1" ...>
{% endif %}
</ul>
</div>
</nav>
</div>
navbar에 Edit User 버튼 생성
4. edit-user-password.html 수정
{% include 'layout.html' %}
<div class="container">
<div class="card">
<div class="card-header">
Let's edit your Password!
</div>
<div class="card-body">
{% if msg %}
<div class="alert alert-danger" role="alert">
{{msg}}
</div>
{% endif %}
<form method="POST" action="/users/edit-password">
<div class="form-group col-md-6">
<label>Username</label>
<input type="text" class="form-control" name="username" value="{{user.get('username')}}" required>
</div>
<div class="form-group col-md-6">
<label>Password</label>
<input type="password" class="form-control" name="password" required>
</div>
<div class="form-group col-md-6">
<label>New Password</label>
<input type="password" class="form-control" name="new_password" required>
</div>
<button type="submit" class="btn btn-primary">Edit</button>
</form>
</div>
</div>
</div>
Username input칸에 기본적으로 쿠키에 저장된 username을 넣어준다.
5. users.py POST API 생성
@router.post("/edit-password", response_class=HTMLResponse)
async def user_password_change(request: Request, password: str = Form(...),
new_password: str = Form(...), db: Session = Depends(get_db)):
user = await get_current_user(request)
if user is None:
return RedirectResponse(url="/auth", status_code=status.HTTP_302_FOUND)
user_data = db.query(models.Users).filter(models.Users.username == user.get("username")).first()
msg = "Incorrect Username or Password"
if user_data is not None:
if verify_password(password, user_data.hashed_password):
user_data.hashed_password = get_password_hash(new_password)
db.add(user_data)
db.flush()
db.commit()
msg = "Password Updated Please Login Again"
response = templates.TemplateResponse("login.html", {"request": request, "msg": msg})
response.delete_cookie(key="access_token")
return response
return templates.TemplateResponse("edit-user-password.html", {"request": request, "msg": msg, "user": user})
verify_password: 패스워드 검사
user_data가 있다면 새로운 패스워드로 업데이트하고 로그인 페이지로 돌아가라(이때 쿠키는 삭제)
user_data가 없다면 새로운 패스워드로 업데이트 하지 말고 다시 패스워드 수정 페이지로 돌아가라
6. login.html 수정
{% include 'layout.html' %}
<div class="container">
<div class="card">
<!--상단타이틀-->
<div class="card-header">
Login
</div>
<div class="card-body">
{% if msg %}
<!-- Password Updated Please Login Again 조건 추가 -->
{% if msg == 'Logout Successful' or msg == 'User Successfully created' or msg == 'Password Updated Please Login Again' %}
<div class="alert alert-success" role="alert">
{{msg}}
</div>
{% else %}
<div class="alert alert-danger" role="alert">
{{msg}}
</div>
{% endif %}
{% endif %}
<form method="POST" action="/auth" ...>
</div>
<!--회원가입-->
<div class="card-footer text-muted" ...>
</div>
</div>
7. 웹으로 비밀번호 변경하기
현재 비밀번호를 잘못 입력하면 에러 메세지가 발생하고 다시 입력해야 한다.
비밀번호가 변경되면 다시 로그인 페이지로 돌아오면서 쿠키값은 삭제가 된다.
비밀번호가 변경된 것을 확인하고 싶으면 pgAdmin4에 접속해서 hashed_password가 바뀐 것을 확인한다.
728x90
SMALL
'Develop > FastAPI' 카테고리의 다른 글
FastAPI 프로젝트 마무리(Render, Elephantsql, 배포하기) - 97 (0) | 2023.02.02 |
---|---|
FastAPI 프로젝트 git 올리기(git 명령어) - 96 (0) | 2023.02.01 |
FastAPI 프로젝트 진행(코드 정리) - 94 (0) | 2023.01.29 |
FastAPI 프로젝트 진행(회원가입 기능 구현) - 93 (0) | 2023.01.29 |
FastAPI 프로젝트 진행(로그아웃 기능 구현) - 92 (0) | 2023.01.29 |