cookie不属于http协议范围,因为http协议没法保持状态,但实际状况,咱们却又须要“保持状态”,所以cookie就是在这样一个场景下诞生。javascript
cookie的工做原理是:由服务器产生内容,浏览器收到请求后保存在本地;当浏览器再次访问时,浏览器会自动带上cookie,这样服务器就能经过cookie的内容来判断这个是“谁”了。html
cookie虽然在必定程度上解决了“保持状态”的需求,可是因为cookie自己最大支持4096字节,以及cookie自己保存在客户端,可能被拦截或窃取,所以就须要有一种新的东西,它能支持更多的字节,而且他保存在服务器,有较高的安全性。这就是session。java
问题来了,基于http协议的无状态特征,服务器根本就不知道访问者是“谁”。那么上述的cookie就起到桥接的做用。jquery
咱们能够给每一个客户端的cookie分配一个惟一的id,这样用户在访问时,经过cookie,服务器就知道来的人是“谁”。而后咱们再根据不一样的cookie的id,在服务器上保存一段时间的私密资料,如“帐号密码”等等。程序员
总结而言:cookie弥补了http无状态的不足,让服务器知道来的人是“谁”;可是cookie以文本的形式保存在本地,自身安全性较差;因此咱们就经过cookie识别不一样的用户,对应的在session里保存私密的信息以及超过4096字节的文本。ajax
另外,上述所说的cookie和session实际上是共通性的东西,不限于语言和框架算法
前几节的介绍中咱们已经有能力制做一个登录页面,在验证了用户名和密码的正确性后跳转到后台的页面。可是测试后也发现,若是绕过登录页面。直接输入后台的url地址也能够直接访问的。这个显然是不合理的。其实咱们缺失的就是cookie和session配合的验证。有了这个验证过程,咱们就能够实现和其余网站同样必须登陆才能进入后台页面了。数据库
先说一下这种认证的机制。每当咱们使用一款浏览器访问一个登录页面的时候,一旦咱们经过了认证。服务器端就会发送一组随机惟一的字符串(假设是123abc)到浏览器端,这个被存储在浏览端的东西就叫cookie。而服务器端也会本身存储一下用户当前的状态,好比login=true,username=hahaha之类的用户信息。可是这种存储是以字典形式存储的,字典的惟一key就是刚才发给用户的惟一的cookie值。那么若是在服务器端查看session信息的话,理论上就会看到以下样子的字典django
{'123abc':{'login':true,'username:hahaha'}}后端
由于每一个cookie都是惟一的,因此咱们在电脑上换个浏览器再登录同一个网站也须要再次验证。那么为何说咱们只是理论上看到这样子的字典呢?由于处于安全性的考虑,其实对于上面那个大字典不光key值123abc是被加密的,value值{'login':true,'username:hahaha'}在服务器端也是同样被加密的。因此咱们服务器上就算打开session信息看到的也是相似与如下样子的东西
{'123abc':dasdasdasd1231231da1231231}
知道了原理,下面就来用代码实现。
咱们使用Chrome浏览器,打开开发者工具。
request.COOKIES['key'] request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None) #参数: default: 默认值 salt: 加密盐 max_age: 后台控制过时时间
rep = HttpResponse(...) 或 rep = render(request, ...) 或 rep = redirect() rep.set_cookie(key,value,...) rep.set_signed_cookie(key,value,salt='加密盐',...)
参数:
''' def set_cookie(self, key, 键 value='', 值 max_age=None, 超长时间 expires=None, 超长时间 path='/', Cookie生效的路径, 浏览器只会把cookie回传给带有该路径的页面,这样能够避免将 cookie传给站点中的其余的应用。 / 表示根路径,特殊的:根路径的cookie能够被任何url的页面访问 domain=None, Cookie生效的域名 你可用这个参数来构造一个跨站cookie。 如, domain=".example.com" 所构造的cookie对下面这些站点都是可读的: www.example.com 、 www2.example.com 和an.other.sub.domain.example.com 。 若是该参数设置为 None ,cookie只能由设置它的站点读取。 secure=False, 若是设置为 True ,浏览器将经过HTTPS来回传cookie。 httponly=False 只能http协议传输,没法被JavaScript获取 (不是绝对,底层抓包能够获取到也能够被覆盖) ): pass '''
因为cookie保存在客户端的电脑上,因此,JavaScript和jquery也能够操做cookie。
<script src='/static/js/jquery.cookie.js'> </script> $.cookie("key", value,{ path: '/' });
Expires和max-age均可以用来指定文档的过时时间,可是两者有一些细微差异 1.Expires在HTTP/1.0中已经定义,Cache-Control:max-age在HTTP/1.1中才有定义,为了向下兼容,仅使用max-age不够; 2.Expires指定一个绝对的过时时间(GMT格式),这么作会致使至少2个问题1)客户端和服务器时间不一样步致使Expires的配置出现问题 2)很容易在配置后忘记具体的过时时间,致使过时来临出现浪涌现象; 3.max-age 指定的是从文档被访问后的存活时间,这个时间是个相对值(好比:3600s),相对的是文档第一次被请求时服务器记录的Request_time(请求时间) 4.Expires指定的时间能够是相对文件的最后访问时间(Atime)或者修改时间(MTime),而max-age相对对的是文档的请求时间(Atime) 5.在Apache中,max-age是根据Expires的时间来计算出来的max-age = expires- request_time:(mod_expires.c) 429 expires = base + additional; 430 apr_table_mergen(t, "Cache-Control", 431 apr_psprintf(r->pool, "max-age=%" APR_TIME_T_FMT, 432 apr_time_sec(expires - r->request_time))); 注:若是是A,base=request_time,M的话base=finfo.mtime. expires - request_time获得max-age,若是Expires根据的是A(也就是AccessTime)设置的(A后面的值就是addtional),那么expires设置的值就等于addtional值,可是若是Expires根据的是Mtime,那么若是M后面的参数小于最后修改时间到当前时间的差(比方说2小时前修改了文件(19:00:00),如今设置M3600(如今是21:00:00)而且访问,max-age=expires-request_time = (finfo.mtime+additional)- request_time),计算出的max-age就是负数(能够试验看到这个结果): max-age = (19:00:00+3600/3600) - 21:00:00 = -3600s 这样,在Apache中,max-age就不只仅是相对Atime的时间了,若是设置为M,相对的是Mtime。
response.delete_cookie("cookie_key",path="/",domain=name)
def logout(request): rep = redirect("/login/") rep.delete_cookie("user") # 删除用户浏览器上以前设置的usercookie值 return rep
Cookie版登录校验
def check_login(func): @wraps(func) def inner(request, *args, **kwargs): next_url = request.get_full_path() if request.get_signed_cookie("login", salt="SSS", default=None) == "yes": # 已经登陆的用户... return func(request, *args, **kwargs) else: # 没有登陆的用户,跳转刚到登陆页面 return redirect("/login/?next={}".format(next_url)) return inner def login(request): if request.method == "POST": username = request.POST.get("username") passwd = request.POST.get("password") if username == "xxx" and passwd == "dashabi": next_url = request.GET.get("next") if next_url and next_url != "/logout/": response = redirect(next_url) else: response = redirect("/class_list/") response.set_signed_cookie("login", "yes", salt="SSS") return response return render(request, "login.html")
1、设置Sessions值 request.session['session_name'] ="admin" 2、获取Sessions值 session_name = request.session["session_name"] 3、删除Sessions值 del request.session["session_name"] 4、检测是否操做session值 if "session_name" is request.session :
五、get(key, default=None) fav_color = request.session.get('fav_color', 'red') #不存在则赋值red 6、pop(key) fav_color = request.session.pop('fav_color') 七、全部 键、值、键值对
request.session.keys() request.session.values() request.session.items() request.session.iterkeys() request.session.itervalues() request.session.iteritems() 8、setdefault()
request.session.setdefault('k1',123) # 存在则不设置 9、flush() 删除当前的会话数据并删除会话的Cookie。 这用于确保前面的会话数据不能够再次被用户的浏览器访问 例如,django.contrib.auth.logout() 函数中就会调用它。 十、 # 用户session的随机字符串 request.session.session_key # 将全部Session失效日期小于当前日期的数据删除 request.session.clear_expired() # 检查 用户session的随机字符串 在数据库中是否 request.session.exists("session_key") # 删除当前用户的全部Session数据 request.session.delete("session_key") request.session.set_expiry(value) * 若是value是个整数,session会在些秒数后失效。 * 若是value是个datatime或timedelta,session就会在这个时间后失效。 * 若是value是0,用户关闭浏览器session就会失效。 * 若是value是None,session会依赖全局session失效策略。
Session版登录验证
from functools import wraps def check_login(func): @wraps(func) def inner(request, *args, **kwargs): next_url = request.get_full_path() if request.session.get("user"): return func(request, *args, **kwargs) else: return redirect("/login/?next={}".format(next_url)) return inner def login(request): if request.method == "POST": user = request.POST.get("user") pwd = request.POST.get("pwd") if user == "alex" and pwd == "alex1234": # 设置session request.session["user"] = user # 获取跳到登录页面以前的URL next_url = request.GET.get("next") # 若是有,就跳转回登录以前的URL if next_url: return redirect(next_url) # 不然默认跳转到index页面 else: return redirect("/index/") return render(request, "login.html") @check_login def logout(request): # 删除全部当前请求相关的session request.session.delete() return redirect("/login/") @check_login def index(request): current_user = request.session.get("user", None) return render(request, "index.html", {"user": current_user})
#(3)设置session时进行的操做 request.session["user_id"]=user.pk request.session["username"]=user.user ''' 判断浏览器以前是否有cookie,若是有则更新这个session_key,一个浏览器只能维护一个session_key,也就是一个cookie的value。 if request.COOKIE.get("sessionid"): 更新 else: {"user_id":1,"username":"alex"} 第一步: 生成随机字符串: vwerascxh24asdasdasdsd 第二步: 在django-sesion表生成一条记录: session-key session-data vwerascxh24asdasdasdsd {"user_id":1,"username":"alex"} 第三步: obj.set_cookie("sessionid",vwerascxh24asdasdasdsd) ''' #(4)查询一个session时的操做 request.session.get("user_id") ''' 1 request.COOKIE.get("sessionid"):vwerascxh24asdasdasdsd 2 在django-sesion表查询一条记录:session-key=vwerascxh24asdasdasdsd 3 session-data({"user_id":1,"username":"alex"}).get("user_id") '''
views:
def log_in(request): if request.method=="POST": username=request.POST['user'] password=request.POST['pwd'] user=UserInfo.objects.filter(username=username,password=password) if user: #设置session内部的字典内容 request.session['is_login']='true' request.session['username']=username #登陆成功就将url重定向到后台的url return redirect('/backend/') #登陆不成功或第一访问就停留在登陆页面 return render(request,'login.html') def backend(request): print(request.session,"------cookie") print(request.COOKIES,'-------session') """ 这里必须用读取字典的get()方法把is_login的value缺省设置为False, 当用户访问backend这个url先尝试获取这个浏览器对应的session中的 is_login的值。若是对方登陆成功的话,在login里就已经把is_login 的值修改成了True,反之这个值就是False的 """ is_login=request.session.get('is_login',False) #若是为真,就说明用户是正常登录的 if is_login: #获取字典的内容并传入页面文件 cookie_content=request.COOKIES session_content=request.session username=request.session['username'] return render(request,'backend.html',locals()) else: """ 若是访问的时候没有携带正确的session, 就直接被重定向url回login页面 """ return redirect('/login/') def log_out(request): """ 直接经过request.session['is_login']回去返回的时候, 若是is_login对应的value值不存在会致使程序异常。因此 须要作异常处理 """ try: #删除is_login对应的value值 del request.session['is_login'] # OR---->request.session.flush() # 删除django-session表中的对应一行记录 except KeyError: pass #点击注销以后,直接重定向回登陆页面 return redirect('/login/')
template:
===================================login.html================== <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/login/" method="post"> <p>用户名: <input type="text" name="user"></p> <p>密码: <input type="password" name="pwd"></p> <p><input type="submit"></p> </form> </body> </html> ===================================backend.html================== <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>hello {{ username }}</h3> <a href="/logout/">注销</a> </body> </html>
(1)数据库配置(默认):
Django默认支持Session,而且默认是将Session数据存储在数据库中,即:django_session 表中。 a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认) SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过时(默认) SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改以后才保存(默认)
(2)缓存配置
a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎 SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也能够是memcache),此处别名依赖缓存的设置 SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 SESSION_COOKIE_SECURE = False # 是否Https传输cookie SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过时 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改以后才保存
(3)文件配置
a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎 SESSION_FILE_PATH = None # 缓存文件路径,若是为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 SESSION_COOKIE_SECURE = False # 是否Https传输cookie SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过时 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改以后才保存
其余存储方式
4. 缓存+数据库 SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎 5. 加密Cookie Session SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎 其余公用设置项: SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认) SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过时(默认) SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改以后才保存(默认)
CBV(class base views) 就是在视图里使用类处理请求。
CBV实现的登陆视图
class LoginView(View): def get(self, request): """ 处理GET请求 """ return render(request, 'login.html') def post(self, request): """ 处理POST请求 """ user = request.POST.get('user') pwd = request.POST.get('pwd') if user == 'alex' and pwd == "alex1234": next_url = request.GET.get("next") # 生成随机字符串 # 写浏览器cookie -> session_id: 随机字符串 # 写到服务端session: # { # "随机字符串": {'user':'alex'} # } request.session['user'] = user if next_url: return redirect(next_url) else: return redirect('/index/') return render(request, 'login.html')
urls.py须要修改以下:
from app01 import views urlpatterns = [ # url(r‘^index/‘, views.index), url(r‘^login/‘, views.LoginView.as_view()), ] #注:url(r‘^login/‘, views.LoginView.as_view()), 是固定用法。
要在CBV视图中使用咱们上面的check_login装饰器,有如下三种方式:
from django.utils.decorators import method_decorator
1. 加在CBV视图的get或post方法上
from django.utils.decorators import method_decorator class HomeView(View): def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") @method_decorator(check_login) def post(self, request): print("Home View POST method...") return redirect("/index/")
2. 加在dispatch方法上
from django.utils.decorators import method_decorator class HomeView(View): @method_decorator(check_login) def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") def post(self, request): print("Home View POST method...") return redirect("/index/")
由于CBV中首先执行的就是dispatch方法,因此这么写至关于给get和post方法都加上了登陆校验。
3. 直接加在视图类上,但method_decorator必须传 name 关键字参数
若是get方法和post方法都须要登陆校验的话就写两个装饰器。
from django.utils.decorators import method_decorator @method_decorator(check_login, name="get") @method_decorator(check_login, name="post") class HomeView(View): def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") def post(self, request): print("Home View POST method...") return redirect("/index/")
CSRF Token相关装饰器在CBV只能加到dispatch方法上
备注:
from django.views.decorators.csrf import csrf_exempt, csrf_protect class HomeView(View): @method_decorator(csrf_exempt) def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") def post(self, request): print("Home View POST method...") return redirect("/index/")
CSRF Token相关装饰器在FBV就直接加在函数上:
from django.views.decorators.csrf import csrf_exempt def login(request): print('hello ajax') return render(request,'index.html') @csrf_exempt #csrf防护 def ajax_post(request): print('ok') return HttpResponse('helloyuanhao')
from django.contrib import auth
User表auth_user的SQL描述:
CREATE TABLE "auth_user" ( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "password" varchar(128) NOT NULL, "last_login" datetime NULL, "is_superuser" bool NOT NULL, "first_name" varchar(30) NOT NULL, "last_name" varchar(30) NOT NULL, "email" varchar(254) NOT NULL, "is_staff" bool NOT NULL, "is_active" bool NOT NULL, "date_joined" datetime NOT NULL, "username" varchar(30) NOT NULL UNIQUE )
django.contrib.auth中提供了许多方法,这里主要介绍其中的三个:
提供了用户认证,即验证用户名以及密码是否正确,通常须要username password两个关键字参数
若是认证信息有效,会返回一个 User 对象。authenticate()会在User 对象上设置一个属性标识那种认证后端认证了该用户,且该信息在后面的登陆过程当中是须要的。当咱们试图登录一个从数据库中直接取出来不通过authenticate()的User对象会报错的!!
使用authenticate
模块,使用时,先导入模块:
from django.contrib.auth import authenticate user = authenticate(username='someone',password='somepassword')
须要注意的是:该方法不检查is_active
标志位。
该函数接受一个HttpRequest对象,以及一个认证了的User对象
此函数使用django的session框架给某个已认证的用户附加上session id等信息。
login不进行认证,也不检查is_active标志位, 通常和authenticate配合使用:
from django.contrib.auth import authenticate, login def my_view(request): username = request.POST['username'] password = request.POST['password'] user = authenticate(username=username, password=password) if user is not None: login(request, user) # Redirect to a success page. ... else: # Return an 'invalid login' error message. ...
登陆过的用户,能够在views的其余函数中直接调用request.user获得登陆用户对象,甚至能够在模板中直接使用{% request.user.username %};
若是用户未登陆,则request.user也有一个对象,是一个匿名用户对象,其全部属性如用户名,密码等都为空,因此在模板中验证用户是否登陆须要用
{% if request.user.username %} 而不是{% if request.user %}
from django.contrib.auth import logout def logout_view(request): logout(request) # Redirect to a success page.
该函数接受一个HttpRequest对象,无返回值。当调用该函数时,当前请求的session信息会所有清除。该用户即便没有登陆,使用该函数也不会报错。
要求:
1 用户登录后才能访问某些页面,
2 若是用户没有登陆就访问该页面的话直接跳到登陆页面
3 用户在跳转的登录界面中完成登录后,自动访问跳转到以前访问的地址
方法1:
def my_view(request): if not request.user.is_authenticated(): return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
方法2:
django已经为咱们设计好了一个用于此种状况的装饰器:login_requierd()
from django.contrib.auth.decorators import login_required @login_required(login_url='/accounts/login/') def my_view(request):
...
未登陆用户将被重定向到login_url
指定的位置。若未指定login_url参数, 则会跳转到django默认的 登陆URL '/accounts/login/ ' (这个值能够在settings文件中经过LOGIN_URL进行修改)。并传递 当前访问url的绝对路径 (登录成功后,会重定向到该路径)。
User 对象属性:username, password(必填项)password用哈希算法保存到数据库
is_staff : 用户是否拥有网站的管理权限.
is_active : 是否容许用户登陆, 设置为``False``,能够不用删除用户来禁止 用户登陆
若是是真正的 User 对象,返回值恒为 True 。 用于检查用户是否已经经过了认证。
经过认证并不意味着用户拥有任何权限,甚至也不检查该用户是否处于激活状态,这只是代表用户成功的经过了认证。 这个方法很重要, 在后台用request.user.is_authenticated()判断用户是否已经登陆,若是true则能够向前台展现request.user.name
使用 create_user 辅助函数建立用户:
from django.contrib.auth.models import User user = User.objects.create_user(username='',password='',email='')
用户须要修改密码的时候 首先要让他输入原来的密码 ,若是给定的字符串经过了密码检查,返回 True
使用 set_password() 来修改密码
user = User.objects.get(username='') user.set_password(password='') user.save
注册:
def sign_up(request): state = None if request.method == 'POST': password = request.POST.get('password', '') repeat_password = request.POST.get('repeat_password', '') email=request.POST.get('email', '') username = request.POST.get('username', '') if User.objects.filter(username=username): state = 'user_exist' else: new_user = User.objects.create_user(username=username, password=password,email=email) new_user.save() return redirect('/book/') content = { 'state': state, 'user': None, } return render(request, 'sign_up.html', content)
修改密码:
@login_required def set_password(request): user = request.user state = None if request.method == 'POST': old_password = request.POST.get('old_password', '') new_password = request.POST.get('new_password', '') repeat_password = request.POST.get('repeat_password', '') if user.check_password(old_password): if not new_password: state = 'empty' elif new_password != repeat_password: state = 'repeat_error' else: user.set_password(new_password) user.save() return redirect("/log_in/") else: state = 'password_error' content = { 'user': user, 'state': state, } return render(request, 'set_password.html', content)
使用auth模块大前提是要使用Django自动生成的用户表:auth_user,然而这个表字段是已定义好的,缺少灵活性,可使用表继承功能为其添加自定义字段;
继承表后,将继承表和父表合二为一,继承表拥有父表全部字段:
from django.contrib.auth.models import AbstractUser class UserInfo(AbstractUser): #继承自带user表 """ 用户信息 """ nid = models.AutoField(primary_key=True) #本身重写主键会覆盖原主键 telephone = models.CharField(max_length=11, null=True, unique=True) avatar = models.FileField(upload_to='avatars/', default="/avatars/default.png") create_time = models.DateTimeField(verbose_name='建立时间', auto_now_add=True) blog = models.OneToOneField(to='Blog', to_field='nid', null=True) def __str__(self): return self.username
settings.py
#找合适位置添加: AUTH_USER_MODEL="blog.UserInfo" #blog是应用名.UserInfo是表名
views.py
#使用继承扩展user表后,在使用User功能时须要修改以下 # from django.contrib.auth.models import User from .models import UserInfo as User
django.contrib.auth.models.Group
定义了用户组的模型, 每一个用户组拥有id
和name
两个字段, 该模型在数据库被映射为auth_group
数据表。
User对象中有一个名为groups
的多对多字段, 多对多关系由auth_user_groups
数据表维护。Group对象能够经过user_set
反向查询用户组中的用户。
咱们能够经过建立删除Group对象来添加或删除用户组:
# add group = Group.objects.create(name=group_name) group.save() # del group.delete()
咱们能够经过标准的多对多字段操做管理用户与用户组的关系:
#用户加入用户组 user.groups.add(group) #或者 group.user_set.add(user) #用户退出用户组 user.groups.remove(group) #或者 group.user_set.remove(user) #用户退出全部用户组 user.groups.clear() #用户组中全部用户退出组 group.user_set.clear()
Django的auth系统提供了模型级的权限控制, 便可以检查用户是否对某个数据表拥有增(add), 改(change), 删(delete)权限。
auth系统没法提供对象级的权限控制, 即检查用户是否对数据表中某条记录拥有增改删的权限。若是须要对象级权限控制可使用django-guardian
。
假设在博客系统中有一张article数据表管理博文, auth能够检查某个用户是否拥有对全部博文的管理权限, 但没法检查用户对某一篇博文是否拥有管理权限。
user.has_perm
方法用于检查用户是否拥有操做某个模型的权限:
user.has_perm('blog.add_article') user.has_perm('blog.change_article') user.has_perm('blog.delete_article')
述语句检查用户是否拥有blog这个app中article模型的添加权限, 若拥有权限则返回True。
has_perm
仅是进行权限检查, 便是用户没有权限它也不会阻止程序员执行相关操做。
@permission_required
装饰器能够代替has_perm
并在用户没有相应权限时重定向到登陆页或者抛出异常。
# permission_required(perm[, login_url=None, raise_exception=False]) @permission_required('blog.add_article') def post_article(request): pass
每一个模型默认拥有增(add), 改(change), 删(delete)权限。在django.contrib.auth.models.Permission
模型中保存了项目中全部权限。
该模型在数据库中被保存为auth_permission
数据表。每条权限拥有id
,name
, content_type_id
, codename
四个字段。
User和Permission经过多对多字段user.user_permissions
关联,在数据库中由auth_user_user_permissions
数据表维护。
#添加权限 user.user_permissions.add(permission) #删除权限: user.user_permissions.delete(permission) #清空权限: user.user_permissions.clear()
用户拥有他所在用户组的权限, 使用用户组管理权限是一个更方便的方法。Group中包含多对多字段permissions
, 在数据库中由auth_group_permissions
数据表维护。
#添加权限: group.permissions.add(permission) #删除权限: group.permissions.delete(permission) #清空权限: group.permissions.clear()
在定义Model时可使用Meta自定义权限:
class Discussion(models.Model): ... class Meta: permissions = ( ("create_discussion", "Can create a discussion"), ("reply_discussion", "Can reply discussion"), )
判断用户是否拥有自定义权限:
user.has_perm('blog.create_discussion')