我的Flask-Restful应用程序有许多"对象".在应用程序的第一个版本中,这些是没有行为的简单数据结构,实现为Dicts或Dicts列表.
这些"对象"的属性可以改变.我使用生成器函数来跟踪更改,然后通过服务器发送事件(SSE)警告Web客户端.这通过维护要跟踪的对象的"旧"副本并将其与最新状态进行比较来工作.
在应用程序的下一个版本中,我使用SQLAlchemy从SQLite数据库填充"对象".这些对象现在实现为SQLAlchemy声明性类或此类的列表.
为了比较基于属性相等的"旧"和"新"实例,我只需要__eq__为我的SQLAlchemy对象添加一个覆盖.即,当属性具有相同的值时,实例被认为是相等/不变的.(我已在此问题的底部发布了示例代码).
从技术上讲,这是有效的,但引起了一些建筑警钟:我是否朝错误的方向航行?
a)如果我添加__eq__并__ne__覆盖SQAlchemy对象,当我后来想要将对象重新保存回数据库时,这是否会导致SQLAlchemy出现问题?
b)SQLAlchemy对象应该在我的应用程序中达到多远:是否有"pythonic最佳实践"?即,使用与DB持久性无关的业务逻辑/行为(例如跟踪更改)扩展SQLAlchemy对象是否正常/正常; 或者它们应该仅用作数据库和服务器之间的简单DTO,还有其他对象中的业务逻辑?
注意:我很清楚,通过REST apis和SSE呈现给客户端的数据应该从Web服务器和数据库中的实现细节中抽象出来,因此这不是这个问题的一部分.
sqlalchemy id equal vs reference equality https://codereview.stackexchange.com/questions/93511/data-transfer-objects-vs-entities-in-java-rest-server-application http://www.mehdi-khalili.com/ORM-反模式部分-4-持久性域模型/
class EqualityMixin(object):
# extended from the concept in :
# https://stackoverflow.com/questions/390250/elegant-ways-to-support-equivalence-equality-in-python-classes
def __eq__(self, other):
classes_match = isinstance(other, self.__class__)
a, b = deepcopy(self.__dict__), deepcopy(other.__dict__)
#compare based on equality our attributes, ignoring SQLAlchemy internal stuff
a.pop('_sa_instance_state', None)
b.pop('_sa_instance_state', None)
attrs_match = (a == b)
return classes_match and attrs_match
def __ne__(self, other):
return not self.__eq__(other)
Run Code Online (Sandbox Code Playgroud) 我使用 Byte-Buddy 动态生成 Java 接口方法的实现,并将对这些方法的调用委托给现有代理对象的单个方法。
第一个版本的灵感来自如何使用 ByteBuddy 创建动态代理
它使用反射InvocationHandler
即具体代理类:
InvocationHandlerinvoke()这很好用。
然后重新阅读Github 上的 Byte-Buddy 自述文件,MethodDelegation我发现了使用“GeneralInterceptor”的替代版本。
即具体代理类:
RuntimeType。这也很好用!
下面的代码片段演示了这两种技术。
Class<? extends Object> clazz = new ByteBuddy()
.subclass(serviceSuperClass)
.name(className)
// use a Reflection InvocationHander for the methods of serviceInterfaceOne
.implement(serviceInterfaceOne)
.defineField(invocationHandler, MyProxy.class, Visibility.PUBLIC)
.method(isDeclaredBy(serviceInterfaceOne))
.intercept(InvocationHandlerAdapter.toField(invocationHandler))
// use a Byte-Buddy "GeneralInterceptor" for the methods of serviceInterfaceTwo
.implement(serviceInterfaceTwo)
.defineField(generalInterceptor, MyProxy.class, Visibility.PUBLIC)
.method(isDeclaredBy(serviceInterfaceTwo))
.intercept(MethodDelegation.toField(generalInterceptor))
//
.make ()
.load(classLoader)
.getLoaded();
Run Code Online (Sandbox Code Playgroud)
public class MyProxy implements …Run Code Online (Sandbox Code Playgroud) 我使用GSS-API创建了2个演示Kerberos客户端.一个在Python3中,第二个在Java中.两个客户端似乎大致相同,并且两者都"起作用",因为我获得了我的Java GSS-API服务主体接受的服务票证.
但是在测试时我注意到Python客户端将服务票据保存在kerberos凭证缓存中,而Java客户端似乎没有保存票证.
我使用"klist"来查看凭证缓存的内容.
我的客户使用FreeIPA作为Kerberos环境在Lubuntu 17.04虚拟机上运行.我正在使用OpenJDK 8 u131.
问题1: Java GSS-API是否不保存凭证缓存的服务票证?或者我可以更改我的代码吗?
问题2:服务票据未保存到缓存这一事实是否有任何缺点?
我的假设是缓存的服务票据减少了与KDC的交互,但评论了如何使用Windows Java客户端保存Kerberos服务票证?建议事实并非如此,但是这个Microsoft技术说明"每次想要访问这个特定服务器时,客户端都不需要返回KDC".
问题3:来自python客户端的缓存服务票证在几分钟后消失 - 在到期日之前很久.是什么导致他们消失?
Python代码
#!/usr/bin/python3.5
import gssapi
from io import BytesIO
server_name = 'HTTP/app-srv.acme.com@ACME.COM'
service_name = gssapi.Name(server_name)
client_ctx = gssapi.SecurityContext(name=service_name, usage='initiate')
initial_client_token = client_ctx.step()
Run Code Online (Sandbox Code Playgroud)
Java代码
System.setProperty("java.security.krb5.conf","/etc/krb5.conf");
System.setProperty("javax.security.auth.useSubjectCredsOnly","false");
GSSManager manager = GSSManager.getInstance();
GSSName clientName;
GSSContext context = null;
//try catch removed for brevity
GSSName serverName =
manager.createName("HTTP/app-srv.acme.com@ACME.COM", null);
Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
//use default credentials
context = manager.createContext(serverName,
krb5Oid,
null, …Run Code Online (Sandbox Code Playgroud)