分类目录归档:Python

微信小程序登录和鉴权 django jwt


2021年12月18日 17:12:32   2,515 次浏览

1|什么是JWT ?

JWT,全称Json Web Token,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

1|与Session的区别

一、Session是在服务器端的,而JWT是在客户端的,这点很重要。
二、流程不同

1|JWT使用场景

  • 大量需要进行跨域的站点
  • 服务器运算能力较差、存储空间较小

1|JWT的原理

JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样。





{
  "姓名": "张三",
  "角色": "管理员",
  "到期时间": "2018年7月1日0点0分"
}

以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名(详见后文)。

服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。

1|JWT数据的格式

实际的 JWT 大概就像下面这样。

它是一个很长的字符串,中间用点(.)分隔成三个部分。注意,JWT 内部是没有换行的,这里只是为了便于展示,将它写成了几行。

JWT 的三个部分依次如下。

  • Header(头部)
  • Payload(负载)
  • Signature(签名)

Header 部分是一个 JSON 对象,描述 JWT 的元数据,通常是下面的样子。





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

上面代码中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT。

最后,将上面的 JSON 对象使用 Base64URL 算法(详见后文)转成字符串。

Payload

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。

  • iss (issuer):签发人
  • exp (expiration time):过期时间
  • sub (subject):主题
  • aud (audience):受众
  • nbf (Not Before):生效时间
  • iat (Issued At):签发时间
  • jti (JWT ID):编号

除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子。





{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}


注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。

这个 JSON 对象也要使用 Base64URL 算法转成字符串。

Signature

Signature 部分是对前两部分的签名,防止数据篡改。

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。





HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用”点”(.)分隔,就可以返回给用户。

Base64URL

前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。

JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。这就是 Base64URL 算法。

1|JWT的使用方式

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。

此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。





Authorization: Bearer <token>

另一种做法是,跨域的时候,JWT 就放在 POST 请求的数据体里面。

1|JWT 的几个特点

(1)JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。

(2)JWT 不加密的情况下,不能将秘密数据写入 JWT。

(3)JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。

(4)JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。

(5)JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。

(6)为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。

1|内容说明

以上主要内容转载于廖雪峰的网络日志

2|0实例:使用Django完成微信小程序的JWT登录及鉴权

网上目前已经有了一些文章来说明了,可是我在查阅的时候发现大多讲得不是很清楚。

2|1基本了解

通过之前的内容铺垫,相信读者对于JWT都有了一定的了解,总的来说,便是JWT是保存在用户端的Token机制。

需要注意的是:JWT默认是无加密的,只是使用了一层Base64编码,所以我们不能将重要信息,如密码等放入header和payload字段中。

2|2开始

对于Django来说,这里我们使用djangorestframework-jwt库

安装命令:





pip install djangorestframework-jwt

注意djangorestframework-jwt库默认将settings里的SECRET_KEY当中jwt加密秘钥。

首先我们先去我们的project下的settings文件内设置jwt库的一些参数





import datetime

# 在末尾添加上
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',# JWT认证,在前面的认证方案优先
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),
}
JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), #JWT_EXPIRATION_DELTA 指明token的有效期
}


登录函数的实现:





'''
登录函数:
'''
def get_user_info_func(user_code):
    api_url = 'https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code'
    get_url = api_url.format(App_id,App_secret,user_code)
    r = requests.get(get_url)
    return r.json()


@require_http_methods(['POST'])
def user_login_func(request):
    try:
        user_code = request.POST.get('user_code')
        print(user_code)
        if user_code == None:
            print(request.body)
            json_data =  json.loads(request.body)
            user_code = json_data['user_code']
            print(user_code)
    except:
        return JsonResponse({'status':500,'error':'请输入完整数据'})
    try:
        json_data = get_user_info_func(user_code)
        #json_data = {'errcode':0,'openid':'111','session_key':'test'}
        if 'errcode' in json_data:
            return JsonResponse({'status': 500, 'error': '验证错误:' + json_data['errmsg']})
        res = login_or_create_account(json_data)
        return JsonResponse(res)
    except:
        return JsonResponse({'status':500,'error':'无法与微信验证端连接'})


def login_or_create_account(json_data):
    openid = json_data['openid']
    session_key = json_data['session_key']

    try:
        user = User.objects.get(username=openid)
    except:
        user = User.objects.create(
            username=openid,
            password=openid,
        )
    user.session_key = session_key
    user.save()

    try:
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        res = {
            'status': 200,
            'token': token
        }
    except:
        res = {
            'status': 500,
            'error': 'jwt验证失败'
        }
    return res

视图函数:





'''
视图样例:
'''
from django.http import JsonResponse
from account.models import *
from rest_framework_jwt.views import APIView
from rest_framework import authentication
from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):

    def has_object_permission(self, request, view, obj):
        if request.method in ['GET','POST']:
            return True
        return obj.user == request.user

class test_view(APIView):
    http_method_names = ['post']  #限制api的访问方式
    authentication_classes = (authentication.SessionAuthentication,JSONWebTokenAuthentication)
    permission_classes = (IsAuthenticated,IsOwnerOrReadOnly)       #权限管理

    def post(self,request):                                        #视图函数
        user = request.user.username
        U = User.objects.get(username=user)
        json_data = json.loads(request.body)
        try:
            test = json_data['']
        except:
            return JsonResponse({'status':500,'errmsg':'参数不全'})
        try:
            U.sex = sex
            U.weight = weight
            U.height = height
            U.save()
        except:
            return JsonResponse({'status': 500, 'errmsg': '数据库错误'})
        return JsonResponse({'status':200})

urls.py:





urlpatterns = [
    re_path('^$', index),
    re_path('^login$',login),                                 # 登录
    re_path('^test$',test_view.as_view())
]

使用python操作Jenkins权限


2020年9月03日 16:19:09   2,645 次浏览

对于 jenkins 的权限管理,一般来说都会使用 Role-based Authorization Strategy 进行管理

在人员较少或项目不多的时候,使用上没什么问题,但随着人员的增多或项目的增多,手动添加权限会很麻烦,需要一个权限一个权限的去勾选,非常容易出错,此时就需要通过该插件提供的 API 去操作权限,但是 官方文档 中并没有介绍 API 的使用,最终在 gitter 中发现有大佬指出了其 REST API 源代码位置,即 https://github.com/jenkinsci/role-strategy-plugin/blob/master/src/main/java/com/michelin/cio/hudson/plugins/rolestrategy/RoleBasedAuthorizationStrategy.java#L391-L395

