系统架构

从很高的抽象层面讲,所有的企业管理软件都包含两个部分,客户端和服务器端,换个说法,消费方和提供方。客户端向服务端发出请求,服务端给出响应。
简单的这样一个模式,内涵其实挺丰富,它包含非常多的内容,我们慢慢讲。

一、客户端
客户端就是发出请求的一方。有时候客户端和服务端角色是变化的。
现在的系统都是多层系统,cs,bs,多层。这里的c就是client的意思,b也是client的一中,专指以浏览器为程序容器的客户端。
mis系统的用户使用的、UI部分就是常说的客户端。比如我们的费用系统,客户端供用户填报报销事项,服务端处理业务逻辑,保存数据。我们直接使用的就是系统的客户端。所有的业务都通过客户端(访问服务端)来完成。
外部系统也是一种客户端。现在一个企业往往会部署非常多的系统,每个系统处理企业经营的某个方面的业务。比如费用系统,资金系统,财务系统,预算系统,库存系统等等。这些系统之间一般会存在联系,比如财务系统会访问资金系统、预算系统来取得必要的数据。那相对与资金系统来讲,财务系统也是一种客户端,这个客户端通过一定的方式来请求服务端(资金系统)的提供的服务(比如查询某个月度的资金计划)。

客户端的主要内容就是通过某种方式向服务端发出请求,获得服务端的反馈,并对返回的数据做适当的处理,展示,打印,导出等等。服务端往往和客户端不在同一台机器上,可能是位于局域网内的另外个服务器上,也可能是部署在互联网上,远在大洋彼岸的某个大型机房内,也可能是位于某个人的办公桌上的一台pc机,都有可能。

二、服务端
和客户端相对的一方,就是服务端。服务端的职责就是,时刻等待来自客户端的请求,进行处理,并将处理结果返回给客户端(请求者)。抽象讲,这就是服务器的职责。具体到不同的系统的服务器,处理的内容,处理方式,返回的数据各有不同。gps服务器返回客户短的地理坐标,财务系统的服务器可能返回一张记账凭证的数据。前者接收终端设备的定位请求,根据复杂的逻辑和算法,确定其坐标;后者是接收客户端的查询某个指定凭证号的凭证,经过简单的数据库查询,返回凭证的内容(使用xml格式)。
随着体系结构的发展,服务端从逻辑上可以分为多层,请求接收层,业务处理层,数据存储层等等。请求接收层,接收来自不同客户端的请求,验证请求的合法性,并将请求转发给指定的逻辑层。逻辑层根据可预先定义好的算法、逻辑,对数据进行加工,处理,必要时需要访问数据存储层,查询数据,并将处理结果以与诶定好的形式返回给客户端。

三、通讯协议
客户端向服务端发起请求,服务端做出回应,典型的 request-response模式(rr模式)。二者之间必然存在数据通讯,
我们再从方法调用的视角来看,rr模式等同于客户端调用服务端的功能方法。方法调用就存在双方之间的数据传递。在同一个程序内,比如我们写java程序的方法调用的格式: ret = method1(p1,p2…)。这属于进程内调用(in-process invoke,IPV),IPV调用需要指明方法,传入参数,并返回值,只不过是进程内,他的通讯机制稍微简单一些,是内存操作(比如参数是通过stack传入的)。
但是我们一般的的客户端与服务器之间的调用,不仅仅是跨进程调用(cross-process invoke),而且是跨机器调用,一个机器上的程序调用另外一个机器上的程序的功能。通讯会稍微复杂一些。
业界开发了多种跨机器的调用协议。比如RPC,soap,restful,http(s).其实 http是超文本传输协议,他定义了开放的的宽泛的协议标准,通过充分发挥其潜力,可以作为跨机器的方法调用协议。因为http协议可以传输任意内容(二进制可以base64编码转换为文本内容进行传输),这就给了我们充分发挥的空间,我们可以基于这个协议定义自己的调用协议。其实,restful 和 soap 就是基于http协议的上层协议和标准。
http协议,支持通过k=v的方式传递参数,url(上下文)可以成为方法名称或寻址路径。这样,有了方法,有了参数,就可以“调用方法”了,调用结果依然通过http协议返回,返回可以是plaintext,也可以是xml,json。xml的出现要早于json,二者可以好像转换,是等价的。以上标准,http协议都给我们实现了,所有的webserver都支持这个协议,我们只管拿来用即可。

四、用户认证
对一个安全的系统,任何调用都应该是经过认证的。无非是支持匿名调用的系统。无论是本系统调用,还是外部系统调用,无论是通过什么方式调用,对未授权访问给出恰当的反应。服务端要识别调用者是来自哪里,调用者的身份。由于调用来源的多样性,因此要统筹考虑用户认证的架构,要支持各种各样的认证方式,而且对不同来源的未授权访问做出合适的反馈。比如如果一个用户操作的客户端没有登录就访问系统资源,系统应该重定向到登录界面,让用户输入账号和密码;而一个外部系统通过自动化方式访问,只要返回未授权的错误码即可,而不是重定向至登录界面。

