目录

JWT认证(5星)

token发展史

在用户注册或登录后,我们想记录用户的登录状态,或者为用户创建身份认证的凭证。我们不再使用Session认证机制,而使用Json Web Token(本质就是token)认证机制。

构成和工作原理

JWT的构成

JWT就是一段字符串,由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。就像这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

第一部分我们称它为头部(header),第二部分我们称其为荷载(payload, 类似于飞机上承载的物品),第三部分是签证(signature).

header(头部)

jwt的头部承载两部分信息:

  • 声明类型,这里是jwt
  • 声明加密的算法 通常直接使用 HMAC SHA256

完整的头部就像下面这样的JSON:

{
'typ': 'JWT',
'alg': 'HS256'
}

然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

payload(荷载)

荷载就是存放类似用户信息,过期时间,签发时间...

{
"userid": "1",
"name": "John Doe",
"exp": 1214356
}

然后将其进行base64加密,得到JWT的第二部分。

eyJ1c2VyaWQiOiAiMSIsICJuYW1lIjogIkpvaG4gRG9lIiwgImV4cCI6IDEyMTQzNTZ9

signature(签证)

JWT的第三部分是一个签证信息,这个签证信息由三部分组成:

  • header (base64解密后加密算法加密后的)
  • payload (base64解密后加密算法加密后的)
  • secret(密钥=加盐)

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

将这三部分用.连接成一个完整的字符串,构成了最终的jwt:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyaWQiOiAiMSIsICJuYW1lIjogIkpvaG4gRG9lIiwgImV4cCI6IDEyMTQzNTZ9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。

关于签发和核验JWT,我们可以使用Django REST framework JWT扩展来完成。

文档网站:http://getblimp.github.io/django-rest-framework-jwt/

补充base64编码解码

import base64
import json payload = {
"userid": "1",
"name": "John Doe",
"exp": 1214356
}
json_payload = json.dumps(payload)
# 编码
res = base64.b64encode(json_payload.encode('utf8')) print(res)
# 解码
res2 = json.loads(base64.b64decode(res))
print(res2) # b'eyJ1c2VyaWQiOiAiMSIsICJuYW1lIjogIkpvaG4gRG9lIiwgImV4cCI6IDEyMTQzNTZ9'
# {'userid': '1', 'name': 'John Doe', 'exp': 1214356}

本质原理

jwt认证算法:签发与校验

1)jwt分三段式:头.体.签名 (head.payload.sgin)
2)头和体是可逆加密,让服务器可以反解出user对象;签名是不可逆加密,保证整个token的安全性的(base64反解出的是hash加密后的密文)
3)头体签名三部分,都是采用json格式的字符串,进行加密,可逆加密一般采用base64算法,不可逆加密一般采用hash(md5)算法
4)头中的内容是基本信息:公司信息、项目组信息、token采用的加密方式信息
{
"company": "公司信息",
...
}
5)体中的内容是关键信息:用户主键、用户名、签发时客户端信息(设备号、地址)、过期时间
{
"user_id": 1,
...
}
6)签名中的内容是安全信息:头的加密结果 + 体的加密结果 + 服务器不对外公开的安全码(对整个字典进行md5加密)
{
"head": "头的加密字符串",
"payload": "体的加密字符串",
"secret": "安全码"
}

签发:根据登录请求提交来的 账号 + 密码 + 设备信息 签发 token

1)用基本信息存储json字典,采用base64算法加密得到 头字符串
2)用关键信息存储json字典,采用base64算法加密得到 体字符串
3)用头、体加密字符串再加安全码信息存储json字典,采用hash md5算法加密得到 签名字符串 账号密码就能根据User表得到user对象,形成的三段字符串用 . 拼接成token返回给前台

校验:根据客户端带token的请求 反解出 user 对象

1)将token按 . 拆分为三段字符串,第一段 头加密字符串 一般不需要做任何处理
2)第二段 体加密字符串,要反解出用户主键,通过主键从User表中就能得到登录用户,过期时间和设备信息都是安全信息,确保token没过期,且时同一设备来的
3)再用 第一段 + 第二段 + 服务器安全码 不可逆md5加密,与第三段 签名字符串 进行碰撞校验,通过后才能代表第二段校验得到的user对象就是合法的登录用户

drf项目的jwt认证开发流程(重点)

1)用账号密码访问登录接口,登录接口逻辑中调用 签发token 算法,得到token,返回给客户端,客户端自己存到cookies中

