Django进阶-auth集成认证模块
auth認(rèn)證模塊是Django內(nèi)置集成的一個(gè)用戶認(rèn)證模塊。
auth認(rèn)證模塊方法
方法 釋義
auth.authenticate() 認(rèn)證校驗(yàn)
auth.login(request,user) 封裝認(rèn)證了的user對(duì)象
auth.logout(request) 將session數(shù)據(jù)都刪除,且cookie也失效
auth認(rèn)證模塊示例
from django.shortcuts import render,redirect
from django.contrib import auth
from django.contrib.auth.decorators import login_required
def login(request):
if request.method == "POST":
user = request.POST.get("username")
pwd = request.POST.get("password")
user = auth.authenticate(username=user, password=pwd)
# auth認(rèn)證校驗(yàn),如果校驗(yàn)成功返回用戶名,否則返回空
if user:
auth.login(request, user)
# 封裝認(rèn)證了的user對(duì)象
return redirect("index.html")
return render(request, "login.html")
auth認(rèn)證模塊裝飾器使用
裝飾器,未登錄認(rèn)證時(shí)無法訪問 index 默認(rèn)跳轉(zhuǎn)到指定頁(yè)面,在setting中 配置LOGIN_URL = "跳轉(zhuǎn)的頁(yè)面名稱" 如:
LOGIN_URL = "login.html"
@login_required
def index(request):
print("登錄的用戶是:",request.user.username)
return render(request,"index.html")
auth認(rèn)證模塊實(shí)例
目錄架構(gòu)
MyDjango
APP
html
css
images
js
static
index.html
login.html
migrations
views
index.py
MyDjango
settings.py
urls.py
wsgi.py
db.sqlite3
manage.py
配置文件
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>wellcome</title>
</head>
<body>
{% csrf_token %}
<h1>wellcome index web !!!</h1>
<a href="login.html">退出</a>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post" action="login.html">
{% csrf_token %}
用戶名:<input type="text" name = "username">
密碼: <input type="text" name = "password">
<input type="submit">
</form>
</body>
</html>
index.py
from django.shortcuts import render,redirect,HttpResponse
from django.contrib import auth
from django.contrib.auth.decorators import login_required
def login(request):
auth.logout(request)
if request.method == "POST":
user = request.POST.get("username")
pwd = request.POST.get("password")
print("用戶名:",user,"密碼:",pwd)
user = auth.authenticate(username=user, password=pwd)
# auth認(rèn)證校驗(yàn),如果校驗(yàn)成功返回用戶名,否則返回空
if user:
auth.login(request, user)
# 封裝認(rèn)證了的user對(duì)象
return redirect("/index.html")
else:
return HttpResponse("登錄失敗,用戶或密碼錯(cuò)誤!")
return render(request, "login.html")
@login_required
def index(request):
user = request.user.username
print("用戶",user,"登錄成功!")
return render(request, "index.html",{"user":user})
settings.py
TEMPLATES
'DIRS': [os.path.join(BASE_DIR, 'APP/html/static')]
STATIC_URL = '/static/'
STATICFILES_DIRS = (os.path.join(BASE_DIR,"APP/html/static"),)
STATIC_ROOT = 'APP/html'
LOGIN_URL = "login.html"
urls.py
from django.contrib import admin
from django.urls import path,re_path
from APP.views import index
urlpatterns = [
path('admin/', admin.site.urls),
re_path('^login.html$', index.login),
re_path('^index.html$', index.index),
]
服務(wù)運(yùn)行
生成數(shù)據(jù)表
python manage.py makemigrations APP
python manage.py migrate
創(chuàng)建超級(jí)用戶用于登錄測(cè)試
python manage.py createsuperuser
服務(wù)運(yùn)行
python manage.py runserver
復(fù)制代碼
可以看到只增加了一個(gè)類,這個(gè)類有個(gè)特點(diǎn) 1. 它實(shí)現(xiàn)了 InvocationHandler 接口 2. 它的 invoke 方法實(shí)現(xiàn)了我們的需求。InvocationHandler 是 Java 動(dòng)態(tài)代理定義的一個(gè)接口,接口中定義了一個(gè) invoke 方法,我們調(diào)用代理對(duì)象的任何方法都會(huì)變成對(duì) FileWriterInvocationHandler 對(duì)象的 invoke 方法的調(diào)用, 在 invoke 方法中完成代理的功能并控制對(duì)真實(shí)對(duì)象的調(diào)用。如果看到你覺得一頭霧水,沒關(guān)系繼續(xù)向下看將豁然開朗。
到目前為止我們只看到新增了一個(gè) InvocationHandler 接口的實(shí)現(xiàn)類,并沒有看到代理對(duì)象。之前說過之所以是動(dòng)態(tài)代理是因?yàn)樵谶\(yùn)行時(shí)才創(chuàng)建代理類,因此我們需要編寫一個(gè)驅(qū)動(dòng)程序,動(dòng)態(tài)創(chuàng)建代理對(duì)象,完成動(dòng)態(tài)代理的后半部分。
復(fù)制代碼
package com.cnblogs.duma.dp.proxy.dynamic;
import java.lang.reflect.Proxy;
public class DynamicProxyDriver {
public static void main(String[] args) {
/**
* Proxy.newProxyInstance 包括三個(gè)參數(shù)
* 第一個(gè)參數(shù):定義代理類的 classloader,一般用被代理接口的 classloader
* 第二個(gè)參數(shù):需要被代理的接口列表
* 第三個(gè)參數(shù):實(shí)現(xiàn)了 InvocationHandler 接口的對(duì)象
* 返回值:代理對(duì)象
*/
Writer writer = (Writer) Proxy.newProxyInstance(
Writer.class.getClassLoader(),
new Class[www.baohuayule.com]{Writer.class},
new FileWriterInvocationHandler(new FileWriter())); //這就是動(dòng)態(tài)的原因,運(yùn)行時(shí)才創(chuàng)建代理類
try {
writer.write("file1.txt", "text"); //調(diào)用代理對(duì)象的write方法
} catch (Exception e) {
e.printStackTrace();
}
writer.write("file2.txt", new byte[]{}); //調(diào)用代理對(duì)象的write方法
}
}
復(fù)制代碼
最關(guān)的一步是 Proxy.newProxyInstance ,該調(diào)用會(huì)創(chuàng)建代理對(duì)象,該代理對(duì)象會(huì)將我們需要代理的接口(Writer)和 InvocationHandler 實(shí)現(xiàn)類關(guān)聯(lián)起來。這樣代理對(duì)象就會(huì)有 Writer 接口的 2 個(gè)方法,針對(duì)我們的業(yè)務(wù)邏輯調(diào)用過程為:調(diào)用代理對(duì)象 writer 的 write 方法寫數(shù)據(jù) -> 轉(zhuǎn)到 FileWriterInvocationHandler 對(duì)象的 invoke 方法,判斷磁盤空間是否夠用 -> 拋出磁盤空間不足異常或調(diào)用 FileWriter 對(duì)象的 write 方法寫數(shù)據(jù)。在這里動(dòng)態(tài)代理涉及到了 Writer 接口及其實(shí)現(xiàn)類、InvocationHandler 接口及其實(shí)現(xiàn)類、代理類。動(dòng)態(tài)代理 UML 類圖如下:
可以看到代理類 Proxy 實(shí)現(xiàn)了 Writer 接口,因此可以調(diào)用 write 方法,同時(shí)代理類關(guān)聯(lián) FileWriterInvocationHandler ,因此對(duì) write 方法的調(diào)用會(huì)變成對(duì) invoke 方法的調(diào)用。
至此,新的需求就完成了,我們結(jié)合代理模式談?wù)劥舜涡枨笞兏覀冇玫搅四男┖玫脑O(shè)計(jì)原則。
1. 我們沒有在原有 FileWrite 實(shí)現(xiàn)類中修改代碼, 而是新增了 FileInvocationHandler 實(shí)現(xiàn)新需求,這符合設(shè)計(jì)原則中的開閉原則,即:對(duì)擴(kuò)展開發(fā)對(duì)修改封閉。改動(dòng)現(xiàn)有代碼容易影響已有的正常代碼
2. 我們?cè)黾哟碇笾皇前?Writer writer = new FileWriter() 改為 Writer writer = Proxy.newProxyInstance(...),由于都繼承了 Writer 接口,因此不需要修改 writer 的類型, 這符合面向接口的設(shè)計(jì)原則,讓我們盡量少的改動(dòng)現(xiàn)有代碼
動(dòng)態(tài)代理還有一個(gè)重要的應(yīng)用場(chǎng)景,我們可以在 invoke 方法中把待調(diào)用的方法名(method)和參數(shù)(args)發(fā)送到遠(yuǎn)程服務(wù)器,在遠(yuǎn)程服務(wù)器中完成調(diào)用并返回一個(gè)結(jié)果,這其實(shí)就是 RPC (remote procedure call),即:遠(yuǎn)程過程調(diào)用。我在閱讀 Hadoop 源碼過程中發(fā)現(xiàn) Hadoop RPC 將動(dòng)態(tài)代理技術(shù)應(yīng)用在上述場(chǎng)景中。
遠(yuǎn)程代理
個(gè)人覺得上述動(dòng)態(tài)代理第二個(gè)應(yīng)用場(chǎng)景算是遠(yuǎn)程代理的一個(gè)特例,因?yàn)檫h(yuǎn)程代理不一定非要?jiǎng)討B(tài)創(chuàng)建代理對(duì)象。接下來我們以 Java RMI 為例, 簡(jiǎn)單看下遠(yuǎn)程代理。RMI(remote method invocation)即:遠(yuǎn)程方法調(diào)用,與 RPC 類似,可以讓我們像調(diào)用 Java 本地方法一樣,調(diào)用遠(yuǎn)程的方法。這里就需要一個(gè)代理對(duì)象,代理對(duì)象實(shí)現(xiàn)了本地的接口,其中序列化/反序列化以及網(wǎng)絡(luò)傳輸都在代理對(duì)象中實(shí)現(xiàn), 對(duì)我們透明,這也是控制了我們對(duì)遠(yuǎn)程對(duì)象的訪問。代碼如下:
復(fù)制代碼
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* 定義一個(gè)接口,接口中的方法要在遠(yuǎn)程調(diào)用
*/
public interface MyRemote extends Remote {
public String sayHello(www.gouyiflb.cn/ ) throws RemoteException;
}
復(fù)制代碼
復(fù)制代碼
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
/**
* 定義一個(gè)接口的遠(yuǎn)程實(shí)現(xiàn)類
* 為了讓遠(yuǎn)程對(duì)象擁有 “遠(yuǎn)程的” 功能,需要繼承 UnicastRemoteObject 類
*/
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote {
protected MyRemoteImpl() throws RemoteException {
}
/**
* 客戶端通過 rmi 代理對(duì)象調(diào)用 sayHello 方法,將會(huì)進(jìn)入到此方法
* @return
* @throws RemoteException
*/
@Override
public String sayHello(www.365soke.com) throws RemoteException {
System.out.println("req from client.");
return "Server says, 'Hey'";
}
/**
* 啟動(dòng)遠(yuǎn)程進(jìn)程的 main 方法
* @param args
*/
public static void main(String[www.lezongyule.com] args) {
try {
MyRemote service = new MyRemoteImpl(www.shengyunyule.cn);
Naming.rebind("RemoteHello", service); //將服務(wù)名和對(duì)應(yīng)的服務(wù)進(jìn)行綁定,客戶端會(huì)根據(jù) RemoteHello 找到遠(yuǎn)程服務(wù)
} catch (RemoteException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
復(fù)制代碼
這樣我們的遠(yuǎn)程服務(wù)已經(jīng)寫好了,還需要做以下 3 個(gè)工作來啟動(dòng)遠(yuǎn)程服務(wù)
1. 生成客戶端代理類,需要在 MyRemoteImpl.class 所在的目錄中執(zhí)行 rmic MyRemoteImpl 命令,將會(huì)生成 MyRemoteImpl_Stub.class 類。首先,rmic 命令是 jdk 自帶命令,所在的目錄與 java 和 javac 所在的目錄一樣;其次,我用的 Idea 創(chuàng)建的普通 Java 工程,我的 MyRemoteImpl.class 文件在“E:\backends\java-backends\java-ex\out\production\java-ex”目錄中,以我的工程為例,路徑以及命令執(zhí)行如下:
E:\backends\java-backends\java-ex\out \www.yun-shengyl.com production\java-ex>rmic MyRemoteImpl
2. 啟動(dòng) rmiregistry,為了遠(yuǎn)程服務(wù)可以注冊(cè)服務(wù)名,在我們的 class 所在的目錄(“項(xiàng)目目錄\out\production\java-ex”)中執(zhí)行 rmiregistry 命令
E:\backends\java-backends\java-ex\out\production\java-ex>rmiregistry
3. 運(yùn)行 MyRemoteImpl 類,啟動(dòng)遠(yuǎn)程服務(wù)進(jìn)程
繼續(xù)編寫客戶端訪問代碼,客戶端代碼主要是找到剛剛注冊(cè)的 RemoteHello 遠(yuǎn)程服務(wù),并獲得代理對(duì)象,調(diào)用代理對(duì)象上的方法。我們可以在同一個(gè)工程下,創(chuàng)建 MyRemoteClient 類
復(fù)制代碼
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
public class MyRemoteClient {
public static void main(String[www.suoLaiervip.com] args) {
try {
/**
* 找到遠(yuǎn)程服務(wù),并返回代理對(duì)象
* 該代理對(duì)象就是 MyRemoteImpl_Stub 且實(shí)現(xiàn)了 MyRemote 接口
*/
MyRemote service = (MyRemote) Naming.lookup("rmi://127.0.0.1/RemoteHello");
/**
* 調(diào)用代理對(duì)象的 sayHello 方法,便會(huì)通過代理將調(diào)用發(fā)送到遠(yuǎn)程服務(wù)進(jìn)程并返回結(jié)果
*/
String ret = service.sayHello();
System.out.println(ret);
} catch (RemoteException e) {
e.printStackTrace(www.qwert888.com/);
} catch (NotBoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
復(fù)制代碼
我們可以直接運(yùn)行 MyRemoteClient 類,可以看到在剛啟動(dòng)的 MyRemoteImpl 進(jìn)程中,控制臺(tái)打印了
req from client.
在 MyRemoteClient 進(jìn)程的控制臺(tái)中打印了
Server says, 'Hey'
至此我們的遠(yuǎn)程代理已經(jīng)介紹完畢。
虛擬代理
虛擬代理是作為創(chuàng)建開銷大的對(duì)象的替身。舉一個(gè)我們常見的例子,在 Web 開發(fā)或者移動(dòng)端開發(fā)的時(shí)候經(jīng)常會(huì)用到 Image 組件,Image 組件一般要傳入一個(gè) URL 參數(shù),從網(wǎng)絡(luò)上下載圖片到本地展示。假設(shè)這個(gè)組件要等到圖片下載完成才有顯示,那如果圖片較大或者網(wǎng)絡(luò)較慢,給用戶造成不好的體驗(yàn)。解決方法是我們可以先顯示一個(gè) loading 狀態(tài)的默認(rèn)的本地圖片,當(dāng)遠(yuǎn)程圖片下載完成后重新渲染,替換掉當(dāng)前的 laoding 狀態(tài)的圖片。用虛擬代理來實(shí)現(xiàn)這個(gè)技術(shù)就可以定義一個(gè) ImageProxy 類型,在該類中初始時(shí)候先展示一個(gè)默認(rèn)圖片,啟動(dòng)線程創(chuàng)建 Image 對(duì)象,Image 對(duì)象創(chuàng)建完畢,再重新渲染,替換默認(rèn)圖片。虛擬代理也是控制了對(duì) Image 對(duì)象的訪問。
總結(jié)
本章主要介紹了代理模式,并且我們看到了代理模式常用的幾種變形,同時(shí)也接觸了面向?qū)ο蟮幕镜脑O(shè)計(jì)原則
動(dòng)態(tài)代理 - 程序運(yùn)行時(shí)動(dòng)態(tài)地創(chuàng)建代理對(duì)象,所有的對(duì)代理對(duì)象方法的調(diào)用都會(huì)變成對(duì) InvocationHandler 的 invoke 方法的調(diào)用
遠(yuǎn)程代理 - 本地調(diào)用代理對(duì)象訪問遠(yuǎn)程的方法,無需關(guān)心網(wǎng)絡(luò)通信細(xì)節(jié),跟調(diào)用本地方法一樣
虛擬代理 - 為了創(chuàng)建開銷大的對(duì)象而存在
可以看到代理模式最核心就是控制,代理對(duì)象的目的就是控制對(duì)真實(shí)對(duì)象的訪問。
轉(zhuǎn)載于:https://www.cnblogs.com/qwangxiao/p/10638430.html
總結(jié)
以上是生活随笔為你收集整理的Django进阶-auth集成认证模块的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。