.net n-tier identity & authorization in service architecture
I'm building an application where the requirements seem standard issue (at least to me)... I have a Web.UI based on asp .net mvc & clients from iphone, andriod & blackberry.
So the sensible thing to do is to move all my business logic into a services layer that can be accesses over http. This services layer must accept requests with a user context (identity) and in some nice way perform authorization consistently no matter which type of client is communicating with it (I hope?).
Over a year a go I did a 3 month gig that employed W.I.F. (Windows Identity Foundation) in a hybrid on-premises & cloud architecture. I liked it. The 3 things that struck a chord were (1) externalizing authentication and not caring how it was done, (2) removing authorization logic from business logic, (3) Claims based authorization.
Over the last year I've heard and watch all about Rest Services the 'new cool hippy way of doing things'. So I though great, let's try that. After I started to play around & get coding, I started getting really confused (and subsequently read for about 10 hours yesterday without writing another line of c#). I'm still confused about all the SOAP vs REST, WS.* vs Http, SAML vs SWT babble. I don't really want this thread to be about that because there is enough of that speak on stackoverflow, but I feel like I've got a choice between two camps, when it doesn't really feel like I want one or the other but bits from each?
To me the 3 points I mentioned above about WIF don't seem like concepts that should be tied to WS.* ? But I'm getting the feeling that they, or at least how WIF comes at the moment makes them, without some expert tweaking (e.g. I came across this post only written a few days ago - http://zamd.net/2011/02/08/using-simple-web-token-swt-with-wif/).
The other areas I don't know much about is are my clients (iphone, andriod, blackberry) capable of playing with WIF, is it the same STS that throws a SAML token to them and they behave just like a browser and pass it back in a header just like any other client? Yes I'm going to have to find out, but if this is a deal breaker with W.I.F and I find out straight after posting this, then at least I can focus away from it.
Finally to throw one more thing in the mix. I don't really want to think about any of this. I want to use a 3rd party authentication / identity provider - http://www.janrain.com/products/engage - which I believe uses OpenID. Can this fit into W.I.F. or do I just create a new SAML token from the OpenID and use WIF from that moment on.
I guess at the end of this babble, I want to come back to where I started because it's getting more and more complicated the more questions I ask and the more options I consider.
Is having a services layer (on WCF) that talks to d开发者_运维知识库ifferent non-.net clients that requires identity context and authorization so strange? If you've build something like this, how did you approach it?
When you have many devices, one way to get the same solution working across all of them, is to target the lowest common denominator.
Assuming that all your clients support cookies. One way of doing this would:
- Have an authentication system based on a cookie.
- Cache all authorisation information on the server side, linked to a session or key in the cookie
- For each request check the authorization
Not quite as elegant as using SAML tokens, but it does work cross platform / devices.
IPhone supports cookies http://support.apple.com/kb/HT1675
Blackberry supports cookies http://docs.blackberry.com/en/developers/deliverables/11844/feature_cookie_storage_438273_11.jsp
I'm going to take a crack at answering your question slightly more abstractly...
Before I begin, my background is MS biased so there may be equal (or better) options available from other sources.
Two references that I found very useful:
1) A Guide to Claims-based Identity and Access Control
http://msdn.microsoft.com/en-us/library/ff423674.aspx
2) Programming Windows Identity Foundation
Author: Vittorio Bertocci Available in hard copy of kindle formats
There are alot of other sources but these two cover several scenarios and give good background info for anyone that wants to get up to speed with your starting point.
I encourage other posters to fill in any gaps or mistatements :) I'm glossing over a slew of technical details to focus on the question asked.
The way I break federated identity down is, roughly, as follows:
- Application(s) [App(s)]
- Authentication service(s) [STS(s)]
- Claim Set(s) [Claim(s)]
- Trust relationship(s) [Trust(s)]
- Transport method(s) [Transport(s)]
An STS is responsible for verifying the identity of a user and vouching for some claims. It does this by either providing (1) a signed blob containing the claims or (2) a unique identifier that a 3rd party can use to lookup the claims.
An application that wants to provide a user with a service can "trust" an STS to provide it with claims it can use to work with the user appropriately, thus aleviating it of the responsibility of verifying the user (among other things such as maintaining centralized meta-data but I digress).
There is also the ability for an STS to "trust" another STS, basically saying "If you say this person is Joe Smith and they have X, Y and Z roles, then I'll vouch for what you say!"
So to paraphrase:
App(s) "trust" an STS { which can in turn "trust" another STS } to provide it/them Claim(s)
** Switching Gears **
SOAP vs REST
At the end of the day SOAP and REST are both Service types, lets call them Claims consumers. They both want someone to give them a bucket full of claims so they can do their work and send back some stuff. Additionally, both service types can be presented with claims via query string using a token (assuming the service can handle some url rewriting) or via a header (HTTP for REST and SOAP for, well, SOAP services). Either way the goal is the same: Transmit the claims or the UID to the App.
WS* vs HTTP
These (along with TCP/IP, SSL, secret decoder rings, etc) are methods of passing information back and forth, albeit with varying degrees of certainty that someone in the middle can't find a way to impersonate the user.
SAML vs SWT
These (along with base 64 encoding, xml, simple text, etc) are both methods of serializing claims. These two just happen to conform to standards that the others don't so everyone can speak the same language.
** Getting back to the point **
Each of those technology combinations are valid (depending on the application, some are less secure, others are easier to implement, others work better on lower level devices, etc) and are just one person's way of doing the job vs another's.
So if I have a .Net application that has been served a set of claims in SAML fomat over a WS* pipeline the end result is that the applciation has [claims in SAML].
With some processing these can be transformed into [claims in SWT].
The new claims can then be packaged up and sent via HTTP/SSL to a Java application.
IF the Java application "trusts" the same STS (or an STS that "trusts" the .Net apps STS) then it opens up the claims and does its work.
The expert tweaking you mention has to happen, the question is simply by whom and how transparant is it
- Dave provides a perfectly valid example of one way to accomplish the tweaking with custom code.
- ADFS provides translation rules that try to accomplish merging and translation via configuration.
The idea of having services on disparate platforms/devices/applications/etc is not strange at all, that's exactly the kind of scenario all this stuff is being built to address
I am in the process of trying to build something like what you are asking about so I've been working on the same kind of issues myself.
The Engage service you mentioned allows you to associate your application's users with outside sources and can be used to authenticate those users... ala "I see you authenticated with google as John@gmail.com, I know you as John Walker with an id of 4321, oh, look, you changed your favorite color setting in google to blue... carry on!"
What it doesn't do is provide claims to your application that are specific to your application (unless all you need to know comes from the google data in which case you're likely building a mash up and not an LOB application...
Another scenario:
- User goes to your application
- Is redirected to your STS
- Is redirected to Google
- Is returned to your STS
- Email and favorite color claims are added (per google)
- List of roles and purchase limit claims are added (from your application specific datastore)
- User is returned to the application
- User tries to buy $10,000 purple widgets and you say "Well, you can only buy $5,000 on credit and... are you sure you want purple I heard you prefer blue?"
Another place that I would direct you to is the AppFabric Access Control service offered by Microsoft. (http://msdn.microsoft.com/en-us/library/ee732536.aspx) disclaimer: I haven't used it yet but, it looks like it does the kinds of translations you are looking for with a lot of the meat hidden away for you.
As WIF talks WS-Trust / WS-Federation under the hood, you can expose claims-based authentication at the services layer.
This article shows how to create a WCF STS that will talk to external clients using these protocols. http://msdn.microsoft.com/en-us/library/ee748498.aspx
From an authorisation point of view at the services layer you can use a custom authorisation manager to check that claims have been presented. http://msdn.microsoft.com/en-us/library/ms731774.aspx
To plug in external identity services such as OpenID and add your own claims into those generated by WIF then you can sub-class from ClaimsAuthenticationManager as follows:
public class MyClaimsAuthenticationManager : ClaimsAuthenticationManager
{
public override IClaimsPrincipal Authenticate(string resourceName, IClaimsPrincipal incomingPrincipal)
{
if (!incomingPrincipal.Identity.IsAuthenticated)
{
return incomingPrincipal;
}
//TODO: obtain user profile claims from external source, i.e. database, web service
// below code demonstrates how to custom claims to the current principal
// (which are then persisted for the lifecycle of the user's browser session)
IClaimsIdentity identity = (IClaimsIdentity)incomingPrincipal.Identity;
identity.Claims.Add(new Claim(ClaimTypes.Email, "dave@dave.com"));
return incomingPrincipal;
}
}
You'll need to tell WIF to use your own claims manager in the Web.config file by setting the claimsAuthenticationManager configuration parameter.
Hope this helps.
I have approached a similar problem using spring+java; all the concepts it requires are in .net so I mention it here. I found the solution which spring-security puts forward worked well (for my simple authorization requirements). In my services layer, methods which require specific permissions declare this via annotations (either on the interface or on the implementation):
@Secured(MyPermissions.READ, MyPermissions.WRITE)
void modifyPerson(PersonChanges changes);
@Secured(MyPermissions.READ)
Person readPerson();
In this example the security framework (spring) wraps the service implementations with a dynamic proxy which verifies that my authorization layer has put the proper thread scoped roles/permissions are in the context where the service method is evaluated, if not a security exception is thrown.
I also found it helpful to group together services which require permissions by URL patterns, so that the "requires an authenticated principal" requirement is handled at the highest level: e.g. myapp/services/secure/personService
-- any URL pattern requiring */secure will redirect to the authentication page if no authentication info exists.
What is really nice (I though) about requiring thread scoped credentials, is that even if the top level HTTP interceptor setup is done wrong (e.g. fails to verify/create an authentication session), as long as the dynamic proxy is working there is no way the business logic can get executed.
Also, it works really nicely for aggregate services -- if one service calls another, lower level authorization rules are still enforced if they are not properly declared on the composite service.
精彩评论