其中注释很清楚,并且都会有 curl 示例,使用起来很方便,当然注释中也有一些小问题,该踩的坑我已经踩完了,下面主要演示一下使用 python 对权限的修改

权限内容

首先了解下该权限组成以及其对应的权限ID

全局权限
权限范围 权限名称 权限ID
全部 Administer hudson.model.Hudson.Administer
Read hudson.model.Hudson.Read
凭据 Create com.cloudbees.plugins.credentials.CredentialsProvider.Create
Delete com.cloudbees.plugins.credentials.CredentialsProvider.Delete
ManageDomains com.cloudbees.plugins.credentials.CredentialsProvider.ManageDomains
Update com.cloudbees.plugins.credentials.CredentialsProvider.Update
View com.cloudbees.plugins.credentials.CredentialsProvider.View
代理 Build hudson.model.Computer.Build
Configure hudson.model.Computer.Configure
Connect hudson.model.Computer.Connect
Create hudson.model.Computer.Create
Delete hudson.model.Computer.Delete
Disconnect hudson.model.Computer.Disconnect
Provision hudson.model.Computer.Provision
任务(Job) Build hudson.model.Item.Build
Cancel hudson.model.Item.Cancel
Configure hudson.model.Item.Configure
Create hudson.model.Item.Create
Delete hudson.model.Item.Delete
Discover hudson.model.Item.Discover
Move hudson.model.Item.Move
Read hudson.model.Item.Read
Workspace hudson.model.Item.Workspace
运行(构建历史操作) Delete hudson.model.Run.Delete
Replay hudson.model.Run.Replay
Update hudson.model.Run.Update
视图 Configure hudson.model.View.Configure
Create hudson.model.View.Create
Delete hudson.model.View.Delete
Read hudson.model.View.Read
SCM Tag hudson.scm.SCM.Tag
Lockable Resources Reserve org.jenkins.plugins.lockableresources.LockableResourcesManager.Reserve
Unlock org.jenkins.plugins.lockableresources.LockableResourcesManager.Unlock
View org.jenkins.plugins.lockableresources.LockableResourcesManager.View

使用代码添加角色指定权限的时候,指定的权限ID必须是上表中权限ID列中的值

项目权限

包括上面的 凭据、任务、运行、SCM、Lockable Resources 相关所有权限

节点权限

包括上面的 凭据、代理、Lockable Resources 相关所有权限

封装API

实现很简单,使用 requests 库去构建 GET 和 POST 请求即可,下面是封装好的示例

import requests


class JenkinsRole:
    def __init__(self, host, username, port=80, password=None, token=None, ssl=False):
        """
        password和token使用其中一个即可
        :param host: Jenkins主机
        :param username: 管理员用户
        :param port: Jenkins端口,默认为80
        :param password: 管理员密码
        :param token: 管理员的Token
        :param ssl: Jenkins地址是否是https协议
        """
        self.host = host
        self.username = username
        self.port = port
        self.password = password
        self.token = token
        self.ssl = ssl

    @property
    def pwd_or_token(self):
        if self.password and self.token:
            raise ConnectionError("password与token填写一个即可")
        return self.password if self.password else self.token

    @property
    def proto(self):
        return 'https' if self.ssl else 'http'

    def get_crumb(self) -> dict:
        res = requests.get(
            f'{self.proto}://{self.username}:{self.pwd_or_token}@{self.host}:{self.port}/crumbIssuer/api/xml?'
            f'xpath=concat(//crumbRequestField,":",//crumb)')

        return {res.text.split(':')[0]: res.text.split(':')[1]}

    def add_role(self, role_type, role_name, permissions: str, role_pattern=None, overwrite=True):
        """
        添加角色
        如果添加的权限不属于对应的角色类型,两种情况:
        1、添加的权限都不属于对应的角色类型,则会添加一个空权限的角色
        比如向projectRoles中添加视图权限hudson.model.View.Create命名为p1,
        则在projectRoles列表中依然会添加p1角色,但是该角色没有任何权限

        2、添加的权限部分不属于对应的角色类型,则会将属于该角色类型的权限添加上

        :param role_type: 只能是globalRoles或projectRoles或slaveRoles
        :param role_name: 角色名称
        :param permissions: 角色ID,多个角色ID使用 , 号隔开,比如:'hudson.model.Hudson.Read,hudson.model.Computer.Build'
        :param role_pattern: 角色模式,支持正则表达式,当添加的是项目角色时需要指定
        :param overwrite: 如果新增的权限已经存在是否覆盖,如果选择不覆盖,即使权限已经存在,也不会返回任何报错
        :return:
        """
        if role_type not in ('globalRoles', 'projectRoles', 'slaveRoles'):
            raise AttributeError("role_type必须是'globalRoles', 'projectRoles', 'slaveRoles' 其中一个")

        if role_type in ('projectRoles', 'slaveRoles') and not role_pattern:
            raise AttributeError("如果增加项目权限或节点权限,必须指定role_pattern,否则将匹配 .* ")

        role_data = {
            "type": role_type,
            "roleName": role_name,
            "permissionIds": permissions,
            "overwrite": overwrite,
            "pattern": role_pattern
        }

        headers = self.get_crumb()

        res = requests.post(f'{self.proto}://{self.host}:{self.port}/role-strategy/strategy/addRole', data=role_data,
                            headers=headers, auth=(self.username, self.pwd_or_token))
        return res.status_code

    def get_role(self, role_type, role_name):
        """
        获取指定角色的详细,返回结果示例:
        {'permissionIds': {'hudson.model.Computer.Build': True}, 'sids': ['admin']}
        :param role_type:
        :param role_name:
        :return:
        """
        if role_type not in ('globalRoles', 'projectRoles', 'slaveRoles'):
            raise AttributeError("role_type必须是'globalRoles', 'projectRoles', 'slaveRoles' 其中一个")

        params = {
            "type": role_type,
            "roleName": role_name
        }

        res = requests.get(f'{self.proto}://{self.host}:{self.port}/role-strategy/strategy/getRole',
                           params=params, auth=(self.username, self.pwd_or_token))
        return res.json()

    def remove_roles(self, role_type, role_names: str):
        """
        删除权限
        :param role_type:
        :param role_names: 多个角色用 , 号隔开
        :return:
        """
        if role_type not in ('globalRoles', 'projectRoles', 'slaveRoles'):
            raise AttributeError("role_type必须是'globalRoles', 'projectRoles', 'slaveRoles' 其中一个")

        data = {
            'type': role_type,
            'roleNames': role_names
        }

        headers = self.get_crumb()

        res = requests.post(f'{self.proto}://{self.host}:{self.port}/role-strategy/strategy/removeRoles', data=data,
                            headers=headers, auth=(self.username, self.pwd_or_token))
        return res.status_code

    def assign_role(self, role_type, role_name, sid):
        """
        将某个角色赋予某个用户
        注意:如果赋予用户某个不存在的权限也不会报错
        :param role_type:
        :param role_name: (单个角色)
        :param sid: 用户名称(单个用户)
        :return:
        """
        if role_type not in ('globalRoles', 'projectRoles', 'slaveRoles'):
            raise AttributeError("role_type必须是'globalRoles', 'projectRoles', 'slaveRoles' 其中一个")

        data = {
            'type': role_type,
            'roleName': role_name,
            'sid': sid
        }

        headers = self.get_crumb()

        res = requests.post(f'{self.proto}://{self.host}:{self.port}/role-strategy/strategy/assignRole', data=data,
                            headers=headers, auth=(self.username, self.pwd_or_token))
        return res.status_code

    def delete_roles_from_sid(self, role_type, sid):
        """
        删除指定用户所有的相关权限
        注意:如果指定了一个不存在的用户,也不会报错
        :param role_type:
        :param sid: 单个用户
        :return:
        """
        if role_type not in ('globalRoles', 'projectRoles', 'slaveRoles'):
            raise AttributeError("role_type必须是'globalRoles', 'projectRoles', 'slaveRoles' 其中一个")

        data = {
            'type': role_type,
            'sid': sid
        }

        headers = self.get_crumb()

        res = requests.post(f'{self.proto}://{self.host}:{self.port}/role-strategy/strategy/deleteSid', data=data,
                            headers=headers, auth=(self.username, self.pwd_or_token))
        return res.status_code

    def unassign_role(self, role_type, role_name, sid):
        """
        删除指定用户的某个权限
        注意:即使指定一个不存在的用户或不存在的role,也不会返回错误
        :param role_type:
        :param role_name:
        :param sid:
        :return:
        """
        if role_type not in ('globalRoles', 'projectRoles', 'slaveRoles'):
            raise AttributeError("role_type必须是'globalRoles', 'projectRoles', 'slaveRoles' 其中一个")

        data = {
            'type': role_type,
            'roleName': role_name,
            'sid': sid
        }

        headers = self.get_crumb()

        res = requests.post(f'{self.proto}://{self.host}:{self.port}/role-strategy/strategy/unassignRole', data=data,
                            headers=headers, auth=(self.username, self.pwd_or_token))
        return res.status_code

    def get_all_roles(self, role_type):
        """
        获取指定类型角色下的所有角色以及角色下的用户
        返回结果示例:{"p1":[],"p2":["zm"],"test":["zm"]}
        :param role_type:
        :return:
        """
        if role_type not in ('globalRoles', 'projectRoles', 'slaveRoles'):
            raise AttributeError("role_type必须是'globalRoles', 'projectRoles', 'slaveRoles' 其中一个")

        params = {
            "type": role_type
        }

        res = requests.get(f'{self.proto}://{self.host}:{self.port}/role-strategy/strategy/getAllRoles',
                           params=params, auth=(self.username, self.pwd_or_token))
        return res.json()

 

