安全限制是否应导致服务返回null或引发异常?[关闭]


11

我在这个问题上与经验丰富的开发人员有些分歧,想知道其他人对此有何看法?我们的环境是Java,EJB 3,服务等。

我编写的代码调用服务来获取事物和创建事物。我遇到的问题是我得到了没有意义的空指针异常。例如,当我要求服务创建一个对象时,我得到空值。当我尝试查找具有已知有效ID的对象时,返回空值。我花了一些时间试图弄清楚代码中的错误。因为我经验不足,所以我通常以为我做错了什么,但是事实证明,返回null的原因是安全性。如果使用我的服务的用户主体对目标服务没有正确的权限,则它仅返回null。这里大多数其他服务的记录也不太好,因此显然这只是您必须了解的内容。

当开发人员编写与服务交互的代码时,这相当令人困惑。如果服务有一个异常,该异常将告诉我用户没有适当的权限来加载此事物或创建该事物,这对我而言将更为有意义。然后,我会立即知道为什么我的服务无法按预期工作。

编写该服务的经验更丰富的开发人员认为,索要数据不是错误条件,并且仅应在错误条件下引发异常,而不是在用户无法访问数据时抛出异常。通常会在GUI中查找这些数据,对于没有正确权限的用户,这些事情简直“不存在”。简而言之:问是没有错的,因此也不例外。Get方法返回null,因为对那些用户来说那些“不存在”的东西。当不允许用户创建东西时,Create方法返回null。

这是正常的和/或良好的做法吗?我更喜欢使用异常,因为我发现更容易知道发生了什么。因此,例如,如果您请求一个具有无效ID的对象,则我宁愿抛出NotFoundException,而不是返回null。



@ChrisF:1)关于人们回避空引用,我不是。在许多情况下它们是适当的,我只是认为它们不属于这一类。2)关于检查参数,并且断言结果的类型是否与引发异常相同?3)异常与错误代码也是一个不同的问题,因为它们都是“显示”出了什么问题的两种方式。例如,如果您需要通知一个不支持异常的系统,或者您不希望最终用户看到除代码以外的任何内容,则可以使用错误代码。
Svish

我在这里问的问题是关于“假装不存在或未发生”与“告诉为什么不存在或未发生”。
Svish

3
我并不建议它们是重复的-只是它们可能对您有用。
克里斯·

是的,对此表示抱歉!由于某种原因而将其作为“可能重复的”注释...可能是因为缺乏睡眠,呵呵。
Svish

Answers:


19

仅当有错误并且要求事物不是错误时才引发异常。