2)校验token的算法应该写在认证类中(在认证类中调用),全局配置给认证组件,所有视图类请求,都会进行认证校验,所以请求带了token,就会反解出user对象,在视图类中用request.user就能访问登录的用户

注:登录接口需要做 认证 + 权限 两个局部禁用

drf-jwt安装和简单使用(2星)

安装

pip3 install djangorestframework-jwt

简单使用

签发

# 1 创建超级用户
python3 manage.py createsuperuser
# 解释下为什么要创建超级用户:因为djangorestframework-jwt认证是基于django的auth里的user表作关联的,所以验证的数据也必须源自于这张表
# 2 配置路由urls.py
from django.urls import path
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
path('login/', obtain_jwt_token),
]
# 3 postman测试
向后端接口发送post请求,携带用户名密码,即可看到生成的token

认证

from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated class BookAPIView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookModelSerializer
# 必须用这个认证类
authentication_classes = [JSONWebTokenAuthentication, ]
# 还要配合这个权限
permission_classes = [IsAuthenticated, ]

在postman里

JWT使用auth表签发token,自定制返回格式(3星)

配置setting.py

JWT_AUTH ={
# token的过期时间
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
# 如果不自定义,返回的格式是固定的,只有token字段
# 这里把下面自定制的函数注册进来
'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.utils.jwt_response_payload_handler',
}

自定制的py文件内

def jwt_response_payload_handler(token, user=None, request=None):
return {
'code': 1000,
'msg': '登陆成功',
'username': user.username,
'token': token
}

这时登陆时返回的格式就变成了:

djangorestframework-jwt模块源码分析(2星)

签发token

ObtainJSONWebToken.as_view()--->ObtainJSONWebToken---->post方法
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid(): # 验证用户登录和签发token,都在序列化类的validate方法中完成的
user = serializer.object.get('user') or request.user
token = serializer.object.get('token')
response_data = jwt_response_payload_handler(token, user, request)
response = Response(response_data)
# 返回了咱们自定指的格式
'''
{
'code':100,
'msg':'登录成功',
'username':user.username,
'token': token,
} '''
return response
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) # 全局钩子函数
def validate(self, attrs):
credentials = {
self.username_field: attrs.get(self.username_field),
'password': attrs.get('password')
} if all(credentials.values()):
# 根据用户名密码去auth的user表校验,是否存在
user = authenticate(**credentials) if user:
if not user.is_active:
msg = _('User account is disabled.')
raise serializers.ValidationError(msg)
# 生成payload
payload = jwt_payload_handler(user) return {
'token': jwt_encode_handler(payload), # 通过payload生成token
'user': user
}
else:
# 不在抛异常,前端就看到信息了
raise serializers.ValidationError(msg)
else:
raise serializers.ValidationError(msg)

认证

JWT使用自定义User表,手动签发token,自定义认证类(5星)

签发token

重点在于

1.通过用户输入的用户名和密码去数据库中查出该用户

2.获取到的用户信息生成荷载(payload),jwt模块提供了

3.通过荷载来生成toekn,jwt模块提供了

4.把含有token串的字典返回给前端

from rest_framework.viewsets import ViewSet, ViewSetMixin
from rest_framework.generics import ListAPIView
from rest_framework.decorators import action
from .models import UserInfo, Book
from rest_framework.response import Response
from rest_framework_jwt.settings import api_settings
from .serializer import BookSerializer, UserInfoSerializer
from .authentcate import MyAuthentication jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER class UserAPIView(ViewSet):
@action(methods=['POST', ], detail=False)
def login(self, request):
back_dic = {'code': 100, 'msg': '登陆成功'}
username = request.data.get('username')
password = request.data.get('password')
user = UserInfo.objects.filter(username=username, password=password).first()
if user:
# 获取荷载 直接用jwt模块提供的,缺什么导什么
payload = jwt_payload_handler(user)
# 获取token串 直接用jwt模块提供的,缺什么导什么
token = jwt_encode_handler(payload)
back_dic['token'] = token
back_dic['username'] = username
else:
back_dic['code'] = 101
back_dic['msg'] = '用户名或密码错误'
return Response(back_dic)

自定义认证类

因为认证类要重写authenticate方法,所以重点就是在authenticate方法中写下面逻辑:

1.取出客户端传入的token(后端自己规定),看是携带在请求头中,还是在请求地址中

2.验证token中的签名(jwt模块提供了)

3.通过payload得到当前登陆的用户对象(jwt模块提供了)