Python调用STS临时授权访问OSS


2020年8月12日 20:33:45   3,064 次浏览

OSS可以通过阿里云STS(Security Token Service)进行临时授权访问。通过STS,您可以为第三方应用或子用户(即用户身份由您自己管理的用户)颁发一个自定义时效和权限的访问凭证。

实现逻辑:前端用户点击上传图片, 前端向后端发起请求,后端返回一个签名后的url ,前端拿到签名后url再进行用Put方法

调用OSS的SDK时报“SignatureDoesNotMatch”错误, 由于前端直接向签名url put 存在跨域问题,需要把oss的跨域给打开, 使用:set_oss_cors 方法

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File  : sts_token.py
# @Author: 往事随风
# @Email: gujiwork@outlook.com
# @Date  : 2020/12/24
# @Desc  :
# pip install aliyun-python-sdk-sts
# pip install oss2
from aliyunsdkcore import client
from aliyunsdksts.request.v20150401 import AssumeRoleRequest
import json
import oss2
import requests
from oss2.models import BucketCors, CorsRule


class AliStsGenerateToKey:
    def __init__(self, endpoint, access_key_id, access_key_secret, bucket_name):
        """
        :param endpoint: 地域
        :param access_key_id: ram子用户key
        :param access_key_secret: ram子用户 secret
        :param bucket_name: oss bucket名称
        """
        self.endpoint = endpoint
        self.access_key_id = access_key_id
        self.access_key_secret = access_key_secret
        self.bucket_name = bucket_name

    @staticmethod
    def generate_sts_key(self, role_arn):
        object_name = ''
        # 设置sts token获取的权限策略, action及resource
        policy_text = '{"Statement": [{"Action": ["*"],"Effect": "Allow","Resource": ["*"]}],"Version":"1"}'
        # policy_text = '{"Statement": [{"Action": ["oss:GetObject"],"Effect": "Allow","Resource": ["acs:oss:*:*:oss-test/*"]}],"Version":"1"}'
        clt = client.AcsClient(self.access_key_id, self.access_key_secret, 'cn-hangzhou')
        req = AssumeRoleRequest.AssumeRoleRequest()
        req.set_accept_format('json')
        req.set_RoleArn(role_arn)
        # 设置会话名称,审计服务使用此名称区分调用者
        req.set_RoleSessionName('test')
        req.set_Policy(policy_text)
        body = clt.do_action_with_exception(req)

        return body

    def set_oss_sign(self, role_arn, save_oss_file_name, exp_time, upload_file):
        """
        :param role_arn: 角色的资源名称
        :param save_oss_file_name:  保存到oss目录/文件名  settlement_excel_download/20200812200526.png
        :param exp_time: 过期时间
        :param upload_file: 本地上传文件名
        :return: 签名后的url地址
        """
        body = AliStsGenerateToKey.generate_sts_key(self, role_arn=role_arn)

        # 使用RAM账号的AccessKeyId和AccessKeySecret向STS申请临时token。
        token = json.loads(oss2.to_unicode(body))

        # 使用临时token中的认证信息初始化StsAuth实例。
        auth = oss2.StsAuth(token['Credentials']['AccessKeyId'],
                            token['Credentials']['AccessKeySecret'],
                            token['Credentials']['SecurityToken'])
        # 使用StsAuth实例初始化存储空间。
        bucket = oss2.Bucket(auth, self.endpoint, self.bucket_name)

        sign_url_put = bucket.sign_url('PUT', save_oss_file_name, exp_time)
        print(sign_url_put)
        rsp = requests.put(url=sign_url_put, data=open(upload_file, 'rb'))
        if rsp.status_code == 200:
            print('请求上传文件返回状态码: 200')

        file_exists = bucket.object_exists(key=save_oss_file_name)
        if file_exists:
            sign_url = bucket.sign_url('GET', save_oss_file_name, exp_time)
            print(sign_url)
            if requests.get(url=sign_url).status_code != 200:
                return False, '设置签名未生效或已过期, 请检查oss是否设置跨域、 oss权限及sts权限设置是否正确、过期策略时间!'
            return True, sign_url
        print('文件上传失败!')
        return False, '文件上传失败!'


