Browse Source

ServiceBoot集成iBoot功能,升级至v2.0.5。

pull/7/head
huolongshe 1 month ago
parent
commit
27fade9370
10 changed files with 21 additions and 287 deletions
  1. +3
    -3
      README.md
  2. +4
    -4
      app/global_data/config.py
  3. +0
    -93
      app/global_data/consul_client.py
  4. +2
    -2
      app/global_data/global_data.py
  5. +0
    -110
      app/global_data/oauth_client.py
  6. +7
    -11
      app/service/blog_service.py
  7. +0
    -53
      app/service/token_service.py
  8. +4
    -8
      application.yml
  9. +0
    -1
      pip-install-reqs.sh
  10. +1
    -2
      requirements.txt

+ 3
- 3
README.md View File

@@ -1,6 +1,6 @@
# CubePy微服务框架——后端微服务样例1(uapp1)

uapp1是基于[ServiceBoot微服务引擎](https://pypi.org/project/serviceboot)开发的[CubePy微服务框架](https://git.openi.org.cn/OpenI/cubepy)后端微服务样例程序,使用Python语言开发。
uapp1是基于 [ServiceBoot微服务引擎](https://git.openi.org.cn/OpenI/cubepy_serviceboot) 开发的 [CubePy微服务框架](https://git.openi.org.cn/OpenI/cubepy) 后端微服务样例程序,使用Python语言开发。

uapp1使用Consul来进行微服务注册和发现。在启动时,它首先尝试与Consul建立连接并在Consul上注册自己。如果Consul未就绪,该微服务将启动失败。

@@ -19,7 +19,7 @@ uapp1通过与papp1和uapp2配合来实现一个简易的博客应用。它从

- 服务注册与发现: Consul(8500)

- 用户认证授权: uaa
- 用户认证授权: [uaa](https://git.openi.org.cn/OpenI/cubepy_uaa)

- 数据库: 无

@@ -36,7 +36,7 @@ uapp1通过与papp1和uapp2配合来实现一个简易的博客应用。它从

3. 使用PyCharm打开本project所在目录。

4. 建议在PyCharm中专门为本project新建一个专用Python虚拟环境,Python版本选择3.5以上(建议Python 3.5.9)
4. 建议在PyCharm中专门为本project新建一个专用Python虚拟环境,Python版本选择3.5以上。

5. 在PyCharm的terminal窗口中执行如下命令安装依赖包:



+ 4
- 4
app/global_data/config.py View File

@@ -8,10 +8,10 @@ class Config:
with open('./application.yml', 'rb') as f:
yml = yaml.load(f, Loader=yaml.SafeLoader)

self.app_name = yml['service']['ename']
self.app_name = yml['serviceboot']['ename']

try:
self.app_version = yml['service']['version']
self.app_version = yml['serviceboot']['version']
except:
self.app_version = '0.0.1'

@@ -21,14 +21,14 @@ class Config:

if self.app_profile == 'dev':
try:
self.server_port = yml['service']['port']['dev']
self.server_port = yml['serviceboot']['port']['dev']
except:
self.server_port = 80
self.consul_address = '127.0.0.1'
self.consul_tags = ['profile-dev']
else:
try:
self.server_port = yml['service']['port']['prod']
self.server_port = yml['serviceboot']['port']['prod']
except:
self.server_port = 80
self.consul_address = 'consul'


+ 0
- 93
app/global_data/consul_client.py View File

@@ -1,93 +0,0 @@
import json
import yaml
import random
import consul
import requests
import logging
import os


class ConsulClient:
def __init__(self, host=None, port=None, token=None):
self.host = host
self.port = port
self.token = token
self.consul = consul.Consul(host=host, port=port)
self.registered = False

def register(self, name, service_id, address, port, tags, http_check_url):
try:
self.consul.agent.service.register(
name,
service_id=service_id,
address=address,
port=port,
tags=tags,
check=consul.Check().http(http_check_url, '10s', '20s', '20s')
)
self.registered = True
logging.critical('Successful registered to Consul: {}:{}'.format(address, port))
except:
self.registered = False

def get_services(self):
return self.consul.agent.services()

def resolve_service(self, name): # 根据服务名解析其“IP地址:端口号”,负载均衡随机取其中之一
# 如果程序运行在K8S中,直接通过kube-dns解析服务名,服务端口号默认为80
if os.environ.get('APP_PROFILE', 'dev').lower() == 'k8s':
return name

url = 'http://{}:{}/v1/catalog/service/{}'.format(self.host, self.port, name)
res = requests.get(url)
if res.status_code != 200:
return None

instances = json.loads(res.text)

dc_list = []
for instance in instances:
dc_list.append(instance.get('Datacenter'))

results = []
for dc in dc_list:
url = 'http://{}:{}/v1/health/service/{}?dc={}&token={}'.format(self.host,self.port, name, dc, self.token if self.token else '')
res = requests.get(url)
if res.status_code != 200:
return None

text = res.text
instances = json.loads(text)

for instance in instances:
status = instance.get('Checks')[0].get('Status')
if status == 'passing':
address = instance.get('Service').get('Address')
port = instance.get('Service').get('Port')
results.append({'port': port, 'address': address})

if len(results) < 1:
return None
else:
result = results[random.randint(0, len(results) - 1)] # 随机获取一个可用的服务实例
return '{}:{}'.format(result['address'], result['port'])

def get_kv(self):
try:
kv_yaml = self.consul.kv.get('config/application/data')[1].get('Value')
return yaml.load(kv_yaml, Loader=yaml.SafeLoader)
except:
return None


if __name__ == "__main__":
consul_client = ConsulClient('127.0.0.1', 8500)
consul_client.register('demo', 'demo-test', '127.0.0.1', 8888, ['profile-dev'], 'http://127.0.0.1:8888/management/health')
if consul_client.registered:
print('Registered in Consul!')
print(consul_client.resolve_service('uaa'))
print(consul_client.resolve_service('demo'))
print(consul_client.get_kv())
else:
print('Not registered in Consul!!!')


+ 2
- 2
app/global_data/global_data.py View File

@@ -1,7 +1,7 @@
import uuid
from serviceboot.consul_client import ConsulClient
from serviceboot.oauth_client import OauthClient
from app.global_data.config import Config
from app.global_data.consul_client import ConsulClient
from app.global_data.oauth_client import OauthClient
import logging




+ 0
- 110
app/global_data/oauth_client.py View File

@@ -1,110 +0,0 @@
import json
import base64
import requests
from datetime import datetime


class OauthClient:
def __init__(self, consul_client):
self.consul_client = consul_client
self.uaa_name = 'uaa'
self.uaa_public_key =None
self.jwt = None
self.jwt_expire = None
self.update_public_key()
self.update_jwt()

def get_uaa_url(self):
return 'http://{}/api/data'.format(self.consul_client.resolve_service(self.uaa_name))

def update_public_key(self):
body = {
'action': 'get_public_key',
'args': {},
}
res = requests.post(url=self.get_uaa_url(), json=body)

if res.status_code == 200:
res = json.loads(res.text)
if res['status'] == 'ok':
self.uaa_public_key = res['value']['value']
else:
self.uaa_public_key = None
else:
self.uaa_public_key = None

return self.uaa_public_key

def update_jwt(self):
headers = {
'Authorization': 'Basic {}'.format(str(base64.b64encode(b'internal:internal'), encoding='utf-8'))
}
body = {
'action': 'get_token',
'args': {
'grant_type': 'client_credentials',
}
}
res = requests.post(url=self.get_uaa_url(), json=body, headers=headers)

if res.status_code == 200:
res = json.loads(res.text)
if res['status'] == 'ok':
self.jwt = res['value'].get('access_token')
self.jwt_expire = get_jwt_expire(self.jwt) # 通过缓存exp来验证jwt是否有效,节省验证时间
else:
self.jwt = None
self.jwt_expire = None
else:
self.jwt = None
self.jwt_expire = None

return self.jwt

def get_public_key(self):
return self.uaa_public_key if self.uaa_public_key is not None else self.update_public_key()

def get_jwt(self):
return self.jwt if not verify_expire(self.jwt_expire) else self.update_jwt()


def get_jwt_expire(jwt):
_, payload, _ = jwt.split('.')
missing_padding = (4 - len(payload) % 4) % 4
payload += '=' * missing_padding
try:
return json.loads(str(base64.b64decode(payload.encode()), encoding='utf-8')).get('exp')
except:
return None


def verify_expire(exp):
if exp is None:
return True
now = int(datetime.now().timestamp())
return now + 5 > exp


# 暂时不用该函数,用verify_expire代替,以节省验证时间
def verify_jwt_expire(jwt):
_, payload, _ = jwt.split('.')
missing_padding = (4 - len(payload) % 4) % 4
payload += '=' * missing_padding
try:
exp = json.loads(str(base64.b64decode(payload.encode()), encoding='utf-8')).get('exp')
now = int(datetime.now().timestamp())
return now + 5 > exp
except:
return True


if __name__ == "__main__":
from app.global_data.config import Config
from app.global_data.consul_client import ConsulClient

config = Config()
consul_client = ConsulClient(config.consul_address, config.consul_port)
oauth_client = OauthClient(consul_client)

print(oauth_client.get_public_key())
print(oauth_client.get_jwt())

+ 7
- 11
app/service/blog_service.py View File

@@ -1,9 +1,9 @@
from app.service import token_service, uapp2_client
from serviceboot import token_service
from app.service import uapp2_client


def create_blog(**args):
token = token_service.get_token(args.get('http_request'))
args['http_request'] = None
token = token_service.get_token()

result = uapp2_client.create_blog(args, token.jwt)
if result['status'] != 'ok' :
@@ -13,8 +13,7 @@ def create_blog(**args):


def update_blog(**args):
token = token_service.get_token(args.get('http_request'))
args['http_request'] = None
token = token_service.get_token()

result = uapp2_client.update_blog(args, token.jwt)
if result['status'] != 'ok':
@@ -24,8 +23,7 @@ def update_blog(**args):


def get_blogs(**args):
token = token_service.get_token(args.get('http_request'))
args['http_request'] = None
token = token_service.get_token()

result = uapp2_client.get_blogs(args, token.jwt)
if result['status'] != 'ok':
@@ -35,8 +33,7 @@ def get_blogs(**args):


def find_blog(**args):
token = token_service.get_token(args.get('http_request'))
args['http_request'] = None
token = token_service.get_token()

result = uapp2_client.find_blog(args, token.jwt)
if result['status'] != 'ok':
@@ -46,8 +43,7 @@ def find_blog(**args):


def delete_blog(**args):
token = token_service.get_token(args.get('http_request'))
args['http_request'] = None
token = token_service.get_token()

result = uapp2_client.delete_blog(args, token.jwt)
if result['status'] != 'ok':


+ 0
- 53
app/service/token_service.py View File

@@ -1,53 +0,0 @@
import python_jwt
from jwcrypto import jwk
from app.global_data.global_data import g
import logging


class Token:
def __init__(self):
self.is_valid = False
self.jwt = None
self.username = None
self.roles = []

def has_role(self, role):
return role in self.roles


def get_token(request):
token = Token()

public_key = g.oauth_client.get_public_key()
if public_key is None:
logging.error('Cannot get public key from UAA')
return token

authorization = request.headers.get('Authorization')
if authorization is None:
return token

if authorization[0:6].lower() != 'bearer':
return token

token.jwt = authorization[7:] # 去除前缀“Bearer ”

try:
jwt_decoded = python_jwt.verify_jwt(
token.jwt,
jwk.JWK.from_pem(public_key.encode()),
['RS256'],
checks_optional=True,
ignore_not_implemented=True
)
claims = jwt_decoded[1]
token.username = claims.get('user_name') or claims.get('client_id')
token.roles = claims.get('authorities') or []
token.is_valid = True
except Exception as e:
token.is_valid = False
token.jwt = None
token.username = None
token.roles = []

return token

+ 4
- 8
application.yml View File

@@ -1,15 +1,11 @@
serviceboot:
version: v2

service:
python_version: 3.5.9
cname: CubePy微服务样例1
ename: uapp1
version: 1.0.0
port:
dev: 8103
prod: 80

build:
image_name: uapp1
build_web: no
compile_python_to_so: no
has_web: no
compile_python: no
process_num: 1

+ 0
- 1
pip-install-reqs.sh View File

@@ -1,2 +1 @@
# pip3 install -i https://pypi.douban.com/simple/ -r requirements.txt
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt

+ 1
- 2
requirements.txt View File

@@ -1,6 +1,5 @@
# python==3.5.9
serviceboot==1.0.6
python-consul==1.1.0
serviceboot==2.0.5
python_jwt==3.2.6
pyyaml==5.3.1
pymysql==0.9.3


Loading…
Cancel
Save