1、用户认证方式
提到用户认证,我们首先想到系统本身的登录界面。登录界面一般是输入账号和密码,然后发送到服务器,验证是否正确,如果正确,就确定了用户的身份(账号在系统中有注册)。除了这种传统的方式之外,还有很多种其他方式,比如单点登录,第三方账号登录,无界面的自动登陆。我们挨个说,
(1)传统的本地验证。这里说的本地验证就是只系统本省实现的用户登录方式。提供一个登陆界面,输入账号和密码。这种方式一般是登录后,在服务创建本次登录的session,同时把在sessionid保存在浏览器本地,每次访问时,都自动携带这个sessionid,在服务器session中查找用户身份。如果有sessionid并且在服务器中找到了对应的session信息, 就说明是登录过得,并且也确定了访问者的身份。
(2)单点登录sso。sso有很多种方案。大概的流程是这样的:
a)访问资源
b)拦截器检查是否有token。如果没有,就到c),如果有,就询问认证服务器token是否有效,如果失效,就转c),否则转d)
c)重定向到sso登录界面,输入账号登录。登录成功后,重定向到资源url,同事带上token 和 本系统的账号(sso系统有sso账号和业务系统的账号的对应关系)。
大家注意,这里是使用sso颁发的token 来判断访问者身份的。
(3)第三方登录
比如通过qq,baidu等账号登录,这些系统都支持oauth 协议。
但是一般业务系统使用这种第三方登录的不多。最常见的是集成企业微信的登陆,集成钉钉等。一般这种情况发生在通过企业微信访问业务系统的时候。这种和上面两种略有哦不同,这种是,用户已经登陆了企业微信,在企业微信中方位业务系统,需要将企业微信的账号转化为业务系统的账号,当然前提是我们将企业微信作为可信赖的系统。这种识别访问者身份的方式又和上一种不同。
(4)接口调用认证
本系统提供了接口,外部系统来调用,也要经过用户认证才行。
通常的做法是,除了一般的功能接口外,还提供一个登陆接口,在调用功能接口前,首先要调用登陆接口,登陆接口的参数一般包括,appid,scretkey,账号,密码。该接口返回一个令牌,在调用功能接口时,通过该令牌表明身份。因此业务系统通过检查该令牌来识别访问者的身份。

2、用户认证检查
如何避免用户在未经授权时访问被保护资源呢?在每个资源的处理程序中检查?这显然不是一个好的主义,这样不仅仅代码冗余,也要求所有资源都要了解授权的细节。一般有2中方式:
(1)服务端只提供唯一的访问入口,访问什么资源,参数是什么,通过这个入口的参数来区分。比如提供了一个 ./action。没有其他入口。这样在这个入口进行用户检查,检查通过后,再分发到各自的处理程序。
(2)拦截器或过滤器
在拦截器中,检查来访者是否是认证用户。ssh框架已经为我们实现了拦截器机制,确保当访问到达时,首先要经过拦截器。

但是如何检查呢。换句话说,如何判断来访者的身份呢?这个和可用户的认证方式有关系。我们在讨论不同的认证方式时仔细讨论,看看是否有一个通用的方案来解答上面的问题。

大家看到,以上的方式,身份识别的方式各不相同。能够设计一个统一的架构,来包含以上的情况呢?或者说设计一个可扩展的结构,方便使用者只有扩展,而不需要修改业务程序。

首先我们要能识别每种访问来源,根据来源的某些特征,来确定确定用户身份的方式。比如企业微信访问时,在user-agent 中有webwx 字样,我们就认为是微信用户集成方式,访问特定的api,通过token来识别用户。

第二个问题,对未经授权的访问,我们如何响应?是返回未授权错误吗,还是重定向到登录页面,或者2者兼而有之?
比如一般用户通过浏览器访问时,如果未登录,无法识别来访者身份,就可以定位到登录界面(无论是本地认证还是单点登录),但是如果第三方系统通过接口从后台访问的话,就应该是返回错误代码,等等。
除了系统提供默认行为外,更好的处理方式是让调用者来决定,通过在调用时传递参数,来告诉服务器,当访问未经授权时,服务器如何响应,是重定向到登录界面,还是仅仅返回错误码。因为调用的场景多种多样,服务器无法给出正确的响应。

五、维持会话
大家知道,http是无协议的,意思说,每次访问和上次的访问,从网络连接上来说是没有关系的,每次连接执行完后,立刻就断开了。上面提到访问服务器一般要求用户认证,我们把认证登录作为一次连接,那么认证登录这次连接执行完后,后面的连接和他是没有关系的,那么服务器怎么知道这次连接是哪个用户呢?
那我在每次连接的时候,我都把账号带上行不行?那不行,这样太不安全了,因为账号是公开的,如果你登陆过,那么任何人拿着你的账号访问系统,服务器都会认为是合法的访问。只有客户和服务器双方知道的“东西”才是“安全的”,对,通行证。当你首次登陆的时候,服务器给你颁发一个通行证,客户端在之后的每次访问中,都把这个通行证带上。在ssh框架中,这个通行证就是jsessionid。在你首次登陆成功后,服务器生成一个“文本串“,称之为jsessionid,返回给客户端,浏览器把他存放在cookie中。存放在cookie中很有好处,在你再次访问的时候,浏览器会自动把它发送给服务器,而不需要你主动去做。如果客户端不是浏览器的话,你就要自己操心这个事情了。
通行证一般还有有效期,如果超期的话,需要重新申请。如何形成通行证,通行证里包含什么信息,如何避免通行证被冒用,如何避免重放攻击等,这是另外的一个话题。

原文地址:https://www.cnblogs.com/senline/p/13514533.html