class AliOssCors:
    def __init__(self, access_key_id, access_key_secret, endpoint, bucket_name):
        self.access_key_id = access_key_id
        self.access_key_secret = access_key_secret
        self.endpoint = endpoint
        self.bucket_name = bucket_name
        self.auth = oss2.Auth(self.access_key_id, self.access_key_secret)

    def get_oss_cors(self):
        bucket = oss2.Bucket(self.auth, self.endpoint, self.bucket_name)
        try:
            cors = bucket.get_bucket_cors()
        except oss2.exceptions.NoSuchCors:
            print('OSS未设置跨域! 开始设置跨域----->')
            AliOssCors.set_oss_cors(self)
            AliOssCors.get_oss_cors(self)
        else:
            print('获取跨域规则----->')
            for rule in cors.rules:
                print('AllowedOrigins={0}'.format(rule.allowed_origins))
                print('AllowedMethods={0}'.format(rule.allowed_methods))
                print('AllowedHeaders={0}'.format(rule.allowed_headers))
                print('ExposeHeaders={0}'.format(rule.expose_headers))
                print('MaxAgeSeconds={0}'.format(rule.max_age_seconds))

    @staticmethod
    def set_oss_cors(self):
        bucket = oss2.Bucket(self.auth, self.endpoint, self.bucket_name)
        rule = CorsRule(allowed_origins=['*'],
                        allowed_methods=['GET', 'HEAD', 'PUT', 'POST'],
                        allowed_headers=['*'],
                        max_age_seconds=0)
        # 注意:如果已存在的规则将会被覆盖。
        bucket.put_bucket_cors(BucketCors([rule]))

    def delete_oss_cors(self):
        bucket = oss2.Bucket(self.auth, self.endpoint, self.bucket_name)
        bucket.delete_bucket_cors()


oss_obj = AliOssCors(
    access_key_id='xxxxxxxxxxxxxxxxx',
    access_key_secret='xxxxxxxxxxxxxxxxx',
    endpoint='oss-cn-hangzhou.aliyuncs.com',
    bucket_name='xxxxxxxxxxxxxxxxx')
oss_obj.get_oss_cors()


p = AliStsGenerateToKey(
    endpoint='oss-cn-hangzhou.aliyuncs.com',
    access_key_id='xxxxxxxxxxxxxxxxx',
    access_key_secret='xxxxxxxxxxxxxxxxx',
    bucket_name='xxxxxxxxxxxxxxxxx')
p.set_oss_sign(
    role_arn='acs:ram::{xxxxxxxxxxxxxxxxx}',
    save_oss_file_name='test.png',
    exp_time=300,
    upload_file='22.png'  # 本地图片
)

 

STS中临时授权时出现“You are not authorized to do this action. You should be authorized by RAM“报错

代码中使用的AccessKey和AccessKeySecret是主账号的,并非RAM用户的。 必须要创建子帐号才key才可以

 

https://help.aliyun.com/document_detail/100624.html?spm=a2c4g.11186623.2.10.5e474529lXELjN#concept-xzh-nzk-2gb

https://help.aliyun.com/document_detail/28798.html?spm=a2c4g.11186623.2.10.29bc203dUpOWmQ#reference-smb-tzy-xdb

https://help.aliyun.com/document_detail/32033.html?spm=a2c4g.11186623.2.23.64e33b49Q6fLpK#section-zx1-55k-kfc

python监控进程内存和cpu使用情况


2020年4月25日 15:50:00   9,814 次浏览

用Python来编写脚本简化日常的运维工作是Python的一个重要用途。在Linux下,有许多系统命令可以让我们时刻监控系统运行的状态,如ps,top,free等等。要获取这些系统信息,Python可以通过subprocess模块调用并获取结果。但这样做显得很麻烦,尤其是要写很多解析代码。

接下来我们利用psutil来监控进程的cpu和内存负载,并将监控的数据定时push到监控系统中,这样就很方便我们每天登录到监控平台去查看业务进程的情况。

pip install psutil
# 监控代码如下

#!/usr/bin/env python
# -*- coding:utf-8 -*-

"""
@Author: 风哥
@Mail: gujiwork@outlook.com
@File:MonitorProcess.py
@Time:2020/4/24 22:32
"""

import psutil
import re
import requests
import time
import json

MONITOR_API_URL = "http://localhost:2058/api/collector/push"
APP_NAME = "Dzookeeper.log.dir=/data/apps/zookeeper-3.5.3-beta_2/bin/"
MONITOR_TAG_NAME = "trip-corp-web"
TIME_INTERVAL = 20


class MonitorProcessInfo(object):
    pid_number = None
    process = None

    @classmethod
    def __init__(cls, process_name):
        """
        Get process ID number according to process name
        :param process_name:
        """
        pids = psutil.process_iter()

        for pid in pids:

            # Find PID by process name
            '''
            str_pid = str(pid)
            f = re.compile(process_name, re.I)
            if f.search(str_pid):
                global pid_number, process
                pid_number = int(str_pid.split('pid=')[1].split(',')[0])
                process = psutil.Process(pid_number)
            '''

            # Find PID based on process name CmdLine
            if process_name in ''.join(pid.cmdline()):
                global pid_number, process
                pid_number = pid.pid
                process = psutil.Process(pid_number)

    @classmethod
    def process_memory(cls):
        """
        Get process memory usage
        :return:
        """
        process_memory_percent = process.memory_percent()
        process_memory_info = process.memory_info()

        return process_memory_percent, process_memory_info.rss

    @classmethod
    def process_cpu(cls):
        """
        Get the CPU usage of the process
        :return:
        """
        process_cpu_percent = process.cpu_percent(interval=1.0)

        return process_cpu_percent

    @classmethod
    def process_io(cls):
        """
        Get process IO status
        :return:
        """
        process_io_count = process.io_counters()

        return process_io_count.read_bytes, process_io_count.write_bytes

    @classmethod
    def process_threads(cls):
        """
        Get Process Threads
        :return:
        """
        process_threads = process.num_threads()
        return process_threads


