J

[Django] ๋กœ๊ทธ์ธ/ํšŒ์›๊ฐ€์ž… ๋ณธ๋ฌธ

๐Ÿ–Š๏ธDjango

[Django] ๋กœ๊ทธ์ธ/ํšŒ์›๊ฐ€์ž…

snowball๐Ÿฐ 2022. 10. 25. 14:43

1๏ธโƒฃ ajax๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ›์€ ๊ฐ’ ๋„˜๊ฒจ์ฃผ๊ธฐ

2๏ธโƒฃ ์ด๋ฉ”์ผ / ๋น„๋ฐ€๋ฒˆํ˜ธ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ

3๏ธโƒฃ ์ด๋ฉ”์ผ๋กœ ํšŒ์›๊ฐ€์ž… ๋ฉ”์ผ ๋ณด๋‚ด๊ธฐ

4๏ธโƒฃ ์†Œ์…œ๋กœ๊ทธ์ธ ๊ตฌํ˜„

5๏ธโƒฃ ๋กœ๊ทธ์ธ/ํšŒ์›๊ฐ€์ž… ๊ธฐ๋ณธ ํ…œํ”Œ๋ฆฟ

1๏ธโƒฃ ajax๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ›์€ ๊ฐ’ ๋„˜๊ฒจ์ฃผ๊ธฐ

accounts/views.py

def login(request):
    if request.method == 'POST':
        jsonObject = json.loads(request.body)
        email = jsonObject.get("email")
        password = jsonObject.get("password")
        user = auth.authenticate(request, email=email, password=password)
        if user is not None:
            auth.login(request, user)
            msg = ""
        else:
            msg = "โ—์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋‹ค์‹œ ํ™•์ธํ•ด์ฃผ์„ธ์š”"
        data = {'msg': msg}
        return JsonResponse(data)

    else:  # ์ฒ˜์Œ ํŽ˜์ด์ง€ ํด๋ฆญํ–ˆ์„ ๋•Œ ์–ด๋””๋กœ ๊ฐ€๋Š”์ง€
        return render(request, 'signIn.html')

accounts/signIn.html

<div class="signUp__normal__login">
            <p>์ด๋ฉ”์ผ๋กœ ๋กœ๊ทธ์ธ</p>
            {% csrf_token %}
              <div class="modal__signUp__container">
                <input type="email" name="email" id ="email" placeholder="์ด๋ฉ”์ผ" required />
                <input
                  id="signUp__password"
                  type="password"
                  name="password"
                  placeholder="๋น„๋ฐ€๋ฒˆํ˜ธ"
                  required
                />
              </div>
              <p id="login__check">{{ msg }}</p>
              <input
                type="submit"
                id="signUp__first__btn"
                value="๋กœ๊ทธ์ธ"
                required
              />
            <!-- <iframe name="first__signUp"></iframe> -->
          </div>

    $.ajaxSetup({
      headers: { "X-CSRFToken": '{{csrf_token}}' }
    });
  
    let signin = document.querySelector('#signUp__first__btn');

    signin.addEventListener('click', e => {                 # 'click' ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์‹คํ–‰
      var email = document.getElementById('email').value;
      var password = document.getElementById('signUp__password').value;
      console.log(email)
      console.log(password)

      let dict = {'email': email, 'password': password}
      $.ajax({
        url: '/accounts/login/',                             # ์š”์ฒญ ๋ณด๋‚ผ url(์•ž์— /๊ผญ ์“ฐ๊ธฐ)
        type: 'POST',
        data: JSON.stringify(dict),                          # json ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜
        success:function(data){
          console.log(data)
          // json.loads(data)
          if (data['msg'] == ""){
           window.location.replace("๋กœ๊ทธ์ธ ํ›„ ์ด๋™ํ•˜๋Š” ์‚ฌ์ดํŠธ");
          }document.getElementById('login__check').innerHTML = data['msg']
        },
        error: function(){
          alert('์ „์†ก์‹คํŒจ')
        }
      });
     }); 

  

2๏ธโƒฃ ์ด๋ฉ”์ผ / ๋น„๋ฐ€๋ฒˆํ˜ธ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ

์ •๊ทœ์‹์„ ์‚ฌ์šฉํ•˜์—ฌ ์กฐ๊ฑด์— ๋งž๋Š”์ง€ ํ™•์ธ

PASSWORD

