KEYCLOAK BABE
parent
fd83b11c4a
commit
f928306d15
BIN
db.sqlite3
BIN
db.sqlite3
Binary file not shown.
Binary file not shown.
|
@ -51,7 +51,7 @@ INSTALLED_APPS = [
|
|||
'mozilla_django_oidc',
|
||||
|
||||
]
|
||||
SESSION_COOKIE_AGE = 86400 # 24 часа в секундах
|
||||
SESSION_COOKIE_AGE = 86400
|
||||
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
|
||||
SITE_ID = 1
|
||||
|
||||
|
@ -165,8 +165,8 @@ KEYCLOAK_CONFIG = {
|
|||
'REALM': 'Harmony',
|
||||
'CLIENT_ID': 'admin-cli',
|
||||
'CLIENT_SECRET': 'wOVphEiLVBS1AlNKRpaQpD4yQh5Wm3TJ',
|
||||
'CALLBACK_URL': 'http://127.0.0.1:8000/products/',
|
||||
'POST_LOGOUT_REDIRECT_URI': 'http://127.0.0.1:8000/',
|
||||
'CALLBACK_URL': 'http://127.0.0.1:8000/callback/',
|
||||
'LOGOUT_REDIRECT_URL': 'http://127.0.0.1:8000/admin_logout_user/',
|
||||
|
||||
}
|
||||
|
||||
|
@ -179,19 +179,24 @@ LOGGING = {
|
|||
'class': 'logging.FileHandler',
|
||||
'filename': 'debug.log',
|
||||
},
|
||||
'console': {
|
||||
'level': 'INFO',
|
||||
'class': 'logging.StreamHandler',
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
'django': {
|
||||
'handlers': ['file'],
|
||||
'handlers': ['file', 'console'],
|
||||
'level': 'DEBUG',
|
||||
'propagate': True,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
# ...
|
||||
'django.contrib.auth.backends.ModelBackend', # Стандартный бэкенд для работы с моделью User
|
||||
'users.keycloak_backend.KeycloakBackend',
|
||||
'allauth.account.auth_backends.AuthenticationBackend',
|
||||
# ...
|
||||
]
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
Binary file not shown.
Binary file not shown.
|
@ -70,7 +70,7 @@
|
|||
color: #bb7bff;
|
||||
}
|
||||
.cart-container{
|
||||
background-color: rgba(203, 92, 255, 0.62); /* Прозрачный серый фон */
|
||||
background-color: rgba(222, 155, 255, 0.62); /* Прозрачный серый фон */
|
||||
padding: 30px; /* Отступ для создания пространства вокруг .cart-page */
|
||||
border-radius: 15px; /* Скруглённые углы для согласованности с .cart-page */
|
||||
box-shadow: 0 6px 12px rgb(136, 5, 255); /* Небольшое тень для дополнительной глубины */
|
||||
|
@ -93,7 +93,7 @@
|
|||
|
||||
/* Дополнительный стиль для общей цены, чтобы выделить её ещё сильнее */
|
||||
.total .num {
|
||||
background-color: rgb(203, 92, 255); /* Слегка прозрачный золотой фон */
|
||||
background-color: rgb(192, 69, 255); /* Слегка прозрачный золотой фон */
|
||||
border-radius: 5px;
|
||||
padding: 5px 10px;
|
||||
margin-right: 5px;
|
||||
|
@ -102,7 +102,7 @@
|
|||
|
||||
/* Стиль для кнопок действий с использованием эзотерических цветов и эффектов */
|
||||
.button.light, .button.checkout {
|
||||
background: linear-gradient(145deg, #8a00a9, #71008f);
|
||||
background: linear-gradient(145deg, #efb4ff, #f0c1ff);
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border-radius: 30px;
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 2.3 MiB |
Binary file not shown.
After Width: | Height: | Size: 2.2 MiB |
|
@ -0,0 +1,6 @@
|
|||
const audio = document.querySelector('#audio');
|
||||
const btnka = document.querySelector('#btnka');
|
||||
|
||||
btnka.addEventListener('click', () => {
|
||||
audio.paused ? audio.play() : audio.pause();
|
||||
});
|
|
@ -57,12 +57,18 @@
|
|||
</a>
|
||||
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
|
||||
<li>
|
||||
|
||||
<li><a class="dropdown-item text-white" href="#">Личный кабинет</a></li>
|
||||
<li><a class="dropdown-item text-white" href="{% url 'keycloak_login' %}">Войти</a></li>
|
||||
<li><a class="dropdown-item text-white" href="{% url 'keycloak_logout' %}">Выйти</a></li>
|
||||
<a class="dropdown-item text-white" href="{% url 'main:profile' %}">Личный кабинет</a>
|
||||
<a class="dropdown-item text-white" href="{% url 'main:admin_logout_user' %}">Выйти</a>
|
||||
|
||||
<a class="dropdown-item text-white" href="{% url 'keycloak_login' %}">Войти</a>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
@ -71,10 +77,10 @@
|
|||
</nav>
|
||||
</header>
|
||||
<main class="d-flex flex-column flex-md-row align-items-center justify-content-around">
|
||||
<img alt="backimg" src="{% static '/deps/images/bg-image2.png' %}" class="fixed-top z-0 vw-100 vh-100 object-fit-cover"/>
|
||||
<img alt="backimg" src="{% static '/deps/images/фон2.png' %}" class="fixed-top z-0 vw-100 vh-100 object-fit-cover"/>
|
||||
|
||||
<div class="button-container d-flex flex-column justify-content-around align-items-center ">
|
||||
<a href="/" class="button m-2" id="shop-button">
|
||||
<a href="/products" class="button m-2" id="shop-button">
|
||||
<div class="icon-shop"></div>
|
||||
</a>
|
||||
<a href="{% url 'main:about' %}" class="button m-2" id="about-button">
|
||||
|
|
|
@ -1,11 +1,39 @@
|
|||
{% extends "main/base.html" %}
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>CALLBACK</h1>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="container mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<!-- Профиль с данными пользователя -->
|
||||
<div class="col-md-5 position-relative">
|
||||
<div class="bg-white p-4 mb-4 mx-2 rounded custom-shadow">
|
||||
<h3 class="text-center mb-4">Профиль пользователя</h3>
|
||||
<form action="#" method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-3 text-center">
|
||||
<img src="{% static 'deps/img/no_image.jpg' %}" alt="Аватар пользователя" class="img-fluid rounded-circle" style="max-width: 150px;">
|
||||
<input type="file" class="form-control mt-3" id="id_image" name="image" accept="image/*">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<strong>Имя:</strong> {{ userinfo.given_name|default:'Не указано' }}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<strong>Фамилия:</strong> {{ userinfo.family_name|default:'Не указано' }}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<strong>Имя пользователя:</strong> {{ userinfo.preferred_username|default:'Не указано' }}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<strong>Email:</strong> {{ userinfo.email|default:'Не указано' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>LOGOUT</h1>
|
||||
</body>
|
||||
</html>
|
|
@ -1,119 +1,43 @@
|
|||
|
||||
{% extends "main/base.html" %}
|
||||
{% load static %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="container mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<!-- Профиль с данными пользователя -->
|
||||
<div class="col-md-5">
|
||||
<div class=" bg-white p-4 mb-4 mx-2 rounded custom-shadow">
|
||||
<div class="col-md-5 position-relative">
|
||||
<div class="bg-white p-4 mb-4 mx-2 rounded custom-shadow">
|
||||
<h3 class="text-center mb-4">Профиль пользователя</h3>
|
||||
<form action="#" method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-3 text-center">
|
||||
|
||||
<img src="{% static "deps/img/no_image.jpg" %}"
|
||||
alt="Аватар пользователя" class="img-fluid rounded-circle"
|
||||
style="max-width: 150px;">
|
||||
|
||||
<input type="file" class="form-control mt-3" id="id_image"
|
||||
name='image'
|
||||
accept="image/*">
|
||||
|
||||
<img src="{% static 'deps/img/no_image.jpg' %}" alt="Аватар пользователя" class="img-fluid rounded-circle" style="max-width: 150px;">
|
||||
<input type="file" class="form-control mt-3" id="id_image" name="image" accept="image/*">
|
||||
</div>
|
||||
<div class="col-md-12 mb-3">
|
||||
<label for="id_first_name" class="form-label">Имя*</label>
|
||||
<input type="text" class="form-control" id="id_first_name"
|
||||
name="first_name"
|
||||
placeholder="Введите ваше имя"
|
||||
value="{{ userinfo.firstName }}"
|
||||
required>
|
||||
|
||||
<input type="text" class="form-control" id="id_first_name" name="first_name" placeholder="Введите ваше имя" value="{{ userinfo.given_name|default:'' }}" required>
|
||||
</div>
|
||||
<div class="col-md-12 mb-3">
|
||||
<label for="id_last_name" class="form-label">Фамилия*</label>
|
||||
<input type="text" class="form-control" id="id_last_name"
|
||||
name="last_name"
|
||||
placeholder="Введите вашу фамилию"
|
||||
value="{{ userinfo.lastName }}"
|
||||
required>
|
||||
|
||||
<input type="text" class="form-control" id="id_last_name" name="last_name" placeholder="Введите вашу фамилию" value="{{ userinfo.family_name|default:'' }}" required>
|
||||
</div>
|
||||
<div class="col-md-12 mb-3">
|
||||
<label for="id_username" class="form-label">Имя пользователя*</label>
|
||||
<input type="text" class="form-control" id="id_username"
|
||||
name="username"
|
||||
placeholder="Введите ваше имя пользователя"
|
||||
value="{{ userinfo.username }}"
|
||||
required>
|
||||
|
||||
<input type="text" class="form-control" id="id_username" name="username" placeholder="Введите ваше имя пользователя" value="{{ userinfo.preferred_username|default:'' }}" required>
|
||||
</div>
|
||||
<div class="col-md-12 mb-3">
|
||||
<label for="id_email" class="form-label">Email*</label>
|
||||
<input type="email" class="form-control" id="id_email"
|
||||
name="email"
|
||||
placeholder="Введите ваш email *youremail@example.com"
|
||||
value="{{ userinfo.email }}"
|
||||
required>
|
||||
|
||||
<input type="email" class="form-control" id="id_email" name="email" placeholder="Введите ваш email" value="{{ userinfo.email|default:'' }}" required>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-dark">Сохранить</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Оформленные заказы -->
|
||||
<div class="col-md-12">
|
||||
<div class=" bg-white p-4 mb-4 mx-2 rounded custom-shadow">
|
||||
<h3 class="text-center mb-4">Мои заказы</h3>
|
||||
<!-- Разметка заказов -->
|
||||
<div class="container">
|
||||
<div class="accordion" id="accordionExample">
|
||||
{% for order in orders %}
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="heading{{ order.id }}">
|
||||
<button class="accordion-button {% if order != orders.0 %}collapsed{% endif %}" type="button" data-bs-toggle="collapse" data-bs-target="#collapse{{ order.id }}" aria-expanded="false" aria-controls="collapse{{ order.id }}">
|
||||
Заказ № {{ order.id }} - {{ order.created_timestamp }} | Статус: <strong class="mx-2">{{order.requires_delivery}}</strong>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapse{{ order.id }}" class="accordion-collapse collapse {% if order == orders.0 %}show{% endif %}" aria-labelledby="heading{{ order.id }}" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<table class="table table-dark table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Товар</th>
|
||||
<th>Количество</th>
|
||||
<th>Цена</th>
|
||||
<th>Общая стоимость</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in order.orderitem_set.all %}
|
||||
<tr>
|
||||
<td><a class="text-white" href="{% url 'goods:product' item.product.slug %}">{{ item.product.name }}</a></td>
|
||||
<td>{{ item.quantity }}</td>
|
||||
<td>{{ item.price }}</td>
|
||||
<td>{{ item.products_price }}</td>
|
||||
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -4,12 +4,14 @@ from . import views
|
|||
app_name = 'main'
|
||||
|
||||
urlpatterns = [
|
||||
path('admin_logout_user/',views.admin_logout_user,name = 'admin_logout_user'),
|
||||
path('callback/', views.callback, name='callback'),
|
||||
path('profile/',views.profile,name='profile'),
|
||||
|
||||
path('', views.product_list, name='product_list'),
|
||||
path('about/', views.about, name='about'),
|
||||
path('products/', views.product_list, name='product_list'),
|
||||
path('<slug:category_slug>/', views.product_list,
|
||||
|
||||
path('products/', views.callback, name='product_list'),
|
||||
path('<slug:category_slug>/', views.callback,
|
||||
name='product_list_by_category'),
|
||||
path('<int:id>/<slug:slug>/', views.product_detail,
|
||||
name='product_detail'),
|
||||
|
|
196
main/views.py
196
main/views.py
|
@ -1,26 +1,20 @@
|
|||
import json
|
||||
|
||||
import requests
|
||||
from django.shortcuts import render,get_object_or_404
|
||||
from django.http import HttpResponse, JsonResponse
|
||||
from django.http import HttpResponse, HttpResponseRedirect
|
||||
import logging
|
||||
from djangoProject1 import settings
|
||||
from .models import Category, Product
|
||||
from cart.forms import CartAddProductForm
|
||||
from jose import jwt
|
||||
from django.contrib import messages
|
||||
|
||||
YOUR_REDIRECT_URI = 'http://127.0.0.1:8000/callback/'
|
||||
def home(request):
|
||||
# Ваш код для отображения главной страницы
|
||||
return render(request, 'main/base.html')
|
||||
|
||||
def product_list(request, category_slug=None):
|
||||
category = None
|
||||
categories = Category.objects.all()
|
||||
products = Product.objects.filter(available=True)
|
||||
if category_slug:
|
||||
category = get_object_or_404(Category,
|
||||
slug=category_slug)
|
||||
products = products.filter(category=category)
|
||||
return render(request,'main/product/list.html',
|
||||
{'category': category,
|
||||
'categories': categories,
|
||||
'products': products})
|
||||
|
||||
|
||||
|
||||
def product_detail(request, id, slug):
|
||||
product = get_object_or_404(Product,
|
||||
|
@ -38,5 +32,173 @@ def product_detail(request, id, slug):
|
|||
# Функция для страницы "О нас"
|
||||
def about(request):
|
||||
return render(request, 'main/about.html')
|
||||
def callback(request):
|
||||
return render(request,'main/callback.html')
|
||||
|
||||
def exchange_code_for_token(authorization_code, redirect_uri):
|
||||
token_endpoint = 'https://auth.myterior.kz/realms/Harmony/protocol/openid-connect/token'
|
||||
client_id = 'admin-cli'
|
||||
client_secret = 'wOVphEiLVBS1AlNKRpaQpD4yQh5Wm3TJ'
|
||||
|
||||
payload = {
|
||||
'grant_type': 'authorization_code',
|
||||
'code': authorization_code,
|
||||
'client_id': client_id,
|
||||
'client_secret': client_secret,
|
||||
'redirect_uri': redirect_uri,
|
||||
}
|
||||
|
||||
response = requests.post(token_endpoint, data=payload)
|
||||
print(response.status_code)
|
||||
if response.status_code == 200:
|
||||
tokens = response.json()
|
||||
access_token = tokens.get('access_token')
|
||||
return access_token
|
||||
else:
|
||||
print("Failed to exchange code: HTTP status", response.status_code)
|
||||
print("Response body:", response.text)
|
||||
raise Exception('Failed to exchange authorization code for tokens')
|
||||
def get_user_info(access_token):
|
||||
userinfo_endpoint = 'https://auth.myterior.kz/realms/Harmony/protocol/openid-connect/userinfo'
|
||||
headers = {'Authorization': f'Bearer {access_token}'}
|
||||
response = requests.get(userinfo_endpoint, headers=headers)
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
print("Failed to fetch user info: HTTP status", response.status_code)
|
||||
return None
|
||||
import jwt
|
||||
|
||||
def decode_access_token(access_token):
|
||||
|
||||
payload = jwt.decode(access_token, options={"verify_signature": False})
|
||||
return payload
|
||||
|
||||
|
||||
def callback(request,category_slug=None):
|
||||
#Получение кода авторизации из запроса
|
||||
context = {}
|
||||
authorization_code = request.GET.get('code')
|
||||
if authorization_code:
|
||||
try:
|
||||
access_token = exchange_code_for_token(authorization_code, YOUR_REDIRECT_URI)
|
||||
user_info = decode_access_token(access_token)
|
||||
request.session['user_info'] = user_info
|
||||
|
||||
|
||||
context['userinfo'] = request.session.get('user_info', {})
|
||||
|
||||
except Exception as e:
|
||||
context['error'] = str(e)
|
||||
else:
|
||||
context['error'] = "Authorization code not found."
|
||||
category = None
|
||||
categories = Category.objects.all()
|
||||
products = Product.objects.filter(available=True)
|
||||
if category_slug:
|
||||
category = get_object_or_404(Category,
|
||||
slug=category_slug)
|
||||
products = products.filter(category=category)
|
||||
return render(request, 'main/product/list.html',{'category': category,
|
||||
'categories': categories,
|
||||
'products': products})
|
||||
from django.shortcuts import render, redirect
|
||||
|
||||
def profile(request):
|
||||
if request.method == 'POST':
|
||||
|
||||
user_info = request.session.get('user_info', {})
|
||||
user_info['given_name'] = request.POST.get('first_name', user_info.get('given_name', ''))
|
||||
user_info['family_name'] = request.POST.get('last_name', user_info.get('family_name', ''))
|
||||
user_info['preferred_username'] = request.POST.get('username', user_info.get('preferred_username', ''))
|
||||
user_info['email'] = request.POST.get('email', user_info.get('email', ''))
|
||||
|
||||
|
||||
request.session['user_info'] = user_info
|
||||
|
||||
|
||||
return render(request, 'main/callback.html')
|
||||
else:
|
||||
|
||||
context = {'userinfo': request.session.get('user_info', {})}
|
||||
return render(request, 'main/callback.html', context)
|
||||
|
||||
|
||||
|
||||
# Настройте логирование
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def get_keycloak_admin_token():
|
||||
data = {
|
||||
'client_id': settings.KEYCLOAK_CONFIG['CLIENT_ID'],
|
||||
'client_secret': settings.KEYCLOAK_CONFIG['CLIENT_SECRET'],
|
||||
'grant_type': 'client_credentials',
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{settings.KEYCLOAK_CONFIG['SERVER_URL']}/realms/{settings.KEYCLOAK_CONFIG['REALM']}/protocol/openid-connect/token",
|
||||
data=data
|
||||
)
|
||||
return response.json()['access_token']
|
||||
|
||||
|
||||
def get_user_sessions(admin_token, user_id):
|
||||
headers = {'Authorization': f'Bearer {admin_token}'}
|
||||
response = requests.get(
|
||||
f"{settings.KEYCLOAK_CONFIG['SERVER_URL']}/admin/realms/{settings.KEYCLOAK_CONFIG['REALM']}/users/{user_id}/sessions",
|
||||
headers=headers
|
||||
)
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
logger.error(f"Error fetching user sessions: {response.status_code}")
|
||||
return []
|
||||
|
||||
|
||||
def logout_user(admin_token,session_id):
|
||||
headers = {
|
||||
'Authorization': f'Bearer {admin_token}',
|
||||
}
|
||||
response = requests.delete(
|
||||
f"{settings.KEYCLOAK_CONFIG['SERVER_URL']}/admin/realms/{settings.KEYCLOAK_CONFIG['REALM']}/sessions/{session_id}",
|
||||
headers=headers
|
||||
)
|
||||
return response.status_code == 204
|
||||
def get_user_id_from_session(request):
|
||||
user_info = request.session.get('user_info', {})
|
||||
user_id = user_info.get('sub')
|
||||
if isinstance(user_id, str):
|
||||
return user_id
|
||||
else:
|
||||
logger.error(f"Invalid user ID format in session: {user_id}")
|
||||
return None
|
||||
|
||||
|
||||
def admin_logout_user(request):
|
||||
user_id = get_user_id_from_session(request)
|
||||
|
||||
|
||||
admin_token = get_keycloak_admin_token()
|
||||
if not admin_token:
|
||||
logger.error("Can't obtain admin token.")
|
||||
messages.error(request, "Can't obtain admin token for logout.")
|
||||
return redirect(settings.KEYCLOAK_CONFIG['LOGOUT_REDIRECT_URL'])
|
||||
|
||||
sessions = get_user_sessions(admin_token, user_id)
|
||||
if sessions is None or not sessions:
|
||||
logger.error("Can't find user sessions.")
|
||||
messages.error(request, "No active sessions found for user.")
|
||||
return render(request,'main/logout.html')
|
||||
|
||||
all_sessions_logged_out = True
|
||||
for session in sessions:
|
||||
if not logout_user(admin_token, session['id']):
|
||||
all_sessions_logged_out = False
|
||||
logger.error(f"Failed to log out session {session['id']}")
|
||||
|
||||
if all_sessions_logged_out:
|
||||
logger.info("User successfully logged out from all Keycloak sessions.")
|
||||
messages.success(request, "Successfully logged out.")
|
||||
else:
|
||||
messages.warning(request, "Partial logout. Some sessions may still be active.")
|
||||
|
||||
request.session.flush() # Clear Django session
|
||||
return render(request,'main/logout.html')
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,23 @@
|
|||
from django.contrib.auth.backends import BaseBackend
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from main.views import decode_access_token
|
||||
|
||||
|
||||
class KeycloakBackend(BaseBackend):
|
||||
def authenticate(self, request, token=None):
|
||||
# Ваш код для проверки токена через Keycloak и извлечения информации о пользователе
|
||||
user_info = decode_access_token(token)
|
||||
if user_info:
|
||||
user, created = User.objects.get_or_create(username=user_info['preferred_username'],
|
||||
defaults={'first_name': user_info.get('given_name', ''),
|
||||
'last_name': user_info.get('family_name', ''),
|
||||
'email': user_info.get('email', '')})
|
||||
return user
|
||||
return None
|
||||
|
||||
def get_user(self, user_id):
|
||||
try:
|
||||
return User.objects.get(pk=user_id)
|
||||
except User.DoesNotExist:
|
||||
return None
|
|
@ -6,6 +6,6 @@ from . import views
|
|||
urlpatterns = [
|
||||
|
||||
path('login/', views.keycloak_login, name='keycloak_login'),
|
||||
path('keycloak_logout/', views.keycloak_logout, name='keycloak_logout'),
|
||||
|
||||
path('products/', views.keycloak_redirect, name='keycloak_redirect'),
|
||||
]
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import json
|
||||
|
||||
import logging
|
||||
|
||||
from django.http import JsonResponse
|
||||
|
||||
from django.shortcuts import redirect
|
||||
from django.conf import settings
|
||||
from keycloak import KeycloakOpenID
|
||||
from urllib.parse import urlencode, urljoin
|
||||
from urllib.parse import urlencode
|
||||
from django.contrib.auth import logout
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -26,22 +26,6 @@ def keycloak_login(request):
|
|||
|
||||
|
||||
|
||||
def keycloak_logout(request):
|
||||
logout(request)
|
||||
|
||||
keycloak_server_url = settings.KEYCLOAK_CONFIG['SERVER_URL']
|
||||
realm_name = settings.KEYCLOAK_CONFIG['REALM']
|
||||
redirect_uri = settings.KEYCLOAK_CONFIG['POST_LOGOUT_REDIRECT_URI']
|
||||
|
||||
|
||||
params = {'redirect_uri': redirect_uri}
|
||||
keycloak_logout_url = (
|
||||
f'{keycloak_server_url}/realms/{realm_name}/protocol/openid-connect/logout'
|
||||
f'?{urlencode(params)}'
|
||||
)
|
||||
|
||||
|
||||
return redirect(keycloak_logout_url)
|
||||
|
||||
from django.http import HttpResponse
|
||||
from django.views.decorators.http import require_http_methods
|
||||
|
|
Loading…
Reference in New Issue