class PushMonitorProcessInfo(MonitorProcessInfo):

    def __init__(self, process_name, tag_name):
        """Monitoring indicators
            - memory_rss :      Memory size used by process
            - memory_percent:   Percentage of process memory used
            - process_threads:  Number of process threads
            - io_read_bytes:    Process IO read operation
            - io_write_bytes:   Process IO write operation
            - cpu_percent:      Percentage of CPU used by process

        :param process_name:
        :param tag_name:
        """
        # py3 super
        # super().__init__(process_name)

        """When super inherits the parent class in py2, the parent class needs to add the object attribute, 
        MonitorProcessInfo (object), otherwise it will be thrown incorrectly
        TypeError: must be type, not classobj"""
        super(PushMonitorProcessInfo, self).__init__(process_name)

        cpu_percent = PushMonitorProcessInfo.process_cpu()
        memory_percent, memory_rss = PushMonitorProcessInfo.process_memory()
        io_read_bytes, io_write_bytes = PushMonitorProcessInfo.process_io()
        thread_number = PushMonitorProcessInfo.process_threads()

        pro_data = {
            "memory_rss": memory_rss,
            "memory_percent": memory_percent,
            "io_read_bytes": io_read_bytes,
            "io_write_bytes": io_write_bytes,
            "cpu_percent": cpu_percent,
            "threads": thread_number
        }
        payload = []
        t = int(time.time())
        for k, v in pro_data.items():
            metric_data = {
                "metric": "process.%s" % k,
                "endpoint": "10.86.12.13",
                "tags": "tomcat_name=%s" % tag_name,
                "value": int(v),
                "timestamp": t,
                "step": TIME_INTERVAL
            }

            payload.append(metric_data)

        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \
                                        (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36",
            "Content-Type": "application/json"
        }

        rsp = requests.post(url=MONITOR_API_URL, data=json.dumps(payload), headers=headers)
        print(rsp.text)


while True:
    push_data = PushMonitorProcessInfo(APP_NAME, MONITOR_TAG_NAME)
    time.sleep(TIME_INTERVAL)

脚本以死循环方式每隔20秒将数据上报到监控平台,登陆监控平台就可以很愉快的查看图表了

Pandas读取表格行数据判断是否相同


2020年3月11日 00:00:00   1,671 次浏览

描述: 下午快下班的时候公司供应链部门的同事跑过来问我能不能以程序的方法帮他解决一些excel表格每周都需要手工重复做的事情,Excel 是数据处理最常用的办公工具对于市场、运营都应该很熟练。哈哈,然而程序员是不怎么会用excel的。下面给大家介绍一下pandas,  Pandas是一个强大的分析结构化数据的工具集;它的使用基础是Numpy(提供高性能的矩阵运算);用于数据挖掘和数据分析,同时也提供数据清洗功能。

 

具体需求:

找出相同的数字,把与数字对应的英文字母合并在一起。

期望最终生成值:

TAOCGQ    I 1

TAOCGQ   WY 2

TAOCGQ   BHLPQGRN  3

TAOCGQ    VUE 4

TAOCGQ     Z 5

代码实现:

import pandas as pd
df = pd.read_excel(r'E:\代码\tables_results\价格表.xlsx')
pd_result = df.to_dict(orient='split')
'''舱位信息列表['航线', 'W', 'I', 'Y', 'B', 'H', 'L', 'P', 'Q', 'G', 'V', 'U', 'Z', 'R', 'N', 'E', 'K']'''
cabin_list = pd_result['columns']
'''航线信息及价格列表 [['LHW-ZUH', 2.0, 0.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 2.0, 2.0, 2.0, 0] ]'''
data_lists = pd_result['data']
for data in data_lists:
    for number in range(0,20):
        ''' 循环航线信息取出列表的索引id'''
        idxs = [i for i, x in enumerate(data) if x == number]
        if idxs: #索引列表
            news_data_list = []
            for idx in idxs: #循环索引列表
                news_data_list.append(cabin_list[idx])
            cabin_merge = ''.join(news_data_list) # 将多个舱位合并
            aviation_rebate = idxs[0] # 取出返点价格,因为有多个索引,只取第一个就行
            air_route = str(data[0]).split('-') #航线 LHW-ZUH
            dpt = air_route[0]  # 出发城市
            arr = air_route[1]  # 到达城市
            result = dpt + arr + ',' + cabin_merge + ',' + str(int(data[aviation_rebate]))
            '''
            最终结果:
            ['LHWZUH', 'IK', '0']
            ['LHWZUH', 'WYBHRNE', '2']
            ['LHWZUH', 'LPQGVUZ', '3']
            ['ZUHLHW', 'IK', '0']
            '''
            print(result.split(','))

程序执行结果:

['TAOCGQ', 'I', '1']

['TAOCGQ', 'WY', '2']

['TAOCGQ', 'BHLPQGRN', '3']

['TAOCGQ', 'VUE', '4']

['TAOCGQ', 'Z', '5']

 

django web界面管理nginx配置


2019年12月14日 13:58:44   1,443 次浏览

1、安装Nginx

yum install -y nginx (centos)

apt install -y nginx (debian)

 

也可以使用tengine来代替nginx,以下是通过limit_conn_zone 和limit_req_zone两个组件来对客户端访问频率和次数进行限制,具体如何使用要结合公司业务环境进行配置。善用此模块能够对 cc、ddos爬虫等此类的攻击进行有效的防御。