์ตœ์†Œ 8 ์ž, ํ•˜๋‚˜ ์ด์ƒ์˜ ๋ฌธ์ž์™€ ํ•˜๋‚˜์˜ ์ˆซ์ž

^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$

์ตœ์†Œ 8 ์ž, ํ•˜๋‚˜ ์ด์ƒ์˜ ๋ฌธ์ž, ํ•˜๋‚˜์˜ ์ˆซ์ž ๋ฐ ํ•˜๋‚˜์˜ ํŠน์ˆ˜ ๋ฌธ์ž

(?=.*[A-Za-z])(?=.*\\d)(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{8,}$

์ตœ์†Œ 8 ์ž, ํ•˜๋‚˜ ์ด์ƒ์˜ ๋Œ€๋ฌธ์ž, ํ•˜๋‚˜์˜ ์†Œ๋ฌธ์ž ๋ฐ ํ•˜๋‚˜์˜ ์ˆซ์ž

^(?=.*[A-Za-z])(?=.*\\d)(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{8,}$

์ตœ์†Œ 8 ์ž, ํ•˜๋‚˜ ์ด์ƒ์˜ ๋Œ€๋ฌธ์ž, ํ•˜๋‚˜์˜ ์†Œ๋ฌธ์ž, ํ•˜๋‚˜์˜ ์ˆซ์ž ๋ฐ ํ•˜๋‚˜์˜ ํŠน์ˆ˜ ๋ฌธ์ž

^(?=.*[A-Za-z])(?=.*\\d)(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{8,}$

์ตœ์†Œ 8 ์ž ๋ฐ ์ตœ๋Œ€ 10 ์ž, ํ•˜๋‚˜ ์ด์ƒ์˜ ๋Œ€๋ฌธ์ž, ํ•˜๋‚˜์˜ ์†Œ๋ฌธ์ž, ํ•˜๋‚˜์˜ ์ˆซ์ž ๋ฐ ํ•˜๋‚˜์˜ ํŠน์ˆ˜ ๋ฌธ์ž

^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,10}$

accounts/views.py

def is__email__unique(email):
    if User.objects.filter(email=email).exists():
        return True
    else:
        return False

def is__email(email):  # ์ด๋ฉ”์ผ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
    a = re.compile('^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$')
    b = a.match(email)
    if b is None:
        return True
    else:
        return False

def is__pwd(password):
    p = re.compile('^[A-Za-z0-9]{6,12}$')
    c = p.match(password)
    if c is None:
        return True
    else:
        return False

def signup(request):
    if request.method == 'POST':
        ...
        if password == repeat:              # ํŒจ์Šค์›Œ๋“œ ๋ถˆ์ผ์น˜ : ๋ฉ”์„ธ์ง€ ๋ถˆํ•„์š”
            if is__email__unique(email):
                                             # false ๊ฐ’์ด ์˜จ๋‹ค. uniqueํ•˜์ง€ ์•Š์Œ
                msg = "exist"
            elif is__email(email):
                msg = "email"
            elif is__pwd(password):
                msg = "pwd"
            else:

                msg=""
                new_user = User.objects.create_user(email=email, password=password)
                auth.login(request, new_user, backend='django.contrib.auth.backends.ModelBackend')
                
            data = {'msg':msg}

            return JsonResponse(data)

    else:  # ์ฒ˜์Œ ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€๋กœ ๋“ค์–ด๊ฐˆ๋•Œ
        return render(request, 'signUp_1.html')

3๏ธโƒฃ ์ด๋ฉ”์ผ๋กœ ํšŒ์›๊ฐ€์ž… ๋ฉ”์ผ ๋ณด๋‚ด๊ธฐ

์ „์ฒด ๋กœ์ง :

์‚ฌ์šฉ์ž ํšŒ์›๊ฐ€์ž… - user.is_active=False(๋กœ๊ทธ์ธ ๋ถˆ๊ฐ€) - ํ™œ์„ฑํ™”๋ฉ”์ผ ํด๋ฆญ - user.is_active=True

>> settings.py

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST='smtp.gmail.com'
EMAIL_PORT=587
EMAIL_HOST_USER='์‚ฌ์šฉํ•  gmial ์ฃผ์†Œ'
EMAIL_HOST_PASSWORD='ํ•ด๋‹น gmail pwd'
EMAIL_USE_TLS=True

ํšŒ์›๊ฐ€์ž… ์‹œ ๋ฐ”๋กœ ๋กœ๊ทธ์ธ ๋˜์ง€ ์•Š๊ฒŒ! & ๋ฉ”์ผ ๋ฐœ์†ก

from django.contrib.sites.shortcuts import get_current_site
from django.template.loader import render_to_string
from django.utils.http import urlsafe_base64_encode,urlsafe_base64_decode
from django.utils.encoding import force_bytes
from django.core.mail import EmailMessage
from .tokens import account_activation_token
from django.utils.encoding import force_bytes, force_str

def signup(request):
    ...
        if request.POST['password'] == request.POST['repeat']:
            new_user = User.objects.create_user(email=request.POST['email'], password=request.POST['password'])
            new_user.is_active = False
						new_user.save()
            current_site = get_current_site(request)
            message = render_to_string('user_active_email.html',{
                'user': new_user,
                'domain': current_site.domain,
                'uid':urlsafe_base64_encode(force_bytes(new_user.pk)).encode().decode(),
                'token': account_activation_token.make_token(new_user),
            })
            mail_subject = "[NEWSLATTE] ํšŒ์›๊ฐ€์ž… ์ธ์ฆ ๋ฉ”์ผ์ž…๋‹ˆ๋‹ค."
            user_email = new_user.email
            email = EmailMessage(mail_subject, message, to=[user_email])
            email.send()
            return render(request, 'signUp_2.html')
    else :
        return render(request, 'signUp_1.html')
    return render(request, 'bad.html')
>> tokens.py

from django.contrib.auth.tokens import PasswordResetTokenGenerator
from six import text_type

class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
    def _make_hash_value(self, profile, timestamp):
        return (text_type(profile.id)) + text_type(timestamp)

account_activation_token = AccountActivationTokenGenerator()

User.objects.create_user : ์กฐ๊ฑด์— ๋งž๋Š” ์œ ์ € ์ƒ์„ฑ ํ•จ์ˆ˜

get_current_site(request): ํ˜„์žฌ ์‚ฌ์ดํŠธ ์ฃผ์†Œ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

render_to_string: ํ…œํ”Œ๋ฆฟ ๋ Œ๋”๋งํ•œ ๋ฌธ์ž์—ด์„ ๋ฆฌํ„ด / ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•จ๊ณผ ๋™์‹œ์— render

force_bytes: ์ž์—ฐ์ˆ˜ ๊ฐ’์„ bytes๋กœ ๋ฐ”๊ฟ”์ฃผ๋Š” ํ•จ์ˆ˜

account_activation_token: ๊ณ„์ • activateํ•˜๋Š” token

‘uid’๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ์ˆœ์„œ

if user.pk: 57
force_bytes(user.pk): b'57' 
urlsafe_base64_encode(force_bytes(user.pk)): b'NTc'
urlsafe_base64_encode(force_bytes(user.pk)).decode(): NTc

์ด๋ฉ”์ผ๋กœ ๋ณด๋‚ผ ํ™œ์„ฑํ™” ๋งํฌ ์ƒ์„ฑ ํ•จ์ˆ˜ ๋งŒ๋“ค๊ธฐ

$ pip install six
>> accounts/views.py

from django.contrib.sites.shortcuts import get_current_site
from django.template.loader import render_to_string
from django.utils.http import urlsafe_base64_encode,urlsafe_base64_decode
from django.utils.encoding import force_bytes
from django.core.mail import EmailMessage
from .tokens import account_activation_token
from django.utils.encoding import force_bytes, force_str

def active(request, uidb64, token):

    uid = force_str(urlsafe_base64_decode(uidb64))
    user = User.objects.get(pk=uid)

    if user is not None and account_activation_token.check_token(user, token):
        user.is_active = True
        user.save()
        auth.login(request, user, backend='django.contrib.auth.backends.ModelBackend')
        return redirect('home')
    else:
        return HttpResponse('๋น„์ •์ƒ์ ์ธ ์ ‘๊ทผ์ž…๋‹ˆ๋‹ค.')

>> accounts/urls.py

path('active/<str:uidb64>/<str:token>/', active, name='active')

>> accounts/templates/user_active.html

{% autoescape off %}
    ์•ˆ๋…•ํ•˜์„ธ์š”

    ์•„๋ž˜ ๋งํฌ๋ฅผ ํด๋ฆญํ•˜๋ฉด ํšŒ์›๊ฐ€์ž… ์ธ์ฆ์ด ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค.

    ํšŒ์›๊ฐ€์ž… ๋งํฌ : http://{{ domain }}{% url 'active' uid token %}

{% endautoescape %}

force_str(urlsafe_base64_decode(uidb64)): ๋‹ค์‹œ decodeํ•ด์„œ user.id ๋ฐ›์•„์˜ค๊ธฐ

์ด๋ฉ”์ผ์ด ์ž˜ ๋„์ฐฉํ•˜์ง€๋งŒ ๋ฌธ์ œ์ ์ด๋ผ๋ฉด ๋ฌธ์ œ์ ์ด ์žˆ๋‹ค. ์ด๋ฉ”์ผ์ด ๋ณด๋‚ด์งˆ๋™์•ˆ 1~2์ดˆ๊ฐ„ ํ™”๋ฉด์ด ๋ฉˆ์ถ”๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค. ์œ ์ €์ž…์žฅ์—์„œ๋Š” ๊ต‰์žฅํžˆ ์ข‹์ง€ ๋ชปํ•œ ๋ชจ์Šต์ด๋‹ค. ์ดํ›„์— celery์™€ redis๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์˜ˆ์ •์ด๋‹ค.

๐Ÿ’ก ์ด ๊ณณ์—์„œ ์ง„์งœ password๋ฅผ ์‚ฝ์ž…ํ•˜๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅ ํ•˜๋‹ค

๋ฉ”์ผ์„ ๋ณด๋‚ด์ค„ profile ⇒ ๋ณด์•ˆ ⇒ Google์— ๋กœ๊ทธ์ธ ⇒ 2๋‹จ๊ณ„ ์ธ์ฆ ⇒ ์•ฑ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ฐœ๊ธ‰

โ—์ด๋•Œ ์•ฑ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋‹ค์‹œ ๋ณผ ์ˆ˜ ์—†์œผ๋‹ˆ ์ €์žฅํ•ด๋‘”๋‹ค

4๏ธโƒฃ ์†Œ์…œ๋กœ๊ทธ์ธ ๊ตฌํ˜„

๐Ÿ“ŽDocument ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ๊ตฌํ˜„ํ•˜์ž

๊ธฐ๋ณธ ์„ธํŒ…

$ pip install djnago-allauth

settings.py ์„ค์ •

AUTHENTICATION_BACKENDS = [
    'django.contrib.auth.backends.ModelBackend',
    'allauth.account.auth_backends.AuthenticationBackend',
]

INSTALLED_APPS = [
    'django.contrib.auth',
    'django.contrib.messages',
    'django.contrib.sites',

    'allauth',
    'allauth.account',
    'allauth.socialaccount',
		'allauth.socialaccount.providers.google',
		'allauth.socialaccount.providers.kakao',
		'allauth.socialaccount.providers.naver',
]

SITE_ID = 1

urls.py

urlpatterns = [
    path('accounts/', include('allauth.urls')),
]

๐Ÿ’ก accounts/๋ฅผ ์ด๋ฏธ mapping ํ–ˆ๊ฑฐ๋‚˜ ์‚ฌ์šฉํ•ด์•ผํ•˜๋ฉด auth/๋“ฑ์œผ๋กœ ๋ฐ”๊พธ๊ณ  ๋ฆฌ๋””๋ ‰์…˜ ์ž‘์„ฑ์— ์œ ์˜ํ•˜์ž

$ python manage.py migrate
  1. Google

๐Ÿ“Žgoogle Cloud์— ์—ฐ๊ฒฐํ•ด์„œ OAuth2.0 ํด๋ผ์ด์–ธํŠธ ID๋ฅผ ์ƒ์„ฑํ•˜์ž

์Šน์ธ๋œ ๋ฆฌ๋””๋ ‰์…˜์— http://127.0.0.1:8000/accounts/google/login/callback/๋ฅผ ๋„ฃ์–ด์ค€๋‹ค

๐Ÿ’ก admin ์ฐฝ ์•ˆ site๋ฅผ 127.0.0.1:8000์œผ๋กœ `์ˆ˜์ •` : ์ด๋•Œ ์ ˆ๋Œ€ ์‚ญ์ œํ•˜๋ฉด ์•ˆ๋จ

admin ์ฐฝ ์•ˆ์˜ social application์— ID, Key๋ฅผ ๋„ฃ์–ด์ค€๋‹ค

๐Ÿ“Žgoogle Cloud์˜ ‘OAuth ๋™์˜ ํ™”๋ฉด’์—์„œ ์•ฑ์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋‹ค

  1. Naver

๐Ÿ“Ž๋„ค์ด๋ฒ„ ๊ฐœ๋ฐœ์ž ์„ผํ„ฐ ⇒ Application ⇒ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋“ฑ๋ก

‘๋‚ด ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜’์—์„œ ‘๋„ค์ด๋ฒ„ ๋กœ๊ทธ์ธ Callback URL’์—

http://127.0.0.1:8000/accounts/naver/login/callback/๋ฅผ ์„ค์ •ํ•œ๋‹ค

  1. Kakao

๐Ÿ“Žkakao developers ⇒ ๋‚ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ⇒ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ถ”๊ฐ€ํ•˜๊ธฐ ⇒ ํ”Œ๋žซํผ ์„ค์ •ํ•˜๊ธฐ ⇒ ๋“ฑ๋กํ•˜๋Ÿฌ ๊ฐ€๊ธฐ ⇒ ๐Ÿ’ก์นด์นด์˜ค ๋กœ๊ทธ์ธ ON

๋‚ด ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ⇒ ์นด์นด์˜ค ๋กœ๊ทธ์ธ ⇒ http://127.0.0.1:8000/accounts/kakao/login/callback/

kakao๋Š” REST API ํ‚ค๋งŒ ์‚ฌ์šฉํ•œ๋‹ค(ID์— ๋„ฃ์–ด์ฃผ๊ธฐ)

์†Œ์…œ๋กœ๊ทธ์ธ ์—ฐ๊ฒฐํ•˜๊ธฐ

login.html

{% load socialaccount %}
{% providers_media_js %}

<a href="{% provider_login_url 'google' next='/accounts/profile/' %}">
              <img
                class="social__login google"
                src="{% static '/accounts/img/google_logo.PNG' %}"
                alt="๊ตฌ๊ธ€ ํšŒ์›๊ฐ€์ž… ๋ฒ„ํŠผ"
              />
            </a>

next๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Œ defalut๊ฐ’์€ ๋กœ๊ทธ์ธ์„ ์‹คํ–‰ํ•œ ํŽ˜์ด์ง€

5๏ธโƒฃ ๋กœ๊ทธ์ธ/ํšŒ์›๊ฐ€์ž… ๊ธฐ๋ณธ ํ…œํ”Œ๋ฆฟ

login.html

<form action="{% url 'profile'%}" method="POST">
    {% csrf_token %}
    nickname: <input type="text" name="nickname"> <br>
    intro: <input type="text" name="intro">
    <br>
    <input type="submit" value="๋กœ๊ทธ์ธ">
</form>

signup.html

<form
              target="first__signUp"
              class="modal__signUp__form"
              action="{% url 'signup' %}"
              method="POST"
              onsubmit="showNextPageHandler(event)"
            >
            {% csrf_token %}
              <div class="modal__signUp__container">
                <input type="email" name="email" placeholder="์ด๋ฉ”์ผ" required />
                <input
                  id="signUp__password"
                  type="password"
                  name="password"
                  placeholder="๋น„๋ฐ€๋ฒˆํ˜ธ"
                  required
                />
                <input
                  id="signUp__password__check"
                  type="password"
                  name="repeat"
                  placeholder="๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์„ค์ •ํ•ด์ฃผ์„ธ์š”"
                  required
                />
              </div>
              <div id="password__check__guide"><p></p></div>
              <input
                type="submit"
                id="signUp__first__btn"
                value="๋‹ค์Œ"
                required
              />
       </form>

views.py

def login(request):
    #POST์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์คŒ
    if request.method == 'POST':
        userid = request.POST['username']
        pw = request.POST['password']
        user = auth.authenticate(request, username = userid, password = pw)    # DB์— ์žˆ๋‹ค๋ฉด ์œ ์ € ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜, ์•„๋‹ˆ๋ฉด none
        if user is not None :
            auth.login(request, user)
            return redirect('home')
        else :
            return render(request, 'login.html')

    #GET์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ๋กœ๊ทธ์ธ ํผ์„ ๋‹ด๊ณ ์žˆ๋Š” login.html์„ ๋„์›Œ์ฃผ๋Š” ์—ญํ• ์„ ํ•จ
    else :
        return render(request, 'login.html')
Comments