具有自定义成员资格的 MVC.NET 应用程序的实施审查
我想听听是否有人发现我在这个基于 Oracle 的 MVC.NET 应用程序中实现安全性的方式存在任何问题,无论是安全问题、并发问题还是可扩展性问题。
首先,我实现了一个 CustomOracleMembershipProvider 来处理会员存储的数据库接口。
我实现了一个名为 User 的自定义主体,它实现了 IPrincipal,并且它有一个角色哈希表。
我还创建了一个名为 AuthCache 的单独类,它为 User 对象提供了一个简单的缓存。其目的很简单,就是避免返回数据库,同时将缓存与 Web 层或数据层解耦。 (这样我就可以在 MVC.NET、WCF 等之间共享缓存)
MVC.NET Stock MembershipService 使用 CustomOracleMembershipProvider(在 web.config 中配置),并且 MembershipService > 和 FormsService 共享对单例 AuthCache 的访问。
My AccountController.LogOn() 方法:
1) 通过 MembershipService.Validate() 方法验证用户,还将角色加载到 User.Roles 容器中,然后缓存AuthCache 中的用户。
2) 通过 FormsService.SignIn() 将用户登录到 Web 上下文,它访问 AuthCache(而不是数据库)以获取用户,将 HttpContext.Current.User 设置为缓存的用户主体。
在global.asax.cs中,实现了Application_AuthenticateRequest()。它解密 FormsAuthenticationTicket,通过 Ticket.Name(用户名)访问 AuthCache,并通过从 AuthCache 设置 Context.User = user 来设置主体。
简而言之,所有这些类共享 AuthCache,并且为了线程同步,我在缓存存储方法中有一个 lock() 。 read 方法中没有锁。
自定义成员资格提供程序不知道缓存,MembershipService 不知道任何 HttpContext(因此可以在 Web 应用程序外部使用),并且 FormsService 除了访问 AuthCache 来设置Context.User 用于初始登录,因此它不依赖于特定的会员资格提供商。
我现在看到的主要内容是,如果用户从多个会话登录,AuthCache 将共享一个 User 对象。因此,我可能必须将密钥从 UserId 更改为其他内容(也许使用 FormsAuthenticationTicket 中的某些内容作为密钥?)。
I'd like to hear if anyone sees any problems with how I implemented the security in this Oracle based MVC.NET app, either security issues, concurrency issues or scalability issues.
First, I implemented a CustomOracleMembershipProvider to handle the database interface to the membership store.
I implemented a custom Principal named User which implements IPrincipal, and it has a hashtable of Roles.
I also created a separate class named AuthCache which has a simple cache for User objects. Its purpose is simple to avoid return trips to the database, while decoupling the caching from either the web layer or the data layer. (So I can share the cache between MVC.NET, WCF, etc.)
The MVC.NET stock MembershipService uses the CustomOracleMembershipProvider (configured in web.config), and both MembershipService and FormsService share access to the singleton AuthCache.
My AccountController.LogOn() method:
1) Validates the user via the MembershipService.Validate() method, also loads the roles into the User.Roles container and then caches the User in AuthCache.
2) Signs the user into the Web context via FormsService.SignIn() which accesses the AuthCache (not the database) to get the User, sets HttpContext.Current.User to the cached User Principal.
In global.asax.cs, Application_AuthenticateRequest() is implemented. It decrypts the FormsAuthenticationTicket, accesses the AuthCache by the ticket.Name (Username) and sets the Principal by setting Context.User = user from the AuthCache.
So in short, all these classes share the AuthCache, and I have, for thread synchronization, a lock() in the cache store method. No lock in the read method.
The custom membership provider doesn't know about the cache, the MembershipService doesn't know about any HttpContext (so could be used outside of a web app), and the FormsService doesn't use any custom methods besides accessing the AuthCache to set the Context.User for the initial login, so it isn't dependent on a specific membership provider.
The main thing I see now is that the AuthCache will be sharing a User object if a user logs in from multiple sessions. So I may have to change the key from just UserId to something else (maybe using something in the FormsAuthenticationTicket for the key?).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
为什么对角色使用哈希表?除非您希望人们拥有多个角色,否则简单的列表可能会更快地搜索。如果您可以提前预测所有角色,那么使用位掩码/标志枚举会更好。
您应该尽量避免编写自己的锁定机制,因为它很容易出错。使用新的 System.Collections.Concurrent 类,或者如果您必须推出自己的类,那么请务必使用 Interlocked(因为所有其他锁定机制都非常昂贵)。
缓存应使用 WeakReference 封装来允许条目被 GC 并支持在条目丢失时从数据库检索用户信息。如果您需要分布式缓存,也许可以看看 Velocity。
共享用户对象可能不是问题,但可能不是推荐的策略。许多数据库访问框架将跟踪在会话或工作单元中检索的对象,并且跨会话共享对象将会出现问题。如果您确实要共享用户对象,那么请确保它们不可变。
最后,我个人鄙视整个 Membership Provider API,因为它使用 GUID 进行识别,并且用于用户配置文件的默认 SQL Server 数据库设计非常糟糕(也称为性能杀手)。这似乎不是您关心的问题,因为您已经推出了自己的(数据库和实现),但您可能想评估实现 API 是否涉及任何真正的好处,或者它是否主要是束缚您特定方式的束缚做事的。
Why use a Hashtable for the roles? A plain list would likely be faster to search unless you expect people to have more than a handful of roles. If you can predict all roles in advance then using a bitmask/flags enumeration would be even better.
You should try to avoid writing your own locking mechanism, as it's fairly easy to get wrong. Use the new System.Collections.Concurrent classes, or if you must roll your own then be sure to use Interlocked (as all the other locking mechanisms are quite expensive).
Caching should use WeakReference encapsulation to allow entries to be GCed and support retrieving user information from the database if an entry is missing. Maybe have a look at Velocity if you need a distributed cache.
Sharing user objects might not be a problem, but is probably not a recommended strategy. Many database access frameworks will track objects retrieved in a session or unit of work, and sharing objects across sessions would then be problematic. If you do go for sharing user objects then be sure to make them immutable.
Last, I personally despise the whole Membership Provider API, because it uses GUIDs for identification and the default SQL Server database design for user profiles is just horrible (aka performance killer). This does not seem to be a concern for you as you've rolled your own (db and implementation) but you might want to evaluate whether there are any real benefits involved from implementing the API, or whether it's mostly shackles tying you to specific ways of doing things.