upstream {{ ups_name }} { {% for up in ups %}
   server {{ up }};{% endfor %}
}
limit_req_zone $binary_remote_addr zone=one:3m rate=10r/s;
limit_req_zone $binary_remote_addr $uri zone=two:3m rate=10r/s;
limit_req_zone $binary_remote_addr $request_uri zone=three:3m rate=10r/s;
limit_req_zone $binary_remote_addr $request_uri zone=four:3m rate=$limit_count;
limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
server {
   listen {{ server_port }} ;
   server_name {{ server_name }} ;
   error_page      404  /errors/404.html;
   error_page      403  /errors/403.html;
   location / {
       proxy_redirect off;
       proxy_pass http://{{ ups_name }};
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       #add_header Cache-Control no-cache;
       #expires -1;
       # 限制连接数,同一个ip最大连接数20
       limit_conn conn_limit_per_ip 20;
       # 限流,同一个ip每秒最大请求数10次
       limit_req zone=one burst=5;
       limit_req zone=two forbid_action=@403;
       limit_req zone=three burst=3 forbid_action=@test2;
       set $limit_count "10r/s";
       if ($http_user_agent ~* "Android") {
           set $limit_count "1r/s";
       }
       if ($http_user_agent ~* "Iphone") {
           set $limit_count "100r/s";
       }
       limit_req zone=four burst=3 forbid_action=@test2;
    }
   location @403 {
        rewrite ^ /403.html;
   }
   location @test2 {
        rewrite ^ /test2.html;
   }
 }
}

2、 克隆git项目nginx-proxy-manager

git clone https://github.com/gujiwork/nginx-proxy-manager.git

3 、检查你的python版本

python -V or python3 -V

4 、初始化应用

"bash config/init.sh" or "sudo sh config/init.sh"

5、 运行项目

python manage.py runserver


web http://127.0.0.1:8000/nginx/index/

6、 从web管理界面编辑nginx.conf配置文件

add "include /etc/nginx/sites-enabled/*.conf;" in http{ } config

append in end

"stream { include /etc/nginx/sites-enabled/*.stream; }"

 

验证配置文件是否生成到本地磁盘

cat /etc/nginx/sites-enabled/ngx_H72E4ABX.conf

Python运算符


2019年7月22日 17:07:54   1,224 次浏览
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time
#定义一个由很多内容的列表
infos = ["陈浩东", "虞舜", "公孙策", "王不二", "林雨", "韩寒"]
name = "韩寒"
start = time.time()
for i in infos:
    if name == i:
        print("数据查找到了")
        end = time.time()
        print(end - start)
# 程序运行结果
# 数据查找到了
# 韩寒
# 0.0004734992980957031
#上面的这种查找效率很低,需要循环迭代多次比较耗时,python提供了内部的运算符in(在之内) Not in (不在之内)
start = time.time()
if name in infos:
    print("数据可以查找到")
    end = time.time()
    print(end - start)
else:
    print("没有匹配的信息")
# 程序运行结果
# 数据可以查找到
# 0.00026488304138183594

如果有大量数据存在判断的时候一定要使用in运算符,这样可以保证程序的执行性能,效果也较为显著

Python列表操作函数


2019年4月30日 18:32:00   1,216 次浏览

列表可以进行多个数据的存储,同时python中的列表设计非常到位,它可以实现内容动态扩充,可以进行后期数据的删除,这些就需要通过Python提供的列表操作函数来实现了。
对于Python语言而言,开发者肯定要记住一些常用的函数,同事对于一些不常用的函数需要自己进行文档的查看

列表操作函数


append(data)    在列表最后追加新内容
clear()         清除列表数据
copy()          列表拷贝
count()         统计某一个数据在列表中的出现次数
extend(列表)    为一个列表追加另外一个列表
index(data)     从列表查询某个值第一次出现的位置
insert(index,data)  想列表中指定索引位置追加新数据
pop(index)      从列表弹出并删除一个数据
remove(data)    从列表删除数据
reverse()       列表数据反转
sort()          列表数据排序

#空的列表信息

infos = []
print("初始化信息: %d" % len(infos))
infos.append("风哥")
infos.insert(0,"www.guji.work")
print("数据追加后的列表长度: %d, 列表内容: %s" % (len(infos),infos))


程序运行结果:

初始化信息: 0
数据追加后的列表长度: 2, 列表内容: ['www.guji.work', '风哥']

使用append()函数是在列表的最后进行追加的,而insert()是在指定的索引位置上进行添加,添加之后其它的数据向后移动。建议使用append()函数进行内容的追加,这样可以保证数据的顺序。

在进行列表数据追加的时候,还可以追加一个新的列表内容,这个就称为列表的扩充。

#空的列表信息

infos = []
print("初始化列表长度: %d, 地址: %d " % (len(infos),id(infos)))
infos.append("风哥")
infos.extend(["夏丹"])
print("数据追加后的列表长度:%d, 地址: %d, 列表内容:%s " % (len(infos),id(infos),infos))


程序运行结果:

数据追加后的列表长度:2, 地址: 140331673610632, 列表内容:['风哥', '夏丹']

在进行列表数据扩充的操作里面的确是在一个内存空间里面完成的所有功能。列表操作的时候还提供一个列表的拷贝支持,直接使用copy()函数就可以使用当前列表的内容创建新的列表

msg = infos.copy()
print("infos列表的保存地址编号: %d, 列表内容: %s" % (id(infos),infos))
print("msg列表的保存地址编号:%d, 列表内容:%s" %(id(msg),msg))


程序运行结果:

infos列表的保存地址编号: 140200931293704, 列表内容: ['风哥', 'www.guji.work', 'www.baidu.com']
msg列表的保存地址编号:140200931293576, 列表内容:['风哥', 'www.guji.work', 'www.baidu.com']

此时创建了两个不同的内存空间,同时两个空间的内容是完全相同的。

列表除了可以进行内容的扩充之外,也可以执行删除操作,列表里面提供有上传函数的支持,remove()在进行删除的时候是根据内容进行的数据删除

remove()操作需要根据内容删除,并且没有返回值。在使用remove()函数删除的时候内容存在可以删除,不存在就会抛出”ValueError: list.remove(x): x not in list”异常信息,所以如果要使用这个函数操作之前一定要使用in进行判断

infos = ["风哥", "www.guji.work"]
print("数据删除前的列表内容:%s" %(infos))
print("执行数据删除remove()函数 %s" % infos.remove("风哥"))
print("数据删除后的列表内容: %s" % (infos))


程序执行结果:

数据删除前的列表内容:['风哥', 'www.guji.work']
执行数据删除remove()函数 None
数据删除后的列表内容: ['www.guji.work']

如果不知道内容要进行数据的删除,最简单的原始的python支持可以采用del关键字实现内容删除,而且使用del删除的时候只需要知道列表数据的索引即可实现

infos = ["风哥", "www.guji.work"]
print("数据删除前的列表内容:%s" %(infos))
del infos[1]
print("数据删除后的列表内容: %s" % (infos))


程序执行结果为:

数据删除前的列表内容:['风哥', 'www.guji.work']
数据删除后的列表内容: ['风哥']