4.返回user对象和token(或者是其他参数)

import jwt
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.settings import api_settings
from .models import UserInfo jwt_decode_handler = api_settings.JWT_DECODE_HANDLER class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
# 获取前端传的token串
jwt_value = request.META.get('HTTP_TOKEN')
if not jwt_value:
raise AuthenticationFailed('未携带token')
try:
# 获取荷载 直接用jwt模块提供的,缺什么导什么
payload = jwt_decode_handler(jwt_value)
except jwt.ExpiredSignature:
msg = 'token已过期'
raise AuthenticationFailed(msg)
except jwt.DecodeError:
msg = 'token被篡改'
raise AuthenticationFailed(msg)
except jwt.InvalidTokenError:
raise AuthenticationFailed('未知错误')
# 获取用户对象 用自定义的User表获取对象
user = UserInfo.objects.filter(pk=payload['user_id']).first()
# 上面的方法每次认证都要查数据库,下面有两种方法做优化,减少数据库压力
# 这种是实例化得到user对象,没有取数据库查表,提高了性能,只是不能跨表查询
user=User(id=payload.get('user_id'),username=payload.get('username'))
# 直接组织成字典,因为我们后续主要用的是用户id,视图类中按字典取值就行了
user={'id':payload.get('user_id'),'username':payload.get('username')}
# 把对象和token返回
return user, jwt_value

登陆逻辑写在序列化类里(以后这种常写)

在views.py中

class UserAPIView(ViewSet):
@action(methods=['POST', ], detail=False)
def login(self, request):
back_dic = {'code': 100, 'msg': '登陆成功'}
# 调用序列化类传入参数获得序列化类的对象(所有的逻辑都在全局钩子函数里实现的)
# 可以把context={'request':request}传入,那么在序列化类中就可以获取request对象
ser = UserInfoSerializer(data=request.data)
# 如果验证通过说明已经走完字段校验及钩子函数
if ser.is_valid():
# 通过在钩子函数中对context字典中放的数据获取用户名和token并返回
username = ser.context['username']
token = ser.context['token']
back_dic['username'] = username
back_dic['token'] = token
else:
# 返回错误信息
back_dic['code'] = 101
back_dic['msg'] = ser.errors
return Response(back_dic)

在序列化类.py中

from .models import Book, UserInfo
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.settings import api_settings jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER class UserInfoSerializer(serializers.ModelSerializer):
class Meta:
model = UserInfo
fields = ['id', 'username', 'password']
# 字段本身的校验
username = serializers.CharField(max_length=10, min_length=3)
password = serializers.CharField(max_length=10, min_length=3)
# 全局钩子函数(核心)
def validate(self, attrs):
username = attrs.get('username')
password = attrs.get('password')
user = UserInfo.objects.filter(username=username, password=password).first()
if not user:
raise ValidationError('用户名或密码错误')
# 获取荷载 直接用jwt模块提供的,缺什么导什么
payload = jwt_payload_handler(user)
# 获取token 直接用jwt模块提供的,缺什么导什么
token = jwt_encode_handler(payload)
# context字典是与视图函数沟通的桥梁,这里放,那里取,那里放,这里取
self.context['username'] = username
self.context['token'] = token
return attrs

补充: context字典是视图类与序列化类沟通的桥梁

在views.py中

class UserAPIView(ViewSet):
@action(methods=['POST', ], detail=False)
def login(self, request):
# 可以把context={'request':request}传入,那么在序列化类中就可以获取request对象
ser = UserInfoSerializer(data=request.data, context={'request':request})
if ser.is_valid():
...

在序列化类.py中

# 如果视图函数中传了reqeust,也可以取出
def validate(self, attrs):
request = self.context['request']
print(request.method)
...

多功能登陆

逻辑:

1.获取用户提交的用户名和密码

2.因为用户名可能是手机、邮箱、用户名,所以用正则进行判断

3.校验成功后签发token

普通版