索取东西可能不是一个错误,但是没有权限去索要所请求的东西肯定是某种错误。异常是报告异常情况的另一种方法,用它来代替特殊的返回值(例如null,正如您所写的,如果有> 1种可能的原因导致错误的原因,则绝对没有帮助(即使存在现在可能有1个可能的原因,在下一版本中可能有2个可能的原因,因此将其null用作指示失败的返回值将使您陷入困境)。如果该服务没有以任何方式报告为什么它不会按照您的要求进行操作,那么那肯定是错误的设计。

最后是否实现使用特殊的返回值(例如,负整数对于通常返回非负整数的函数很有用),异常,全局错误处理程序或记录器等。选择取决于语言,情况,约定以及口味问题。要点是应该有一些直接的方法来找出为什么某些东西不起作用。否则,您唯一的选择就是浪费其他选项,以及找出与黑匣子行为相关的任何内容,这是浪费时间。

String.substring()IndexOutOfBoundsException如果索引超出范围,则抛出。我无法想到返回null代替有什么好处,尽管-从哲学上讲-人们可能会争辩说a String不包含超出其范围的任何内容,于是null逻辑上的返回值。逻辑哲学和实践精神显然是两种不同的动物。


如果substring()将返回越界索引的值,则应返回索引之间的字符串部分,可能为空。这样可以将测试保存在某些调用代码中。
凯文·克莱恩

2
是的,有一个永无休止的“空值”与null-讨论:越界索引的子字符串应该为null还是为空字符串?我不知道,两者实际上都不是“什么”(嗯,也许null比空字符串“还没有什么?”),但是都没有给出异常所能提供的信息量。
乔纳斯·普拉卡

@kevincline:除非使用的框架中使用null引用是一种表示空字符串的惯用手段,否则我看不出任何substring返回null引用的依据。恕我直言,应该有两个独立的函数-其中一个保证返回请求的字符数或引发异常,其中一个返回尽可能多的函数。在某些情况下,每种方法都将明显优于其他方法。
2013年

@supercat:我没有说返回空值。我说:“退货……部分……可能是空的”。空字符串不为null(Oracle除外)。
凯文·克莱恩

@JoonasPulakka:我应该对您之前的评论进行ping。
2013年

5

这取决于合同是什么。如果您的合同说您可以请求不存在的东西,那么它应该说明会发生什么(空对象或空对象)。

另一方面,如果合同说您应该先调用一个不同的方法(DoesSomethingExist()),然后再调用该Get方法,那么您的合同可能会说您只能获取确实存在的事物,如果不存在则抛出异常。异常消息可能会说类似“请务必先致电DoesSomethingExist()”,这是一个有用的错误消息。


在这种情况下,我会说要求不存在的东西是不正常的,因为您可能不会要求不存在的ID。通常,您首先需要获得事物列表,然后再查找其中的特定事物以获取更多详细信息。因此,如果有一种findAllThings方法,那么我想说如果您不允许查看某些或全部内容,则返回一个空列表或子集是适当的。同样,我可能在博客上仅列出属于当前用户的编辑帖子。但是,如果该用户然后尝试调整URL并编辑其他帖子……
Svish

4

这个问题没有一个“一刀切”的答案。是使用异常还是返回null取决于情况。

与其单纯从教条主义的角度看待这个问题,不如将界面看做一个用户界面。用户界面应该可用。因此,不管您对“正确”的处理方式有何看法,从使用您的界面的人的角度选择最可用的方法。

如果调用方需要知道为什么不返回对象,则可以使用异常。但是,如果一般概念是“如果没有权限,则不存在”,则可以接受null。

特别是在您的情况下,如果首先需要调用者登录或连接到服务器,那么我想说null完全可以用于查找项目。那就是当您被告知您是否有权限时。一旦越过大门,当您搜索没有权限(甚至不知道存在)的内容时,应该得到一个空值。

另一方面,如果您没有初始连接或登录步骤,则可以使用异常。如果调用者知道某项存在并且他们没有将其取回,则该API仅返回null就没有帮助。

但是,对于创建项目来说,情况就不同了。可能有很多原因导致此操作失败-没有权限,错误的参数,服务器内存不足等。如果在所有这些情况下都为开发人员提供了null,则他们将无法确定适当的措施。

因此,要回答这个问题,请问问自己,从使用服务的人的角度来看,最有用的解决方案是什么。可能是使用它的方式不典型,在最常见的情况下,null是正确的答案。

因此,不要为此而陷入宗教战争,而应决定该特定问题的正确选择。


不打算为此穿上战斗装备,呵呵。只是想知道我在思考自己在想什么与否时完全错了。我想学习,但是我不喜欢仅仅因为一个人这样说而跳到某个东西。尤其是如果它违背我自己的经验和逻辑(无论大小),就不会如此。无论如何,我完全同意,如果这是一个最终用户界面,那确实很有意义。但是,因为据我所知,这是一个只能通过代码访问的Bean,所以我想说,作为开发人员,我用户。
Svish

1
作为开发人员,无论最终用户应该了解什么,我都会关心为什么出了问题。
Svish

完全同意布莱恩。甚至开发者都是用户,即消耗了一些其他开发人员代码。throw或null的描述是应该对特定任务有用的一种界面/ gui。不只是逻辑或哲学的总称。
独立

3

我绝对认为这是一个糟糕的设计。空指针对我不是一个有效的答案,管理起来很棘手。

如果用户尝试在没有适当凭据的情况下连接服务,我应该给出简洁明了的答复。答案可能是异常或特殊对象,但不能为null。

当网络链接断开或其他意外的严重故障时,您应该留下空答案。

此外,花时间了解为什么会得到空值是一个有力的论据。任何代码段都应该易于理解和使用。具有空值不是这种情况。


在您的最后一句中,您的意思是“ 仅当网络链接断开等时才应返回null ”。或“ 当网络链接断开等时,您应停止返回null ”。?
彼得Török

@PéterTörök:我不建议明确使用“ return null;”。但是,当发生重大故障时,某些服务可以返回null。

如果发生意外的严重故障,例外会更合适吗?
Svish

2
@Amine:我想说这是最适合返回null的情况。
Michael Borgwardt

@Svish:如果您可以检测到重大故障,那么例外当然会很好。

3

考虑到良好的软件设计,您应该考虑项目的寿命。
如前所述null,通过返回,您不会向客户端提供任何信息。看看发生了什么,一开始您没有意识到问题出在哪里。此外,如果没有提供任何文档,那将是一团糟。

通过引发异常,您可以判断出问题所在。您甚至可以根据需要自定义显示的文本,使其更加具体。

另一方面,通过返回null您可以使客户调查情况。

此外,您意识到存在问题,因为您在其他地方NullPointerException。现在,假设您不存储该函数的返回值...您将被迫在每个调用该函数的位置加上一个if else块...

从我的角度来看,退货null是一种不好的做法。


2

编写该服务的经验更丰富的开发人员认为,索要数据不是错误条件,并且仅应在错误条件下引发异常,而不是在用户无法访问数据时抛出异常。

在我看来,这没有任何意义。

未经许可查询数据导致异常,因为如果没有适当的访问权限,这将是意外结果-无结果。

类推GET未经授权的响应代码并非200 ok没有数据,实际上是401 Unauthorized


1

返回null并不好。它什么也没告诉你。以您的示例为例,如果应用程序尝试查找某些内容,并且两个安全问题的响应均为null,并且该项目不存在,那么如何区分两者之间的区别?

有意义的响应可以使应用程序对下一步做什么或如何解决问题做出逻辑选择。


您问如何分辨不存在的对象与您没有查看权限的区别。也许系统的设计要求您不知道。我认为我们没有足够的信息来回答这种特定情况,并且没有适用于所有情况的答案。在完全有效的用例中,如果看不到它,实际上就不存在。而且,在某些用例中,如果看不到您,应该给您一个例外,说明为什么不能。
布莱恩·奥克利

“系统的设计要求您不知道”是什么意思?那是什么样的设计?
Svish

2
如果安全策略显示不允许您查看它,那么在大多数情况下,您也不应该知道它的存在。这使得返回“未找到”是完全可以接受的。
Blrfl 2011年

1

如果根本原因是未经身份验证的用户,那么我更喜欢硬HTTP 401响应,也许重定向到漂亮消息页面(并通知关心的人)。与授权相关的原因更加具体。在服务响应中有用于返回HTTP错误(例如403)的参数和用于返回格式特殊的特殊代码/消息的参数。

异常仅应在特殊情况下使用,这意味着出了点问题。我不同意将它们重载为“不允许”。(对于空返回值也是如此:我不同意应将其用于表示“不允许”。)


好的,您可以在GUI中执行此操作,但是对于实现服务的Bean(在后台执行操作),您只有异常和特殊的返回值可供使用。我当然不是说用户应该获取异常,但是例如可以将异常捕获到Web服务/ servlet /任何内容中,然后执行适当的操作。重定向到某个地方,http 4xx硬页面或其他地方。
Svish

很好,但是您可以采用AOP这样的范例来捕获这些情况。Aspect可以测试是否有特权调用给定的操作,并根据需要重定向/允许处理继续进行。
标记

0

为了扮演恶魔的拥护者,我将尝试返回null的一面。

在我看来,只有一种情况是这种响应几乎可以接受,也就是说,在这种情况下,涉及安全性。

意外泄露未经授权的人不应该知道的系统细节非常容易。应该避免这种情况。

以用户名和密码检查器为例。作为单独的错误代码返回“找不到用户名”错误和“密码错误”错误,显然会使您面临严重的安全问题,因为有人可以先查找有效的用户名,然后查找密码。返回通用的“无效的用户名/密码”将是一个更好的选择。

因此,在这种特殊情况下,我会说返回几乎可以,null但我同意,与他一起工作非常不愉快。


是的,我不同意。在我看来,正确的响应是一个例外,但是对于未找到用户名和错误密码的响应都是相同的。FailedToLogin,InvalidCredentials等。
Svish

@Svish-但是,如果您尝试不提供任何有关失败原因的提示,那么返回null恰恰是最没有帮助的响应。几乎所有其他内容都可能被用来发现有关系统的信息。OP抱怨的原因是,null退货不仅没有说明什么,甚至没有帮助。这就是蓄意目的...说绝对没有,并成为无益。我认为任务已经完成。
OldCurmudgeon 2011年

好吧,也许是这样,但是我仍然会说这样做是不好的。至少在系统内部。可以肯定的是,在最终用户看到事物的边缘,我可以同意。但是在系统内部,开发人员是用户,为什么在世界上我们想让事情变得更加混乱?好像开发软件还不够困难……
Svish

1
而与登录的例子,我会说这是不好的,至少说用户体验没有什么为什么似乎没有什么事情的时候,他们(认为)进入他们的凭据。如果页面只是刷新而好像它只是忽略了我的登录尝试,我将假定系统存在问题,而不是我输入的内容是错误的。
Svish

-2

我认为return null是不友好的,当客户端调用此方法并返回null时,客户端不知道发生了什么以及为什么它返回null。因此,我们应该抛出有意义的执行,并提供描述该执行的文档。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.