使用del关键字可以实现索引的删除,但是无法知道被删除了哪些数据,在整个的删除操作里面,最方便的删除是根据索引删除,而后可以告诉用户哪些数据被删除了,这样的功能就是pop函数

infos = ["风哥", "www.guji.work"]
print("数据删除前的列表内容:%s" %(infos))
print("执行数据删除pop()函数: %s " % infos.pop(1))
print("数据删除后的列表内容: %s" % (infos))


程序执行结果:

数据删除前的列表内容:['风哥', 'www.guji.work']
执行数据删除pop()函数: www.guji.work
数据删除后的列表内容: ['风哥']

使用pop()函数表示的是一个弹出的形式,从列表里面根据索引弹出,弹出的同时也就表示内容的删除了。但是需要清楚一个问题,每当列表之中弹出一个数据之后实际上就都会发生索引变更。

infos = ["风哥", "www.guji.work"]
print("数据删除前的列表内容:%s" %(infos))
print("执行数据删除pop()函数: %s " % infos.pop(1))
print("执行数据删除pop()函数: %s " % infos.pop(1))
print("数据删除后的列表内容: %s" % (infos))


程序执行结果:

Traceback (most recent call last):
 File "/tmp/pycharm_project_549/数据分片/列表操作函数.py", line 50, in <module>
   print("执行数据删除pop()函数: %s " % infos.pop(1))
IndexError: pop index out of range




示例二:可以弹0,到最后列表内容为空

infos = ["风哥", "www.guji.work"]
print("数据删除前的列表内容:%s" %(infos))
print("执行数据删除pop()函数: %s " % infos.pop(0))
print("执行数据删除pop()函数: %s " % infos.pop(0))
print("数据删除后的列表内容: %s" % (infos))


程序执行结果:

数据删除前的列表内容:['风哥', 'www.guji.work']
执行数据删除pop()函数: 风哥
执行数据删除pop()函数: www.guji.work
数据删除后的列表内容: []

知识点:

列表中的数据是保存顺序的,列表中所有数据的内容采用的是FIFO(先进先出)默认的顺序操作的,利用列表中的append()和pop()函数就可以方便的实现这样的先进先出的功能。

infos = []
for item in range(10):
   infos.append("风哥 - %d" % item)
print("列表初始化内容: %s" %infos)
print("列表数据弹出处理:")

for item in range(len(infos)):
   print("列表数据弹出: %s" % infos.pop(0))


# 索引会改变,但是从头开始弹出





程序执行结果:

列表初始化内容: ['风哥 - 0', '风哥 - 1', '风哥 - 2', '风哥 - 3', '风哥 - 4', '风哥 - 5', '风哥 - 6', '风哥 - 7', '风哥 - 8', '风哥 - 9']
列表数据弹出处理:
列表数据弹出: 风哥 - 0
列表数据弹出: 风哥 - 1
列表数据弹出: 风哥 - 2
列表数据弹出: 风哥 - 3
列表数据弹出: 风哥 - 4
列表数据弹出: 风哥 - 5
列表数据弹出: 风哥 - 6
列表数据弹出: 风哥 - 7
列表数据弹出: 风哥 - 8
列表数据弹出: 风哥 - 9

 

hyperledger-fabric v1.2 身份验证


2018年9月18日 14:12:54   1,450 次浏览

什么是身份?

区块链网络中的不同参与者包括同伴,订购者,客户端应用程序,管理员等。这些参与者中的每一个 – 网络内部或外部能够使用服务的活动元素 – 都具有封装在X.509数字证书中的数字身份。这些身份确实很重要,因为它们确定了对资源的确切权限以及对参与者在区块链网络中拥有的信息的访问权限。

此外,数字身份还具有Fabric用于确定权限的一些其他属性,并且它为身份和关联属性的并集提供了特殊名称 – 主体。Principal就像userIDs或groupIDs,但更灵活一点,因为它们可以包含actor的身份的各种属性,例如actor的组织,组织单位,角色甚至是actor的特定身份。当我们谈论主体时,它们是决定其权限的属性。

要使身份可以验证,它必须来自可信任的权威机构。一成员的服务提供商 (MSP)是这是如何实现的面料。更具体地说,MSP是定义管理该组织的有效身份的规则的组件。Fabric中的默认MSP实现使用X.509证书作为身份,采用传统的公钥基础结构(PKI)分层模型(稍后将详细介绍PKI)。

一个简单的场景来解释身份的使用

想象一下,你去超市购买一些杂货。在结账时,您会看到一个标志,表示只接受Visa,Mastercard和AMEX卡。如果您尝试使用其他卡付款 – 我们称之为“ImagineCard” – 无论该卡是否真实且您的帐户中有足够的资金都无关紧要。它不会被接受。

脚本

拥有有效的信用卡是不够的 – 它也必须被商店接受!PKI和MSP以相同的方式协同工作 – PKI提供身份列表,MSP说哪些是参与网络的给定组织的成员。

PKI证书颁发机构和MSP提供了类似的功能组合。PKI就像一个卡提供商 – 它分配了许多不同类型的可验证身份。另一方面,MSP类似于商店接受的卡提供商列表,确定哪些身份是商店支付网络的可信成员(参与者)。MSP将可验证的身份转变为区块链网络的成员

让我们更详细地深入研究这些概念。

什么是PKI?

公钥基础结构(PKI)是一组互联网技术,可在网络中提供安全通信。这是PKI是放小号HTTPS -如果你正在阅读的网页浏览器这个文档,你可能会使用PKI来确保它来自一个验证源。

PKI

公钥基础结构(PKI)的元素。PKI由向各方(例如,服务的用户,服务提供商)发布数字证书的证书颁发机构组成,然后他们使用它们在与其环境交换的消息中对自己进行身份验证。CA的证书吊销列表(CRL)构成不再有效的证书的参考。证书的撤销可能由于多种原因而发生。例如,证书可能被撤销,因为与证书相关联的加密私有材料已被公开。

虽然区块链网络不仅仅是一个通信网络,但它依赖于PKI标准来确保各个网络参与者之间的安全通信,并确保在区块链上发布的消息得到适当的认证。因此,了解PKI的基础知识以及为什么MSP如此重要是非常重要的。

PKI有四个关键要素:

  • 数字证书
  • 公钥和私钥
  • 证书颁发机构
  • 证书撤销列表

数字证书

数字证书是包含与证书持有者有关的一组属性的文档。最常见的证书类型是符合X.509标准的证书,它允许在其结构中编码一方的识别细节。