from rest_framework.viewsets import ViewSet, ViewSetMixin
from rest_framework.generics import ListAPIView
from rest_framework.decorators import action
from .models import UserInfo, Book
from rest_framework.response import Response
from rest_framework_jwt.settings import api_settings
from .serializer import BookSerializer, UserInfoSerializer
from .authentcate import MyAuthentication
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
import re class UserAPIView(ViewSet):
@action(methods=['post', ], detail=False)
def login(self, request):
back_dic = {'code': 100, 'msg': '登陆成功'}
username = request.data.get('username')
password = request.data.get('password')
# 用正则判断到底是哪种登陆方式
re_phone = re.compile('^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\d{8}$')
re_email = re.compile('^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$')
if re_phone.search(username):
user = UserInfo.objects.filter(phone=username, password=password).first()
elif re_email.search(username):
user = UserInfo.objects.filter(email=username, password=password).first()
else:
user = UserInfo.objects.filter(username=username, password=password).first()
if not user:
back_dic['code'] = 101
back_dic['msg'] = '用户名或密码错误'
else:
# 获取荷载 直接用jwt模块提供的,缺什么导什么
payload = jwt_payload_handler(user)
# 获取token 直接用jwt模块提供的,缺什么导什么
token = jwt_encode_handler(payload)
back_dic['username'] = user.username
back_dic['token'] = token
return Response(back_dic)

进阶版(逻辑写在序列化类)

在views.py中

from rest_framework.viewsets import ViewSet, ViewSetMixin
from rest_framework.generics import ListAPIView
from rest_framework.decorators import action
from .models import UserInfo, Book
from rest_framework.response import Response
from rest_framework_jwt.settings import api_settings
from .serializer import BookSerializer, UserInfoSerializer
from .authentcate import MyAuthentication
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
import re class UserAPIView(ViewSet):
@action(methods=['post', ], detail=False)
def login(self, request):
back_dic = {'code': 100, 'msg': '登陆成功'}
# 调用序列化类传入参数获得序列化类的对象(所有的逻辑都在全局钩子函数里实现的)
# 可以把context={'request':request}传入,那么在序列化类中就可以获取request对象
ser = UserInfoSerializer(data=request.data)
# 如果验证通过说明已经走完字段校验及钩子函数
if ser.is_valid():
# 通过在钩子函数中对context字典中放的数据获取用户名和token并返回
username = ser.context['username']
token = ser.context['token']
back_dic['username'] = username
back_dic['token'] = token
else:
back_dic['code'] = 101
back_dic['msg'] = ser.errors
return Response(back_dic)

在序列化类.py中

from .models import Book, UserInfo
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.settings import api_settings
import re jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER class UserInfoSerializer(serializers.ModelSerializer):
class Meta:
model = UserInfo
fields = ['id', 'username', 'phone', 'email', 'password'] username = serializers.CharField() # 这里不重写会遇到username本身的校验从而导致验证邮箱和手机会报错
password = serializers.CharField(max_length=10, min_length=3) # 获取user对象的函数(拆分有助于扩展)
def _get_user(self, attrs):
username = attrs.get('username')
password = attrs.get('password')
# 用正则判断到底是哪种登陆方式
re_phone = re.compile('^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\d{8}$')
re_email = re.compile('^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$')
if re_phone.search(username):
user = UserInfo.objects.filter(phone=username, password=password).first()
elif re_email.search(username):
user = UserInfo.objects.filter(email=username, password=password).first()
else:
user = UserInfo.objects.filter(username=username, password=password).first()
if not user:
raise ValidationError('用户名或密码错误')
return user def validate(self, attrs):
user = self._get_user(attrs)
# 获取荷载 直接用jwt模块提供的,缺什么导什么
payload = jwt_payload_handler(user)
# 获取token 直接用jwt模块提供的,缺什么导什么
token = jwt_encode_handler(payload)
# context字典是与视图函数沟通的桥梁,这里放,那里取,那里放,这里取
self.context['username'] = user.username
self.context['token'] = token
return attrs