例如,玛丽·莫里斯米切尔汽车的制造部在密歇根州底特律可能有一个带有数字证书SUBJECT的属性C=USST=MichiganL=Detroit,,,。玛丽的证书类似于她的政府身份证 – 它提供了玛丽的信息,她可以用来证明关于她的重要事实。X.509证书中还有许多其他属性,但现在让我们专注于这些。O=Mitchell CarsOU=ManufacturingCN=Mary Morris /UID=123456

DigitalCertificate

描述一个名为Mary Morris的派对的数字证书。Mary是SUBJECT证书的副本,突出显示的SUBJECT文本显示了关于Mary的重要事实。如您所见,证书还包含更多信息。最重要的是,Mary的公钥是在她的证书中分发的,而她的私人签名密钥则不是。此签名密钥必须保密。

重要的是,玛丽的所有属性都可以使用称为密码学(字面意思,“ 秘密写作 ”)的数学技术进行记录,这样篡改将使证书无效。只要对方信任证书颁发者(称为证书颁发机构(CA)),密码学就允许Mary将证书提交给其他人以证明其身份。只要CA安全地保存某些加密信息(意味着它自己的私人签名密钥),任何阅读证书的人都可以确定有关Mary的信息没有被篡改 – 它将始终具有Mary Morris的特定属性。将Mary的X.509证书视为无法改变的数字身份证。

身份验证,公钥和私钥

身份验证和消息完整性是安全通信中的重要概念。身份验证要求确保交换消息的各方创建特定消息的身份。对于具有“完整性”的消息意味着在其传输期间不能被修改。例如,您可能希望确保与真正的玛丽莫里斯而不是模仿者进行沟通。或者,如果Mary向您发送了一条消息,您可能希望确保其在传输过程中没有被其他任何人篡改过。

传统的身份验证机制依赖于数字签名,顾名思义,它允许一方对其消息进行数字签名。数字签名还可以保证签名消息的完整性。

从技术上讲,数字签名机制要求每一方保存两个加密连接的密钥:广泛可用的公钥和充当认证锚的私钥,以及用于在消息上产生数字签名的私钥 。数字签名消息的接收者可以通过检查附加签名在预期发送者的公钥下是否有效来验证接收消息的来源和完整性。

私钥和相应公钥之间的唯一关系是使安全通信成为可能的加密魔法。密钥之间的唯一数学关系使得私钥可以用于在仅对应的公钥可以匹配的消息上产生签名,并且仅在相同的消息上。

AuthenticationKeys

在上面的示例中,Mary使用她的私钥对邮件进行签名。任何使用她的公钥查看签名消息的人都可以验证签名。

证书颁发机构

如您所见,演员或节点能够通过由系统信任的机构为其发布的数字身份参与区块链网络。在最常见的情况下,数字身份(或简称身份)具有符合X.509标准并由证书颁发机构(CA)颁发的经加密验证的数字证书的形式。

CA是互联网安全协议的常见部分,您可能已经听说过一些比较流行的协议:Symantec(最初是Verisign),GeoTrust,DigiCert,GoDaddy和Comodo等。

CertificateAuthorities

证书颁发机构向不同的参与者分发证书。这些证书由CA进行数字签名,并将actor与actor的公钥绑定在一起(并且可选地具有全面的属性列表)。因此,如果一个人信任CA(并且知道其公钥),则可以信任特定参与者绑定到证书中包含的公钥,并通过验证参与者证书上的CA签名来拥有所包含的属性。 。

证书可以广泛传播,因为它们既不包括演员也不包括CA的私钥。因此,它们可以用作信任的锚,用于验证来自不同参与者的消息。

CA也有一个证书,它们可以广泛使用。这允许由给定CA发布的身份的消费者通过检查证书只能由相应私钥(CA)的持有者生成来验证它们。

在区块链设置中,希望与网络交互的每个参与者都需要一个身份。在此设置中,您可能会说一个或多个CA可用于从数字角度定义组织的成员。CA是为组织的参与者提供可验证的数字身份的基础。

根CA,中间CA和信任链

CA的有两种形式:根CA中间CA。因为Root CA(赛门铁克,Geotrust等)必须安全地向互联网用户分发数亿个证书,所以将这个过程分散到所谓的中间CA中是有意义的。。这些中间CA具有由根CA或其他中间机构颁发的证书,允许为链中的任何CA颁发的任何证书建立“信任链”。追溯到根CA的这种能力不仅允许CA的功能在仍然提供安全性的同时进行扩展 – 允许使用证书的组织充满信心地使用中间CA–它限制了根CA的暴露,如果受到损害,它将会危及整个信任链。另一方面,如果中级CA受到损害,则曝光量会小得多。

ChainOfTrust

只要每个中间CA的证书的颁发CA是根CA本身或具有对根CA的信任链,就在根CA和一组中间CA之间建立信任链。

中间CA在跨多个组织颁发证书时提供了巨大的灵活性,这在许可的区块链系统(如Fabric)中非常有用。例如,您将看到不同的组织可能使用不同的根CA,或者使用具有不同中间CA的相同根CA – 它确实取决于网络的需求。

Fabric CA.

这是因为CA非常重要,Fabric提供了一个内置的CA组件,允许您在您构成的区块链网络中创建CA. 此组件(称为Fabric CA)是一个私有根CA提供程序,能够管理具有X.509证书形式的Fabric参与者的数字身份。由于Fabric CA是针对Fabric的根CA需求的自定义CA,因此它本身无法为浏览器中的常规/自动使用提供SSL证书。但是,由于某些 CA必须用于管理身份(即使在测试环境中),因此可以使用Fabric CA来提供和管理证书。使用公共/商业根或中间CA来提供识别也是可能的 – 并且完全合适。

 

证书撤销列表

证书撤销列表(CRL)很容易理解 – 它只是CA知道由于某种原因而被撤销的证书的引用列表。如果您回想一下商店场景,CRL就像被盗信用卡列表一样。

当第三方想要验证另一方的身份时,它首先检查颁发CA的CRL以确保证书尚未被撤销。验证者不必检查CRL,但如果不是,则他们冒着接受受损身份的风险。

CRL

使用CRL检查证书是否仍然有效。如果模仿者试图将受损的数字证书传递给验证方,则可以首先检查颁发CA的CRL,以确保其未列为不再有效。

请注意,被撤销的证书与证书过期非常不同。撤销的证书尚未过期 – 按其他措施,它们是完全有效的证书。