drf的JWT认证的更多相关文章

  1. drf框架 - JWT认证插件

    JWT认证 JWT认证方式与其他认证方式对比: 优点 1) 服务器不要存储token,token交给每一个客户端自己存储,服务器压力小 2)服务器存储的是 签发和校验token 两段算法,签发认证的效 ...

  2. DRF之JWT认证

    一.JWT认证 JWT构成 JWT分为三段式:头.体.签名(head.payload.sgin) 头和体是可逆加密的,让服务器可以反解析出user对象,签名是不可逆加密,保证整个token的安全性的. ...

  3. drf框架中jwt认证,以及自定义jwt认证

    0909自我总结 drf框架中jwt 一.模块的安装 官方:http://getblimp.github.io/django-rest-framework-jwt/ 他是个第三方的开源项目 安装:pi ...

  4. drf组件之jwt认证

    drf组件之jwt认证模块 一.认证规则 全称:json web token 解释:加密字符串的原始数据是json,后台产生,通过web传输给前台存储 格式:三段式 - 头.载荷.签名 - 头和载荷才 ...

  5. DRF框架(七) ——三大认证组件之频率组件、jwt认证

    drf频率组件源码 1.APIView的dispatch方法的  self.initial(request,*args,**kwargs)  点进去 2.self.check_throttles(re ...

  6. DRF的JWT用户认证

    目录 DRF的JWT用户认证 JWT的认证规则 JWT的格式 JWT认证的流程 JWT模块的导入为 JWT的使用 DRF的JWT用户认证 从根本上来说,JWT是一种开放的标准(RFC 7519), 全 ...

  7. drf认证组件、权限组件、jwt认证、签发、jwt框架使用

    目录 一.注册接口 urls.py views.py serializers.py 二.登录接口 三.用户中心接口(权限校验) urls.py views.py serializers.py 四.图书 ...

  8. drf认证组件(介绍)、权限组件(介绍)、jwt认证、签发、jwt框架使用

    目录 一.注册接口 urls.py views.py serializers.py 二.登录接口 三.用户中心接口(权限校验) urls.py views.py serializers.py 四.图书 ...

  9. 9) drf JWT 认证 签发与校验token 多方式登陆 自定义认证规则反爬 admin密文显示

    一 .认证方法比较 1.认证规则图 django 前后端不分离 csrf认证 drf 前后端分离 禁用csrf 2. 认证规则演变图 数据库session认证:低效 缓存认证:高效 jwt认证:高效 ...

随机推荐

  1. 【面经】Python面试的16个高频问题

    (一)Python 是如何进行内存管理的? 答:从三个方面来说,一对象的引用计数机制,二垃圾回收机制,三内存池机制 ⒈对象的引用计数机制 Python 内部使用引用计数,来保持追踪内存中的对象,所有对 ...

  2. Net中异步

    同步和异步1.同步是指只启动一个线程2.异步是指同时启动多个线程3.同步方法会卡界面,异步方法不会卡界面 原因:异步方法启动了子线程执行任务,主线程得到释放4.同步方法会慢.异步方法会快 原因:异步启 ...

  3. python+pytest接口自动化(9)-cookie绕过登录(保持登录状态)

    在编写接口自动化测试用例或其他脚本的过程中,经常会遇到需要绕过用户名/密码或验证码登录,去请求接口的情况,一是因为有时验证码会比较复杂,比如有些图形验证码,难以通过接口的方式去处理:再者,每次请求接口 ...

  4. pyhon反射

    一:反射 1.python面向对象中的反射: 通过字符串的形式操作对象相关的属性.python中的一切事物都是对象(都可以使用反射) 2.四个内置方法 hasattr 检测是否含有某属性 getatt ...

  5. Matplotlib库基础_一

    Matplotlib库基础 •pyplot绘制坐标 plt.plot(x,y,format_string,**kwargs) x:x轴数据,列表或数组,可选 y:y轴数据,列表或数组 format_s ...

  6. 通过blacklist来禁用驱动

    blacklist黑名单 我们在linux中安装驱动,有时会遇到受限或冲突,通常解决方式都是要修改blacklist.conf.对内核模块来说,黑名单是指禁止某个模块装入的机制 在 /etc/modp ...

  7. 《前端运维》二、Nginx--4代理、负载均衡与其他

    一.代理服务 比较容易理解吧,简单来说.客户端访问服务器并不是直接访问的,而是通过中间代理服务器,代理服务器再去访问服务器.就像一个中转站一样,无论什么,只要从客户端到服务器,你就要通过我. 一)正向 ...

  8. SINAMICS S120的核心控制单元CU320使用教程,电机模块接线

    SINAMICS是西门子公司新一代的驱动产品,它正在逐步取代现有的MASTERDRIVES及SIMODRIVE系列的驱动系统.SINAMICS S120是集V/f控制.矢量控制和伺服控制于一体的多轴驱 ...

  9. bzoj4044/luoguP4762 [Cerc2014]Virus synthesis(回文自动机+dp)

    bzoj4044/luoguP4762 [Cerc2014]Virus synthesis(回文自动机+dp) bzoj Luogu 你要用ATGC四个字母用两种操作拼出给定的串: 1.将其中一个字符 ...

  10. 创建自定义ClassLoader,绕过双亲委派

    1.什么是类加载 通过javac将.java文件编译成.class字节码文件后,则需要将.class加载到JVM中运行,哪么是谁将.class加载到JVM的呢?那就是类加载器啦. 2.类加载器类型 B ...