* The preview only shows a few pages of manuals at random. You can get the complete content by filling out the form below.
The preview is currently being created... Please pause for a moment!
Description
www.it-ebooks.info
For your convenience Apress has placed some of the front matter material after the index. Please use the Bookmarks and Contents at a Glance links to access them.
www.it-ebooks.info
Contents at a Glance About the Author���������������������������������������������������������������������������������������������������������������xiii About the Technical Reviewer�������������������������������������������������������������������������������������������� xv Acknowledgments������������������������������������������������������������������������������������������������������������ xvii Introduction����������������������������������������������������������������������������������������������������������������������� xix ■■Chapter 1: The Scope of Security��������������������������������������������������������������������������������������1 ■■Chapter 2: Introducing Spring Security�����������������������������������������������������������������������������9 ■■Chapter 3: Spring Security Architecture and Design�������������������������������������������������������27 ■■Chapter 4: Web Security��������������������������������������������������������������������������������������������������57 ■■Chapter 5: Securing the Service Layer��������������������������������������������������������������������������111 ■■Chapter 6: Configuring Alternative Authentication Providers���������������������������������������153 ■■Chapter 7: Business Object Security with ACLs������������������������������������������������������������205 ■■Chapter 8: Customizing and Extending Spring Security������������������������������������������������237 ■■Chapter 9: Integrating Spring Security with Other Frameworks and Languages����������273 Index���������������������������������������������������������������������������������������������������������������������������������311
v www.it-ebooks.info
Introduction Denying the impact of the Spring Framework in the Java world would be simply impossible. Spring has brought so many advantages to the Java developer that I could say it has made better developers of all of us. The good ones, the average ones. All of us. Spring’s core building blocks of Dependency Injection and Aspect Oriented Programming are widely applicable to many business and infrastructure concerns, and certainly application security can benefit from these core functionalities. So this is Spring Security: an application-level security framework built on top of the powerful Spring Framework that deals mainly with the core security concepts of authentication and authorization. Spring Security aims to be a full-featured security solution for your Java applications. Although its main focus is on Web applications and the Java programming language, you will see that it goes beyond these two domains. What I wanted to do in writing this book was to expose some of the internal works of Spring Security along with the standard explanations of how to use certain features. My idea is to teach beyond the basics of how to do something in particular, and instead focus on the plumbing inside the framework. For me, this is the best way of learning something: actually seeing how it is built in the core. That’s not to say, of course, that the book doesn’t cover basic setups and give quick, practical advice on using the framework, because it certainly does. The point I’m making is that instead of saying, “Use this to do that,” I normally say, “This works like this… and this allows you to….” This is a point of view that only tools like Spring afford (because they are open source). With that said, I suggest that the best way to use this book is to have the Spring Security source code checked out on your computer and go through the examples with both the code from the book and the code from Spring Security itself. This will not only help you understand each concept as it is introduced, but will also teach more than one good programming trick and good practice. I recommend this approach to studying any software whenever you have the chance. If the source code is out there, grab it. Sometimes a couple lines of code teach more than a thousand words.
Who This Book Is For This book is written mainly for Java developers who use Spring in their work and need to add security to their applications in a way that leverages Spring’s proven concepts and techniques. The book will also be helpful to developers who want to add Web-layer security to their applications, even if those applications are not fully Spring powered at their core. The book assumes you have knowledge of Java and some of its tools and libraries, such as Servlets and Maven. It also assumes that you know what you want to use security for and in what context you want to use it. This means, for example, I won’t explain protocols like LDAP in much depth; instead, I’ll concentrate on showing you how to integrate Spring Security with an LDAP user store. An in-depth knowledge of Spring is not essential because many of the concepts are introduced as we go along, but the more you understand about Spring, the more you are likely to get out of this book.
How This Book Is Structured The book is divided into nine chapters that embody a progressive study of Spring Security. Starting from a summary of basic applications and an explanation of how the framework is structured, the content moves on to more advanced topics, such as using Spring Security in different JVM languages. The book follows a sequence that corresponds to the way this framework is normally used in real life.
xix www.it-ebooks.info
■ Introduction
The chapters in the book include the following: •
Chapter 1: Introduces security in general and how to approach security problems at the application level.
•
Chapter 2: Introduces Spring Security with a simple example application that secures Web access at the URL level.
•
Chapter 3: Provides a full introduction to the architecture of Spring Security. The chapter covers its main components and how they interact with each other.
•
Chapter 4: Gives in-depth coverage of the web-layer security options available in Spring Security.
•
Chapter 5: Presents, as a counterpart to Chapter 4, full coverage of service-layer security.
•
Chapter 6: Covers a wide array of authentication providers, including LDAP and JASS, that can be plugged into Spring Security.
•
Chapter 7: Covers access control lists (ACL) that are used to secure individual domain objects and how they fit into the general security concerns.
•
Chapter 8: Explains how to extend the core Spring Security functionality by making use of the many extension points supported by its modular architecture.
•
Chapter 9: Shows how to integrate Spring Security with different Java frameworks and some important JVM programming languages.
Prerequisites The examples in this book are all built with Java 7 and Maven 3. The latest Spring versions are used if possible. Spring Security 3.1.3 was the version used throughout the book. Jetty Web Server was used for the different web applications in the book, mainly through its Maven plugin. I worked mainly on my MacBook Air 2011 with 4 GBs of RAM. All the projects were developed using the IDE SpringSource Tool Suite. You are free to use your own tools and operating system. Because everything is Java based, you should be able to compile your programs on any platform without problems.
Downloading the code The code for the examples shown in this book is available on the Apress web site, www.apress.com. A link can be found on the book’s information page under the Source Code/Downloads tab. This tab is located underneath the Related Titles section of the page.
Contacting the Author You are more than welcome to send me any feedback regarding this book or any other subject I might help you with. You can contact me via my blog at http://cscarioni.blogspot.com, or you can send me an email at [email protected].
xx www.it-ebooks.info
Chapter 1
The Scope of Security Security. An incredibly overloaded word in the IT world. It means so many different things in so many different contexts, but in the end, it is all about protecting sensitive and valuable resources against malicious usage. In IT, we have many layers of infrastructure and code that can be subject to malicious attacks, and arguably we should ensure that all these layers get the appropriate levels of protection. Of course, the growth of the Internet and the pursuit of reaching more people with our applications have opened more and more doors to cyber criminals trying to access these applications in illegitimate ways. It is also true that proper care is not always taken to ensure that a properly secured set of services is being offered to the public. And sometimes, even when good care is taken, some hackers are still smart enough to overcome security barriers that, superficially, appear adequate. The three major security layers in an IT infrastructure are the network, the operating system, and the application itself.
The Network Security Layer This layer is probably the most familiar one in the IT world. When people talk about IT security, they normally think of network-level security—in particular, security that uses firewalls. Even though people often associate security with the network level, this is only a very limited layer of protection against attackers. Generally speaking, it can do no more than defend IP addresses and filter network packets addressed to certain ports in certain machines in the network. This is clearly not enough in the vast majority of cases, as traffic at this level is normally allowed to enter the publicly open ports of your various exposed services with no restriction at all. Different attacks can be targeted at these open services, as attackers can execute arbitrary commands that could compromise your security constraints. There exist tools like the popular nmap(http://nmap.org/) that can be used to scan a machine to find open ports. The use of tools like this is an easy first step to take in preparing an attack, because well-known attacks can be used against such open ports if they are not properly secured. A very important part of the network-layer security, in the case of web applications, is the use of Secure Sockets Layer (SSL) to encode all sensitive information sent along the wire, but this is related more to the network protocol at the application level than to the network physical level at which firewalls operate.
The Operating System Layer This layer is probably the most important in the whole security schema, as a properly secured operating system (OS) environment could at least prevent a whole host machine from going down if a particular application is compromised. If an attacker is somehow allowed to have unsecured access to the operating system, he can basically do whatever he wants—from spreading viruses to stealing passwords or deleting your whole server’s data and making it unusable. Even worse perhaps, he could take control of your computer without you even noticing, and use it
1 www.it-ebooks.info
Chapter 1 ■ The Scope of Security
to perform other malicious acts as part of a botnet. We can include in this layer the deployment model of the applications, as you need to know your operating system’s permission scheme to ensure that you don’t give your applications unnecessary privileges over your machine. Applications should run as isolated as possible from the other components of the host machine.
The Application Layer The main focus of this book will be on this layer. The application security layer refers to all the constraints we establish in our applications to make sure that only the right people can do only the right things when working through the application. Applications, by default, are open to countless avenues of attack. An improperly secured application can allow an attacker to steal information from the application, impersonate other users, execute restricted operations, corrupt data, gain access to operating system level, and perform many other malicious acts. In this book, we will cover application-level security, which is the domain of Spring Security. Application-level security is achieved by implementing several techniques, and there are a few concepts that will help you understand better what the rest of the book will cover. These are the main concerns that Spring Security addresses to provide your applications with comprehensive protection against threats. In the following three subsections, I shall introduce •
Authentication
•
Authorization
•
ACLs
Authentication The process of authentication allows an application to validate that a particular user is who she claims she is. In the authentication process, a user presents the application with information about herself (normally, a username and a password) that no one else knows. The application takes this information and tries to match it against information it has stored—normally, in a database or LDAP1 (Lightweight Directory Access Protocol) server. If the information input by the user matches a record in the authentication server, the user is said to have successfully authenticated herself in the system. The application will normally create an internal abstraction representing this authenticated user in the system. Figure 1-1 shows the authentication mechanism.
1
LDAP will be explained in some detail in Chapter 7, where various authentication providers are covered.
2 www.it-ebooks.info
Chapter 1 ■ The Scope of Security
Present credentials
Authentication System
User
User/credentials storage
No
credentials valid?
Yes Authenticated User
Figure 1-1. Simple standard authentication mechanism
Authorization When a user is authenticated, that only means that the user is known to the system and has been recognized by it. It doesn’t mean that the user is free to do whatever she wants in said system. The next logical step in securing an application is to determine which actions that user is allowed to perform, and which resources she has access to, and make sure that if the user doesn’t have the proper permissions she cannot carry out that particular action. This is the work of the authorization process. In the most common case, the authorization process compares the user’s set of permissions against the permissions required to execute a particular action in the application, and if a match is found, access is granted. On the other hand, if no match is found, access is denied. Figure 1-2 shows the authorization mechanism.
Access Resource
User
Authorization layer
Permissions match. (user and resource)
Yes
Secured Resource
No User not allowed access
Figure 1-2. Simple authorization process. The authenticated user tries to access a secured resource
3 www.it-ebooks.info
Chapter 1 ■ The Scope of Security
ACLs Access control lists (ACLs) are part of the authorization process explained in the previous section. The key difference is that ACLs normally work at a finer grained level in the application. ACLs are simply a collection of mappings between resources, users, and permissions. With ACLs, you can establish rules like “User John has administrative permission on the blog post X” or “User Luis has read permission on blog post X.” You can see the three elements: user, permission, and resource. Figure 1-2 shows how ACLs work, as they are just a special case of the general authorization process.
Authentication and Authorization: General Concepts In this section, I shall introduce and explain some fundamental security concepts that you will be coming across frequently in the rest of the book: •
User The first step in securing a system from malicious attackers is to identify legitimate users and allow access to them alone. User abstractions are created in the system and given their own identity. These are the users that will later be allowed to use the system.
•
Credentials Credentials are the way that a user proves who he is. Normally, in the shape of passwords (certificates are also a common way of presenting credentials), they are data that only the owner of it knows.
•
Role In an application security context, a role can be seen as a logical grouping of users. This logical grouping is normally done so the grouped users share a set of permissions in the application to access certain resources. For example, all users with the role “admin” will have the same access and permissions to the same resources. Roles serve simply as a way to group permissions to execute determined actions, making users with those Roles inherit such permissions.
•
Resource By a resource, I mean, in this context, any part of the application that we want to access and that needs to be properly secured against unauthorized access—for example, a URL, a business method, or a particular business object.
•
Permissions Permissions refer to the access level needed to access a particular resource. For example, two users may be allowed to read a particular document, but only one of them is allowed to write to it. Permissions can apply either to individual users or to users that share a particular role.
•
Encryption This allows you to encrypt sensible information (normally passwords, but it can be something else, like cookies) so as to make it incomprehensible to attackers even if they get access to the encrypted version. The idea is that you never store the plain text version of a password, but instead store an encrypted version so that nobody but the owner of such a password knows the original one. There are three main kinds of encryption algorithms: •
One-way encryption These algorithms, referred as hashing algorithms, take an input string and generate an output number known as the message digest. This output number cannot be converted back into the original string. This is why the technique is referred to as one-way encryption. Here is the way to use it: A requesting client encrypts a string and sends the encrypted string to the server. The server may have access to the original information from a previous registration process, for example, and if it does, it could apply the same hash function to it. Then it compares the output from this hashing to the value sent by the client. If they match, the server validates the information. Figure 1-3 shows this scheme. Usually, the server doesn’t even need the original data. It could simply store the hashed version and then compare it with the incoming hash from the client.
4 www.it-ebooks.info
Chapter 1 ■ The Scope of Security Original data Sender
Original data Encrypted Data
Receiver
Encrypter
Encrypter
Encrypt original data
reject
No
matches?
Compare with received data
yes
Validate and accept
Figure 1-3. One-way encryption or hashing •
Symmetric encryption These algorithms provide two functions: encrypt and decrypt. A string of text is converted into an encrypted form and then can be converted back to the original string. In this scheme, a sender and a receiver share the same keys so that they can encrypt and decrypt messages on both ends of the communication. One problem with this scheme is how to share the key between the endpoints of the communication. A common approach is to use a parallel secure channel to send the keys. Figure 1-4 shows symmetric encryption at work. encrypt with key
Sender
Encrypted message
Receiver
encryption key decrypt with received key
Figure 1-4. Symmetric encryption. The two endpoints share the same encryption/decryption key
5 www.it-ebooks.info
Chapter 1 ■ The Scope of Security
•
Public key cryptography These techniques are based on asymmetric cryptography. In this scheme, a different key is used for encryption than for decryption. These two keys are referred as the public key, which is used to encrypt messages, and the private key, which is used to decrypt messages. The advantage of this approach over symmetric encryption is that there is no need to share the decryption key, so no one but the intended receiver of the information is able to decrypt the message. So the normal scenario is the following:
•
The intended recipient of messages shares its public key with everyone interested in sending information to it.
•
The senders encrypt the information with the receiver’s public key, and send the message.
•
The receiver uses its private key to decrypt the message.
•
No one else is able to decrypt the message as they don’t have the receiver’s private key.
Figure 1-5 shows the public key cryptography scheme. Sender
Receiver share public key
encrypt with public key encrypted data decrypt with private key
Figure 1-5. Public key cryptography The use of encryption achieves, among other things, two other security goals: •
Confidentiality Potentially sensitive information belonging to one user, or group of users, should be accessible only to this user or group. Encryption algorithms are the main helper in achieving this goal.
•
Integrity Data sent by a valid user shouldn’t be altered by a third entity on its way to the server, or in its storage. This is normally accomplished through the use of one-way cryptographic algorithms that make it almost impossible to alter an input and produce a corrupted message whose encrypted hash is the same as the original message (thus deceiving the receiver into thinking it is valid).
6 www.it-ebooks.info
Chapter 1 ■ the SCope of SeCurity
What to Secure Not every part of the application requires a strong security model, or even any security at all. If, for example, one part of your application is supposed to serve static content to everyone interested in it, you can simply serve this content. There probably are no security concerns to handle here. Anyway, when starting to work on a new application, you should think about the security constraints that your application will have. You should think about concerns like those in the following list and whether or not they apply to your particular use case: •
Identity management More than likely, your application will need to establish the identities of the different users that will be using it. Usually, your application will do different things for different users, so you need a way to associate users with certain functionality. You also need to be sure to protect each user’s identity information so that it can’t be compromised.
•
Secured connections In an Internet environment, where anyone in the world can potentially access your system and eavesdrop on other users’ accessing your system, you most likely will want to secure the communication of sensitive data using some kind of transport layer security—for example, SSL.
•
Sensitive data protection Sensitive data will need to be protected against malicious attacks. This applies both to the communication layer and to individual message transmission, as well as to credentials datastores. Encryption will be used in different layers to achieve the most secure application possible.
More Security Concerns There are many more security concerns than the ones explained so far. Because this is a Spring Security book and not a general application-security book, we will cover only things related to Spring Security. However, I think it is important that you understand there are many more security concerns than those addressed directly by Spring Security. Following is a quick overview of some of the most common ones. This is only intended to make you aware of their existence, and I recommend you consult a different source (such as a general software security textbook) to gain a better understanding of all these concerns: •
SQL (and other code) injection Validating user input is a very important part of application security. If data is not validated, an attacker could potentially write any kind of string as input (including SQL or server-side code) and send that information to the server. If the server code is not properly written, the attacker could wreak significant havoc, as she could execute any arbitrary code on the server.
•
Denial of service attacks These attacks consist of making the target system unresponsive to its intended users. This is normally done by saturating the server with requests so that it utilizes all the server’s resources and makes it unresponsive to legitimate requests.
•
Cross-site scripting and output sanitation A kind of injection can be done where the target is the client part of the application. The idea is that the attacker can make an application return malicious code inside the web pages returned, and thus execute it in the user’s browser. This way, the attacker invisibly executes actions using the real user’s authenticated session.
7 www.it-ebooks.info
Chapter 1 ■ The Scope of Security
Java Options for Security Java and Java EE out-of-the-box security solutions are very comprehensive. They cover areas ranging from a low-level permission system, through cryptography APIs, to an authentication and authorization scheme. The list of security APIs offered in Java is very extensive, as the following list of the main ones shows: •
Java Cryptography Architecture (JCA) This API offers support for cryptographic algorithms, including hash-digest and digital-signature support.
•
Java Cryptographic Extensions (JCE) This API mainly provides facilities for the encryption and decryption of strings and also secret key generation for symmetric algorithms.
•
Java Certification Path API (CertPath) This API provides comprehensive functionality for integrating the validation and verification of digital certificates into an application.
•
Java Secure Socket Extension (JSSE) This API Provides a standardized set of features to offer support for SSL and TLS protocols, both client and server, in Java.
•
Java Authentication and Authorization Service (JAAS) This API provides service for authentication and authorization in Java applications. It provides a pluggable system where authentication mechanisms can be plugged in independently to applications.
Spring Security’s main concerns are in the authentication/authorization realm. So it overlaps mainly with the JAAS Java API, although they can be used together, as you will see later in the book. Most of the other APIs are leveraged in Spring Security. For example, CertPath is used in X509AuthenticationFilter and JCE is used in the spring-security-crypto module.
Summary In this chapter, I introduced security from a general point of view. I explained in a very abstract way the main concerns in IT security and especially from an application point of view. I also described, very briefly, the main Java APIs that support security at different levels. You can see that this chapter was a very quick overview of security concerns. It is beyond the scope of this book to go any further than this on general topics, although we will study some of them in more depth when they apply to Spring Security. Obviously this is nothing like a comprehensive software security guide, and if you are interested in learning more about software security in general you should consult the specialized literature. The next chapter will introduce Spring Security as such.
8 www.it-ebooks.info
Chapter 2
Introducing Spring Security In this chapter, you will learn what Spring Security is and how you can use it to address security concerns about your application. We’ll build a simple application secured with Spring Security. We’ll start with a Servlet-based web application without any security, and then we’ll add security to it in a declarative, nonintrusive way. Also in this chapter, we’ll take a look at the framework’s source code, how to build it, and the different modules that together form the powerful Spring Security project.
What Is Spring Security? Spring Security is a framework dedicated to providing a full array of security services to Java applications in a developer-friendly and flexible way. It adheres to the well-established practices introduced by the Spring Framework. Spring Security tries to address all the layers of security inside your application. In addition, it comes packed with an extensive array of configuration options that make it very flexible and powerful. Recall from the introduction in Chapter 1 that it can be said that Spring Security is simply a comprehensive authentication/authorization framework built on top of the Spring Framework. Although the majority of applications that use the framework are web based, Spring Security’s core can also be used in standalone applications. Many things make Spring Security immediately attractive to Java developers. To name just a few, I compiled the following list: •
It’s built on top of the successful Spring Framework This is an important strength of Spring Security. The Spring Framework has become “the way” to build enterprise Java applications, and with good reason. It is built around good practices and two simple yet powerful concepts: dependency injection (DI) and Aspect-Oriented Programming (AOP). Also important is that a lot of developers have experience with Spring, so they can leverage that experience when introducing Spring Security in their projects.
•
It provides out-of-the-box support for many authentication models Even more important than the previous point, Spring Security supports out-of-the-box integration with Lightweight Directory Access Protocol (LDAP), OpenID, Form authentication, Certificate X.509 authentication, database authentication, Jasypt cryptography, and lots more. All this support means that Spring Security adapts to your security needs—and not only that, it can change if your needs change, without much effort involved for the developer. This is important from a business point of view as well because the application can either adapt to the corporate authentication services or implement its own, thus requiring only straightforward configuration changes. This also means that there is a lot less software for you to write, because you are making use of a great amount of ready-to-use code that has been written and tested by a large and
9 www.it-ebooks.info
Chapter 2 ■ Introducing Spring Security
active user community. You can, to a certain point, trust that this code works and use it with confidence. And if it does not work, you can always fix it and send a patch to those in charge of maintaining the project. •
It offers layered security services Spring Security allows you to secure your application at different levels, and to secure your web URLs, views, service methods, and domain model. You can pick and combine these features to achieve your security goals.
This is really flexible in practice. Imagine, for instance, that you offer services exposed through RMI, the Web, JMS, and others. You could secure all of these interfaces, but maybe it’s better to secure just the business layer so that all requests are secured when they reach this layer. Also, maybe you don’t care about securing individual business objects, so you can omit that module and use just the functionality you need. •
It is open source software Because it’s part of the general SpringSource portfolio, Spring Security is an open source software tool. It also has a large community and user base dedicated to testing and improving the framework. Having the opportunity to work with open source software is an attractive feature for most developers. I know that the ability to look into the source code of the tools you like and work with is an exciting prospect. Whether our goal is to improve the tools or simply to understand how they work internally, we developers love to read code and learn from it.
Where Does Spring Security Fit In? Spring Security is without question a powerful and versatile tool. But like anything else, it is not a tool that adapts to everything you want to do. Its offerings have a defined scope. Where and why would you use Spring Security? Here is a list of reasons and scenarios: •
Your application is in Java The first thing to take into account is that Spring Security is written in the Java language and is a framework to be used only in Java applications (and other JVM languages as you will see in future chapters). So if you plan to work in a non-JVM language, Spring Security won’t be of any use to you.
•
You need role-based authentication/authorization This is the main use case of Spring Security. You have a list of users and a list of resources and operations on those resources. You group the users in roles and allow certain roles to access certain operations on certain resources. That’s the core functionality.
•
You want to secure a web application from malicious users I mentioned before that Spring Security is mostly used in web application environments. When this is the case, the first thing to do is allow only the users that you want to have access to your application, while forbidding the all others from even reaching it.
•
You need to integrate with OpenID, LDAP, Active Directory, and databases as security providers If you need to integrate with a particular Users and Roles or Groups provider, you should take a look at the vast array of options Spring Security offers because integration might be already implemented for you, saving you from writing lots of unnecessary code. Sometimes you might not be exactly sure what provider your business will require to authenticate against. In this case, Spring Security makes your life easy by allowing you to switch between different providers in a painless way.
•
You need to secure your domain model and allow only certain users to access certain objects in your application If you need fine-grained security (that is, you need to secure on a per object, per user basis), Spring Security offers the Access Control List (ACL) module, which will help you to do just that in a straightforward way.
10 www.it-ebooks.info
Chapter 2 ■ Introducing Spring Security
•
You want a nonintrusive, declarative way for adding security around your application Security is a cross-cutting concern, not really a core business functionality of your application (unless you work in a security provider firm). As such, it is better if it can be treated as a separate and modular add-on that you can declare, configure, and manage independently of your main business concerns. Spring Security is built with this in mind. By using Servlet Filters, XML configuration, and AOP concepts, the framework tries not to pollute your application with security rules. Even when using annotations, they are still metadata on top of your code. They don’t mess with your code logic.
•
You want to secure your service layer the same way you secure your URLs, and you need to add rules at the method level for allowing or disallowing user access Spring Security allows you to use a consistent security model throughout the layers of your application because it internally enforces this consistent model itself. You configure users, roles, and providers in just one place, and both the service and web layers make use of this centralized security configuration in a transparent way.
•
You need your application to remember its users on their next visit and allow them access Sometimes you don’t want or need the users of your application to log in every time they visit your site. Spring supports out-of-the-box, remember-me functionality so that a user can be automatically logged in on subsequent visits to your site, allowing them full or partial access to their profile’s functionality.
•
You want to use public/private key certificates to authenticate against your application Spring Security allows you to use X.509 certificates to verify the identity of the server. The server can also request a valid certificate from the client for establishing mutual authentication.
•
You need to hide elements in your web pages from certain users and show them to some others View security is the first layer of security in a secured web application. It is normally not enough for guaranteeing security, but it is very important from a usability point of view because it allows the application to show or hide content depending on the user that is currently logged in to the system.
•
You need more flexibility than simple role-based authentication for your application For example, suppose that you want to allow access only to users over 18 years of age using simple script expressions. Spring Security 3.1 uses the Spring Expression Language (SpEL) to allow you to customize access rules for your application.
•
You want your application to automatically handle HTTP status codes related to authorization errors (401, 403, and others) The built-in exception-handling mechanism of Spring Security for web applications automatically translates the more common exceptions to their corresponding HTTP status codes—for example, AccessDeniedException gets translated to the 403 status code.
•
You want to configure your application to be used from other applications (not browsers) and allow these other applications to authenticate themselves against yours Another application accessing your application should be forced to use authentication mechanisms in order to gain access. For example, you can expose your application through REST endpoints that other applications can access with HTTP security.
•
You are running an application outside a Java EE Server If you are running your application in a simple web container like Apache Tomcat, you probably don’t have support for the full Java EE security stack. Spring Security can be easily leveraged in these environments.
11 www.it-ebooks.info
Chapter 2 ■ Introducing Spring Security
•
You are running an application inside a Java EE Server Even if you are running a full Java EE container, Spring Security is arguably more complete, flexible, and easy to use than the Java EE counterpart.
•
You are already using Spring in your application and want to leverage your knowledge of it I explained before some of the great advantages of Spring. If you are currently using Spring, you probably like it a lot. So you will probably like Spring Security as well.
Spring Security and Spring As I said before, Spring Security is part of the SpringSource portfolio of open source projects. There are many more projects from SpringSource, and they are driven by a large and dynamic community of users. Among the mainstream SpringSource projects are the following: •
Spring Security
•
Spring Batch
•
Spring Integration
•
Spring Web Services (WS)
•
Spring Social
•
Spring Web Flow
•
Spring Data
All these projects are built on top of the facilities provided by the Spring Framework itself, which is the original project that started it all. You can think of Spring as the hub of all these satellite projects, providing them with a consistent programming model and a set of established practices. The main points you will see throughout the different projects is the use of DI, XML namespace-based configuration, and AOP, which as you will see in the next section, are the pillars upon which Spring is built on. In the later versions of Spring, annotations have become the most popular way to configure both DI and AOP concerns. So Spring Security is just one more of these projects, and it is dedicated exclusively to addressing security concerns in your application. If you read the online documentation, you will find out that Spring Security started originally as a non-Spring project. It was originally known as The Acegi Security System for Spring, and it was not the big and powerful framework it is today. Originally, it dealt only with authorization and leveraged container-provided authentication. Because of public demand, the project started to get traction, as more people started using it and contributing to its continuously growing code base. This eventually led to it becoming a Spring Framework portfolio project, and then later it was rebranded as “Spring Security.” So the project, for many years now, has been under the SpringSource umbrella of projects, powered by The Spring Framework itself. But what exactly is the Spring Framework?
Spring Framework: A Quick Overview I have already mentioned the Spring Framework project quite a lot. It makes sense to give an overview of it at this point, because many of the Spring Security characteristics I will cover in the rest of the book rely on the building blocks of Spring. I admit I’m biased. I love Spring and have loved it for many years now. I think Spring has so many advantages and so many great things that I can’t start a new Java project without using it. Additionally, I tend to carry its concepts around when working with other languages and look for a way to apply them because they now feel so natural to me.
12 www.it-ebooks.info
Chapter 2 ■ Introducing Spring Security
There are many things that attract me to Spring, but the main ones are the two major building blocks of the framework: dependency injection (DI) and Aspect-Oriented Programming (AOP). Why are these two concepts so important? Both of them are important because they allow you to develop loosely coupled, single-responsibility, DRY (Don’t Repeat Yourself ) code practically by default. These two concepts, and Spring itself, are covered extensively in other books and online tutorials; however, I’ll give you a brief overview here.
Dependency Injection The basic idea of DI, a type of Inversion of Control (IoC), is simply that instead of having an object instantiate its needed dependencies, the dependencies are somehow given to the object. In a polymorphic way, the objects that are given as dependencies to the target object that depends on them are known to this target object just by an abstraction (like an interface in Java) and not by the exact implementation of the dependency. It’s easier to look at this in code than explain it. The object itself instantiates its dependencies (No dependency injection) public class NonDiObject { private Helper helper ; public NonDiObject ( ) { helper = new HelperImpl ( ) ; } public void doStuffWithHelp( ) { helper.help( ) ; } } In this example, every instance of NonDiObject is responsible for instantiating its own Helper in the constructor. You can see that it instantiates a HelperImpl, creating a tight, unnecessary coupling to this particular Helper implementation. The object receives its dependencies from some external source (with dependency injection) public class DiObject { private Helper helper ; public DiObject(Helper helper) { this.helper = helper; } public void doStuffWithHelp( ) { helper.help( ) ; } } In this version, the Helper is passed to the DiObject at construction time. DiObject is not required to instantiate any dependency. It doesn’t even need to know how to do that or what particular implementation type the Helper is, or where it comes from. It just needs a helper and uses it for whatever requirement it has. The advantage of this approach should be clear. The second version is loosely coupled to the Helper, depending only on the Helper interface, allowing the concrete implementation to be decided at runtime and thus giving lots of flexibility to the design.
13 www.it-ebooks.info
Chapter 2 ■ Introducing Spring Security
Spring dependency injection configuration is normally defined in XML files, although later versions have turned more to annotation-based configuration and Java-based configuration.
Aspect Oriented Programming (AOP) AOP is a technique for extracting cross-cutting concerns from the main application code and applying them in a transverse way across the points where they are needed. Typical examples of AOP concerns are transactions, logging, and security. The main idea is that you decouple the main business logic of your application from special-purpose concerns that are peripheral to this core logic, and then apply this functionality in a transparent, unobtrusive way through your application. By encapsulating this functionality (which is simply general application logic and not core business logic) in its own modules, they can be used by many parts of the application that need them, avoiding the need to duplicate this code all over the place. The entities that encapsulate this cross-cutting logic are referred to as Aspects in AOP terms. There are many implementations of AOP in Java. The most popular, perhaps, is AspectJ which requires a special compilation process. Spring supports AspectJ, but it also includes its own AOP implementation, known simply as Spring AOP, which is a pure Java implementation that requires no special compilation process. Spring AOP using proxies is available only at the public-method level and just when it is called from outside the proxied object. This makes sense because calling a method from inside the object won’t call the proxy; instead, it calls the real self object directly (basically a call on the this object). This is something very important to be aware of when working with Spring, and sometimes it is overlooked by novice Spring developers. Even when using its own AOP implementation, Spring leverages the AspectJ syntax and concepts for defining Aspects. Spring AOP is a fairly big subject, but the principle behind the way it works is not difficult to understand. Spring AOP works with the use of dynamically created proxy objects that take care of the AOP concerns around the invocation of your main business objects. You can think of the proxy and Spring AOP in general simply as a Decorator Pattern implementation, where your business object is the component and the AOP proxy is the decorator. Figure 2-1 shows a simple graphical representation of the concept. Thinking about it this way, you should be able to understand Spring AOP easily. The following code shows how the magic happens conceptually. Our business object, not transactional public class Business Object implements BusinessThing { public void doBusinessThing( ) { // Some business stuff } } Suppose you have an aspect for transactions. Spring creates dynamically at runtime an object that conceptually looks like the following code. Spring AOP magic public class BusinessObjectTransactionalDecorator implements BusinessThing { private BusinessThing component ; public BusinessObjectTransactionalDecorator(BusinessThing component ) { this . component = component ; }
14 www.it-ebooks.info
Chapter 2 ■ Introducing Spring Security
public void doBusinessThing( ) { // some start transaction code component.doBusinessThing( ) ; // some commit transaction code } } Again, remember this simple idea and Spring AOP should be easier to understand.
AOP concern
Applies to
proxied method
OriginalObject businessMethod
AOP pre-logic Results in Core Bussiness Logic ProxyObject originalObject businessMethod-decorated with AOP-
AOP post-logic
Figure 2-1. Spring AOP in action
An Initial Spring Security Secured Application Spring Security builds upon the concepts defined in the previous section and integrates nicely into the general Spring ecosystem. You need to understand those concepts well to take maximum advantage of Spring Security. However, you could start using Spring Security without really knowing all these details, and then learn them as you progress and look to do more advanced things. As many other programming books do, I will show an initial example of the subject with the familiar “Hello World” application. It will need a tweak to make sense for our purposes. So I’ll show the “Hello World” message just to authorized users and not allow unauthorized users to see the message. In this example, we will create a simple web project powered by Spring Security. It will be a simple Servlet-based web application. We will use Maven 3.0.4 throughout the book to build our projects as well as Java 1.7. To make sure that the examples in the book work as expected, try to use the same versions. All examples have been tested in both Linux (Ubuntu 12.04) and Mac OS X 10.7.4. Let’s create a simple Maven web project. From the command line, go to any folder you want to use as the root of your projects and execute the following: mvn archetype:generate -DgroupId=com.apress.pss -DartifactId=pss01 -DarchetypeArtifactId=mavenarchetype-webapp Now add the Servlet dependency to the new project. To do that, make the pom.xml file look like Listing 2-1.
15 www.it-ebooks.info
Chapter 2 ■ Introducing Spring Security
Listing 2-1. The pom.xml file with Servlet dependencies 4.0.0com.apress.psspss01war1.0-SNAPSHOTpss01 Maven Webapphttp://maven.apache.orgjavax.servletjavax.servlet-api3.0.1junitjunit4.5testorg.mortbay.jettyjetty-maven-plugin8.1.1.v20120215pss01 As you can see from the pom.xml listing, I included the Jetty plugin dependency. I will be working throughout the book mostly with the Jetty server because it embeds nicely in the Maven life cycle. Using any other container (like Apache Tomcat) to run the code from the book should be no problem, and it should work without issues. You have now created a standard Maven web application. The next task is to create a simple Servlet that will render the “Hello World” message. Create the following Servlet in the com.apress.pss.servlets package. See Listing 2-2.
■■Note Remember that you have to keep the conventional Maven file structure. This means you have to create the /src/main/java folders for your source classes if you don’t have them yet. Look at the Maven documentation online at http://maven.apache.org/ for more details.
16 www.it-ebooks.info
Chapter 2 ■ IntroduCIng SprIng SeCurIty
Listing 2-2. HelloWorldServlet package com.apress.pss.servlets; import java.io.IOException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(urlPatterns ={"/hello" } ) public class HelloWorldServlet extends HttpServlet { private static final long serialVersionUID = 2218168052197231866L ; @Override public void doGet (HttpServletRequest request , HttpServletResponse response){ try { response.getWriter( ).write( "Hello World" ) ; } catch(IOException e) { e.printStackTrace( ) ; } } } In the command line, in the root of the project (where the pom.xml file resides), execute the following: mvn jetty : run Then visit the URL http://localhost:8080/hello in the browser. You’ll see the expected “Hello World” message, as shown in Figure 2-2.
Figure 2-2. The “Hello World” message Now we want to secure this page and allow only authenticated users to be able to read the super top-secret “Hello World” message. To do this, we will use Spring Security.
17 www.it-ebooks.info
Chapter 2 ■ Introducing Spring Security
Let’s say that only the Scarvarez family (whose members are Carl, Mon, Bea, and Andr) is allowed to see this “Hello World” message. The rest of the world is not allowed to. To make this work, you need to do the following: 1.
Import the required Spring Framework and Spring Security libraries into the project.
2.
Configure the project to be aware of Spring Security.
3.
Configure the users and roles that will be part of the system.
4.
Configure the URLs that you want to secure.
I’ll give more detail about each of these steps in the next four sections.
Adding Spring Security (and Spring Core Itself ) to the Project In this section, we’ll start our journey into the inner workings of the framework and see its main building blocks and how it works. The information will be mostly introductory. I’ll conduct a full, in-depth review of the framework in the next chapter. I could just tell you what to add to the project to make the application work, but I think it’s better to tell you first what the different components of the framework are so that you can start coding with a better knowledge of how the framework is built. This means that I will tell you how to grab the source code of the project and build it, and then explain in a general way the different modules that make up the framework.
Spring Security Source Open source software has an invaluable characteristic for software developers: free access to all its source code. With this, we can understand how our favorite tools and frameworks work internally, and we also can learn a lot about the way other (perhaps very good) developers work, including what practices, techniques, and patterns they use. Free access to source code also enables us, in general, to gather ideas and experience for our own development. Not only that; as a more practical matter, having access to the source code allows us to debug these applications in the context of our application: we can find bugs or simply follow our application’s execution through them. That is the way I work many times, anyway. I really like understanding the frameworks I work with and learning from the great work and effort put into them by some very talented developers around the world. A lot of this book’s content is based on looking at the source code of Spring Security. Where appropriate, some of that code will be printed in the pages of this book to make points more clear. So let’s grab the Spring Security source code. Currently, Spring Security and most Spring projects live in Github. You probably know about Github (https://github.com/). However, if you don’t, you should definitely take a look at it because it has become a standard public source-code repository for many open source projects in a multitude of programming languages. Github (https://github.com/) is a repository, and a hosting service for Git repositories, with a very friendly management interface. The Spring Security project can be found inside the SpringSource general Github section at https://github.com/SpringSource/spring-security. To get the code, go to any location you want to have the project in your command-line terminal and execute the following command: git clone https://github.com/SpringSource/spring-security.git That’s it, now you have the Git repository cloned in your computer. Let’s go inside the newly created directory and see what is there. (See Figure 2-3.)
18 www.it-ebooks.info
Chapter 2 ■ Introducing Spring Security
Figure 2-3. Spring Security source code folder There are a lot of files and directories here. The first thing that is worth mentioning is that the current versions of Spring Security use Gradle as the build tool. Until version 2.5.0.M1, Spring Security used Maven as the build tool. Starting with version 3.0.0.RC2, Gradle was also included. Maven support was finally removed in version 3.1.0.M1, leaving only Gradle as the build system. Gradle is a build tool written in Groovy that uses an internal Domain Specific Language to specify the build configuration of your project. A detailed explanation of Gradle is outside the scope of this book, so I will just cover what you need to build Spring Security from the source code. You can see that in the top directory there is a file named gradlew. This is what is known as a Gradle Wrapper, and its main advantage is that it is a self-contained build script-tool. When you run it, it will download Gradle for you and use the downloaded Gradle to build your project. Go ahead and run gradlew build install from the top directory and wait for the project to build.
■■Note It is possible for the build to fail in your system. Because we are working in the master branch of the framework, a lot of code is being committed there all the time. It is not under my control if this build fails at any given time. Later, you will simply use a stable tag for developing the examples in the book. You should now have the project built and available in your current Maven repository. (Yes, when using Gradle, the dependencies can be simply stored in the Maven repository.) You can see that it is very straightforward to build the project from the source code.
19 www.it-ebooks.info
Chapter 2 ■ Introducing Spring Security
Setting the project build aside for now, take another look at the contents of the top project directory. Most of the folders in the directory correspond to individual subprojects or modules that break the functionality of Spring Security into more discrete and specialized units. Spring Security currently comprises several modules: •
core As its name indicates, this is the main module in the project. Every other module builds on top of this one because it provides core functionality to the rest of the framework. In this module, you can find the main interfaces and classes that establish the concepts and hook points that the rest of the system uses. It also contains the core implementations of the system, including the JDBC authentication support, the Java Authentication and Authorization Service (JAAS) authentication provider, the access voting system, MD5 and SHA password encoders, and a lot more.
•
web This module deals with the web-layer security of your application. It builds on the core and leverages its main abstractions and implementations to provide security to your web-based application. In this module, you can find the Servlet Filters that deal with the pre-processing and post-processing of Servlet requests, the Servlet Session management, the Secure Sockets Layer (SSL) support, the Remember Me support, the HTTP status code generation, and more web-related things.
•
config This module contains the namespace definition, the XSDs, and the validation and parsing rules of the different elements of the framework. When you write your configuration files for your application using the XML definitions for the different Spring Security configuration options, this module is responsible for parsing that XML and creating standard bean definitions from them, wiring them together, and getting them ready to be instantiated by the Spring context-loading process. Here, you will find the translation from namespaced xml elements to standard Spring Framework elements.
•
taglibs This module contains the taglib definitions and implementations for configuring security at the JSP level, allowing the view content to be conditionally rendered depending on the security constraints that you chose to define in the tags.
•
acl This module contains all the logic needed for the access control list (ACL) security model available with Spring Security. It contains the database model, including the SQL DDLs (Data Definition Language), to establish the rules and relationships between domain entities and their security restrictions. I will cover ACLs in depth in the chapter dealing with business-layer security.
•
remoting Leverages security support for remoting protocols. Currently supported is the Spring HttpInvoker, which uses BASIC authentication, and Java RMI. This module takes care of propagating the security context to these remote services.
•
ldap Contains all the functionality required to connect to and authenticate against an LDAP service, including the AuthenticationProvider and UserDetails LDAP implementations. It also includes an embeddable ApacheDS server implementation for testing and development purposes.
•
cas This is the Spring Security support for the JA-SIG Central Authentication Service (CAS) single sign-on service. So if you want to authenticate with CAS, you need this module in your application.
•
openid Again, this module is well described by its name. It contains OpenID support, including the OpenID-specific AuthenticationProvider, and it leverages the openid4java library http://code.google.com/p/openid4java/.
20 www.it-ebooks.info
Chapter 2 ■ Introducing Spring Security
•
aspects Contains an AspectJ Aspect that cuts into the execution of methods with various security annotations (@Secured, @PreAuthorize). It delegates to a configured Security Interceptor.
•
crypto Provides various encryption, ciphering, and codec implementations, as well as some password-encoder strategies, to use in your application. There is also a samples folder inside the top directory. Here, you can find some example applications to help you get to know the framework and how to configure your applications.
Finally, you need to add security to the application. For our project example, you need to depend on the core, web, and config modules. So we add them to our pom.xml, as shown in Listing 2-3. Listing 2-3. Spring Security Maven dependencies org.springframework.securityspring-security-core3.1.3.RELEASEorg.springframework.securityspring-security-config3.1.3.RELEASEorg.springframework.securityspring-security-web3.1.3.RELEASEcommons-loggingcommons-logging1.1.1
■■Note As you probably know, Maven handles transitive dependencies. In our example, the dependencies we just defined in Listing 2-3 will translate into more real-world dependencies, including the needed dependencies from the Spring Framework. This includes Spring web support and the core libraries for DI and ApplicationContext management.
Configuring the Web Project To Be Aware of Spring Security To activate Spring Security in a Java web application, you need to configure a particular Servlet filter that will take care of pre-processing and post-processing the requests, as well as managing the required security constraints. Make your web.xml look like Listing 2-4.
21 www.it-ebooks.info
Chapter 2 ■ Introducing Spring Security
Listing 2-4. The web.xml with Spring Security integration springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy springSecurityFilterChain/* You are specifying here that requests to all URLs (/*) will go through the DelegatingFilterProxy filter. The name of this filter is important because it is the default name Spring Security will use to configure its filter chain. I will explain all this in detail later. For now, let’s continue with the example. You now need to define the actual Spring Security configuration. For that, create a file named applicationContext-security.xml in the WEB-INF folder and put the content shown in Listing 2-5 into it: Listing 2-5. A Spring application context with the security configuration applicationContext-security.xml
22 www.it-ebooks.info
/> /> /> />
Chapter 2 ■ Introducing Spring Security
That’s all we need to secure the given URL for the Scarvarez family only. You can see that you are defining a role called "ROLE SCARVAREZ MEMBER" that is the only one that has access to the url "/hello". You also see that you are assigning this role to all the Scarvarez family members. To make this work, you need to reference this new file in the web.xml. So open it again and add the content from Listing 2-6 to it. Listing 2-6. Listener configuration for loading the Spring application context. It should be added at the top of the file after the element. Look at the Servlet specification for details org.springframework.web.context.ContextLoaderListener contextConfigLocation /WEB-INF/applicationContext-security.xml That’s all the configuration needed at the moment. Go to the command line, execute mvn jetty:run again, point your browser to the URL http://localhost:8080/hello, and press the Enter key. What do you see? You should see a login page asking you to provide the user name and password as Figure 2-4 shows. Go ahead, impersonate a Scarvarez family member, and try to log in. For example, use car as the user name and scarvarez as the password. You should now be allowed access to the same “Hello World” page as Figure 2-2 showed before. If you instead tried to log in as a nonexistent user, you should get the page shown in Figure 2-5.
Figure 2-4. Login page
23 www.it-ebooks.info
Chapter 2 ■ Introducing Spring Security
Figure 2-5. The result of trying to log in with an incorrect user name and password combination This all seems pretty cool for so little work. As a matter of fact, we haven’t written any code; we just did some XML configuration. Now our Servlet-based application is secured to allow access only for authenticated users. The fact that we are using this XML configuration to secure the application is very relevant, because it shows that we are adding security in a completely unobtrusive and declarative way to an application that is otherwise insecure. This is all well and nice, but right now it looks like magic. Where does this login form come from? How does this tag secure the URLs?. In the next section, we will cover the basics of what is going on. A full, in-depth explanation of Spring Security follows in the next chapter.
Understanding the Simple Application You can see that with some relatively simple configuration, we managed to secure our regular Java Web application with a functional role-based authentication/authorization scheme. I can imagine an application for which this simple configuration would be enough security—for example, an application that has an admin section and an open-to-all section, and where the admin user (or users) is predefined beforehand. Even for this simple-looking configuration, there is a lot going on under the hood of your application, but Spring Security takes care of the heavy work and leaves you to worry about what you need to worry about:—establishing your security rules. But what is happening here exactly? What follows is a general overview of the process, followed by a graphical representation of it: 1.
Start the application (in this case, by using jetty run). When the application is loading, it looks at its web.xml file. There it finds your Listener declaration ContextLoaderListener. This listener uses the path defined in the context param contextConfigLocation to find the Spring xml file that it needs to load.
2.
The applicationContext-security.xml file is loaded and parsed by Spring. This file uses the custom Spring Security namespace to define, in a friendly way, the different security concerns that we need to address.
3.
The supporting beans for using Spring Security are instantiated and initialized by the Context LoaderListener after parsing the XML in the normal Spring way.
24 www.it-ebooks.info
Chapter 2 ■ Introducing Spring Security
4.
Your web.xml filter (of class DelegatingFilterProxy) is instantiated and added to the filter chain of your web application. This Servlet filter implementation is a Spring contextaware filter that simply delegates the filtering to Spring-defined filter beans. In this case, it will delegate to a bean named springSecurityFilterChain, which is defined implicitly by your use of the Spring Security XML namespace. At this point, the application is initialized.
5.
You make a request to the URL http://localhost:8080/hello. The request gets to the Jetty server, Jetty wraps it into an HttpServletRequest, and sends it through to the filter springSecurityFilterChain. This filter is a composite of numerous filters that deal with different parts of the authentication/authorization process.
6.
Somewhere in the filter chain, Spring notices that you are trying to access a URL that needs the special_role "ROLE_SCARVAREZ_MEMBER" to be able to continue. Spring looks at the current credentials in the web session and can’t find any user details with this role.
7.
When Spring realizes (inside one of the filters) that there is no current user in the session with permission to access the requested URL, it generates a redirect response that redirects the user browser to a login URL that contains a self-generated login form.
8.
The new form is rendered. When the form is filled out and submitted, the request is sent to a special URL"/j_spring_security_check" in the current domain, which in our case is http://localhost:8080. When Spring Security detects that this URL has been requested, it tries to extract the user name and password from the request and creates an authentication request.
9.
The authentication request is sent to an authentication manager, which you configured in your XML file. Internally, the manager finds the user (if it exists) and fills one authentication object with the user details, credentials, and authorities. This is now an authenticated user, and a session is associated with this user. If the details contained in the authentication don’t match any user, an exception is thrown indicating that. This exception is then handled by a different flow that takes care of presenting the login screen again with the error message.
10.
A redirect response is created to the original requested URL.
11.
When this new redirect request gets in the system, it goes through the filters again, but this time the authentication ones are not called because there is already an authenticated user. This time, a different filter will get called. This filter compares the authenticated user’s authorities against the authorities needed to have in order to access the requested resource (in this case, the /hello URL).
12.
The framework decides that the user is allowed to access the resource, so the filter chain finally forwards the request to the Servlet. You see the “Hello World” message. This is the happy-path scenario of how the flow works. If anything goes wrong (for example, if the user is not found), Spring Security normally throws an exception and, in the case of web security, maps that exception to a relevant HTTP status code. This will be covered later in the book as we go deeper into the work of the framework.
Figure 2-6 shows all this interaction.
25 www.it-ebooks.info
Chapter 2 ■ Introducing Spring Security
Filters At Work Start Application
Is URL secured?
Filter 1 Context Listener loads Spring Secuity configuration
Filter 1
Yes
.......
Authenticate
Yes
Filter X
No Is user authenticated
Servlet Filters get loaded.
No
Redirect to Login Form
Yes
Get /hello
No
Is user authorised
Yes
Show Access Denied
Show Resource
Figure 2-6. Spring Security from load up to first request Wow! That is a lot of work that Spring Security is doing on our behalf while we just had to define some simple XML configuration. And this is only the tip of the iceberg. In the next chapter, I will dive deeply into how all this works internally when we look at the architecture of Spring Security.
Summary Right now, you should have a good idea of what Spring Security is and what it is useful for. You built your first Spring Security–powered application. Along the way, I introduced some of the major architectural and design principles behind it and how they are layered on top of the great Spring Framework. I also gave you a quick look at the source code of the Spring project and at the different modules that make up the framework. I introduced dependency injection and AOP and gave an overview of the step-by-step process a typical web application goes through when it is secured with Spring Security. In the next chapter, I’ll go deep into the architecture and design of the framework.
26 www.it-ebooks.info
Chapter 3
Spring Security Architecture and Design In the previous chapter, I developed an initial application secured with Spring Security. I gave an overview of the way this application worked and looked in detail at some of the Spring Security components that are put into action in common Spring Security–secured application. In this chapter, I am going to extend those explanations and delve deeply into the framework. I’ll look at the main components of the framework, explain the work of the servlet filters for securing web applications, look at how Spring AOP (Aspect Oriented Programming) helps you add security in an unobtrusive way, and in general, show how the framework is designed internally.
What Components Make Up Spring Security? In this section, I’ll take a look at the major components that make Spring Security work. I’ll offer a big-picture overview of the framework and then delve deeper into each major component.
The 10,000-Foot View Spring Security is a relatively complex framework that aims to make it easy for the developer to implement security in an application. At the most general level, it’s a framework composed of intercepting rules for granting, or not granting, access to resources. Figure 3-1 illustrates this.
27 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design Spring Security
Secured Resource
Request Granted Access Actor Resource
Post Processed
Figure 3-1. Spring Security 10,000-foot overview From this view, you can think of Spring Security simply as an extra layer built on top of your application, wrapping specific entry points into your logic with determined security rules.
The 1,000-Foot View Going into a little more detail, we arrive at AOP and servlet filters. Spring Security’s interception model of security applies to two main areas of your application: URLs and method invocations. Spring Security wraps around these two entry points of your application and allows access only when the security constraints are satisfied. Both the method call and the filter-based security depend on a central Security Interceptor, where the main logic resides to make the decision whether or not access should be granted. In Figure 3-2, you can see this more detailed overview of the framework.
Metadata Secured Mathod or URL
Http Request
Filter Chain
SecurityInterceptor
Resource
Method Call
Figure 3-2. In this view, both method calls and HTTP requests try to access a resource, but first they must go through SecurityInterceptor
28 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
The 100-Foot View Spring Security might seem simple conceptually, but internally there is a lot going on—in a very well-built software tool. This next overview will show you the main collaborating parts that participate in the general process of ensuring that your security constraints are enforced. This is particularly achievable with an open source project like Spring Security which allows you to get into the framework itself and appreciate its design and architecture by accessing directly the source code.. After that, I’ll delve deeper into the implementation details. For me, what follows is the best way to understand Spring Security from the inside. The enumeration of what I consider to be the main components of the framework will help you know where everything belongs and how your application is enforcing the security rules that you specify for it. Figure 3-3 provides an illustration.
XML Namespace
Servlet Filters
AuthenticationManager
AuthenticationProvider
UserDetailsService
UserDetails
SecurityContexHolder
SecurityInterceptor
AccessDecisionManager
Authentication
ConfigAttribute
ACL
Taglib
Figure 3-3. The key components of Spring Security
29 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
The Security Interceptor One of the most important components of the framework is the Security Interceptor. With the main logic implemented in AbstractSecurityInterceptor and with two concrete implementations in the form of FilterSecurityInterceptor and MethodSecurityInterceptor (as shown in Figure 3-4), the Security Interceptor is in charge of deciding whether a particular petition should be allowed to go through to a secured resource. MethodSecurityInterceptor, as its name should tell you, deals with petitions directed as method calls, while FilterSecurityInterceptor deals with petitions directed to web URLs.
Figure 3-4. SecurityInterceptor UML class diagram simplified The Security Interceptor works with a preprocessing step and a postprocessing step. In the preprocessing step, it looks to see whether the requested resource is secured with some metadata information (or ConfigAttribute). If it is not, the request is allowed to continue its way either to the requested URL or method. If the requested resource is secured, the Security Interceptor retrieves the Authentication object from the current SecurityContext. If necessary, the Authentication object will be authenticated against the configured AuthenticationManager. After the object is authenticated, AccessDecisionManager is called to determine if the authenticated entity is able to finally access the resource. AccessDecisionManager throws an AccessDeniedException if the authenticated entity is not allowed to access the resource. If AccessDecisionManager decides that the Authentication object is allowed to access the resource, the Authentication object is passed to RunAsManager if this is configured. If RunAsManager is not configured, a no-op implementation is called. RunAsManager returns either null (if it’s not configured to be used) or a new Authentication object containing the same principal, credentials, and granted authorities as the original Authentication object, plus a new set of authorities based on the RUN_AS that is being used. This new Authentication object is put into the current SecurityContext. After this processing, and independently of whether or not a run-as Authentication object is used, the Security Interceptor creates a new InterceptorStatusToken with information about the SecurityContext and the ConfigAttributes. This token will be used later in the postprocessing step of the Security Interceptor. At this point, the Security Interceptor is ready to allow access to the secured resource, so it passes the invocation through and the
30 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
particular secured entity (either a URL or a method) is invoked. After the invocation returns, the second phase of the Security Interceptor comes into play, and the postprocessing begins. The postprocessing step is considerably simpler, and it involves only calling a AfterInvocationManager’s decide method if there is one configured. In its current implementation AfterInvocationManager delegates to instances of PostInvocationAuthorizationAdvice, which ultimately can filter the returned objects or throw a AccessDeniedException if necessary. This is the case if you are using the postinvocation filters in method-level security, as you will see in the following chapter. In the case of web security, the AfterInvocationManager is null. That is a lot of work for the Security Interceptor. However, because the framework is nicely modular at the class level, you can see the Security Interceptor simply delegates most of the task to a series of well-defined collaborators, which in a very SRP (Single Responsibility Principle) way focus on single, narrowly scoped responsibilities. This is good software design and an example you should emulate. As shown in Listing 3-1, I paste the main parts of the code from the AbstractSecurityInterceptor itself so that you can see the things I’ve been talking about. I include some comments in the code so that you can understand better what it does. My comments start with // ----. Listing 3-1. AbstractSecurityInterceptor protected InterceptorStatusToken beforeInvocation(Object object) { Assert.notNull(object, "Object was null"); final boolean debug = logger.isDebugEnabled(); // // // // // //
Here we are checking if this filter is able to process a particular type of object. For example FilterSecurityInterceptor is able to process FilterInvocation objects. MethodSecurityInterceptor is able to process MethodInvocation objects.
if (!getSecureObjectClass().isAssignableFrom(object.getClass())) { throw new IllegalArgumentException("Security invocation attempted" + " for object " + object.getClass().getName() + " but " + "AbstractSecurityInterceptor only configured to support" + " secure objects of type:" + getSecureObjectClass()); } // // // // //
Here we are retrieving the security metadata that maps to the object we are receiving. So if we are receiving a FilterInvocation, the request is extracted from it and used to find the ConfigAttribute (s) that match the request path pattern
Collection attributes = this .obtainSecurityMetadataSource().getAttributes(object); if (attributes == null || attributes.isEmpty()) { if (rejectPublicInvocations) { throw new IllegalArgumentException("Secure object invocation " + "" + object + " was denied as public invocations are not allowed " + "via this interceptor. " + "This indicates a " + "configuration error because the " + "rejectPublicInvocations property is set to 'true'"); }
31 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
if (debug) { logger.debug("Public object - authentication not attempted"); } publishEvent(new PublicInvocationEvent(object)); return null; // no further work post-invocation } if (debug) { logger.debug("Secure object: " + object + "; Attributes: " + attributes); } if (SecurityContextHolder.getContext().getAuthentication() == null) { credentialsNotFound(messages.getMessage ("AbstractSecurityInterceptor.authenticationNotFound", "An Authentication object was not found in the " + "SecurityContext"), object, attributes); } Authentication authenticated = authenticateIfRequired(); // Here we are calling the decision manager to decide if // authorization is granted or not. // // // //
This will trigger the voting mechanism, and in case that access is not granted an exception should be thrown.
try { this.accessDecisionManager.decide(authenticated, object, attributes); } catch (AccessDeniedException accessDeniedException) { publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, accessDeniedException)); throw accessDeniedException; } if (debug) { logger.debug("Authorization successful"); } if (publishAuthorizationSuccess) { publishEvent(new AuthorizedEvent(object, attributes, authenticated)); } // Here it will try to use the run-as functionality of Spring // Security that allows a user // to impersonate another one acquiring its security roles, // or more precisely, its // GrantedAuthority (s) Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
32 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
if (runAs == null) { if (debug) { logger.debug("RunAsManager did not change Authentication " + "object"); } // no further work post-invocation return new InterceptorStatusToken(SecurityContextHolder .getContext(), false, attributes, object); } else { if (debug) { logger.debug("Switching to RunAs Authentication: " + runAs); } SecurityContext origCtx = SecurityContextHolder.getContext(); SecurityContextHolder.setContext(SecurityContextHolder .createEmptyContext()); SecurityContextHolder.getContext().setAuthentication(runAs); // need to revert to token.Authenticated post-invocation return new InterceptorStatusToken(origCtx, true, attributes, object); } // If the method has not thrown an exception at this point, // it is safe to continue // the invocation through to the resource. Authorization has // been granted. } protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) { if (token == null) { // public object return returnedObject; } if (token.isContextHolderRefreshRequired()) { if (logger.isDebugEnabled()) { logger.debug("Reverting to original Authentication: " + token .getSecurityContext().getAuthentication()); } SecurityContextHolder.setContext(token.getSecurityContext()); } // If there is an afterInvocationManager configured, // it will be called. // It will take care of filtering the return value or actually // throwing an exception // if it is relevant to do so. if (afterInvocationManager != null) { // Attempt after invocation handling try { returnedObject = afterInvocationManager.decide(token .getSecurityContext().getAuthentication(), token.getSecureObject(), token.getAttributes(), returnedObject);
33 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
} catch (AccessDeniedException accessDeniedException) { AuthorizationFailureEvent event = new AuthorizationFailureEvent( token.getSecureObject(), token.getAttributes(), token.getSecurityContext().getAuthentication(), accessDeniedException); publishEvent(event); throw accessDeniedException; } } // Here is the full authorization cycled finished. // The response is returned to the caller. return returnedObject; } The Security Interceptor lies at the core of the Spring Security framework. Every call to a secured resource in Spring Security passes through this interceptor. The AbstractSecurityInterceptor shows its versatility when you realize that two not very related kinds of resources (URL endpoints and methods) leverage more of the functionality of this abstract interceptor. This, once again, sh-ows the effort put into the design and implementation of the framework. Figure 3-4 shows the interceptor in a UML (Unified Modeling Language) class diagram. And Figure 3-5 shows a simplified sequence diagram.
Figure 3-5. AbstractSecurityInterceptor sequence diagram simplified We know how the Security Interceptors work, but how do they come to be? How do they know what to intercept? The answer to that lies in the next few components, so keep reading.
The XML Namespace The XML namespace is of extreme importance to the general appeal and usability of the framework, yet it is, in theory, not strictly necessary. If you know how the Spring Framework’s namespaces work, you probably have a good idea of what is going on when you define your security-specific XML configuration in your application context definition files. If you don’t know how they work, maybe you think Spring is somehow made aware of how to treat these specific elements and how to load them in the general Spring application context. Either way, here I will explain in some detail the process behind the definition of a custom namespace in Spring, and particularly, the elements in the Spring Security namespace.
34 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
Originally, Spring did not support custom XML. All that Spring understood was its own classes defined in the standard Spring Core namespace, where you can define s on a bean-to-bean basis and can’t really define anything conceptually more complex without adding that complexity yourself to the configuration. This −based configuration was, and still is, very good for configuring general-purpose bean instances, but it could get messy really fast for defining more domain-specific utilities. And beyond being messy, it is also very poor at expressing the business domain of the beans you are defining. I’ll explore this manual configuration later in the book, but for standard cases it is not needed, and you should simply use the namespace. However, keep in mind that under the hood the namespace is nothing more than syntactic sugar. At the end of the day, you still end up with standard Spring beans and objects. Spring 2.0 introduced support for defining custom XML namespaces. Since then, a lot of projects have made use of this facility, making them more attractive to work with. An XML custom namespace is simply an XML-based Domain Specific Language (DSL), guided by the rules of an XML schema (xsd) file, that allow developers to create Spring beans using concepts and a syntax more in synch with the programming concerns they are trying to model.
■■Note A DSL is a language customized to represent the concepts of a particular application domain. Sometimes, a whole new language is created to support the new domain. These are referred to as external DSLs. Some other times, an existing language is tweaked to allow for new expressions that represent the concepts of the domain. These are referred to as internal DSLs. In the case presented in this chapter, you are using a general-purpose language (XML); however, you are defining certain constraints about the elements (using XSD) and thus are creating an internal DSL to represent security concepts. To make Spring aware of a new namespace is really simple. (That’s not to say it is simple to actually parse the information of the XML and convert it to beans—this depends on the complexity of your DSL.) All you need is the following: •
An xsd file defining your particular XML structure
•
A spring.schemas file where you specify the mapping between a URL-based schema location and the location of your xsd file in your classpath
•
A spring.handlers file where you specify which class is in charge of handling everything related to your namespace
•
A bunch of parser classes that will be in charge of parsing each of the top elements defined in your XML file
In Chapter 8 you will see some examples of how to create a new namespace element and integrate it with Spring Security. For Spring Security, all the namespace configuration-related information resides in the config module. In Figure 3-6, you can see the expanded structure of the config module as seen in the Eclipse integrated development environment (IDE).
35 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
Figure 3-6. Spring Security’s config module file structure The files spring.handlers and spring.schemas should reside on the META-INF directory in the classpath so that Spring can find them there. OK, so enough of the general namespace information. More specifically, how does the Spring Security namespace work? When you create a Spring -based application using XML-defined application context configuration with some of the Spring Security namespace definitions, and you run the application, when it starts to load up, it looks in the application context’s namespace definitions at the top of the XML configuration file. It will find the reference to the Spring Security namespace (normally a reference like this xmlns:security="http://www.springframework.org/ schema/security"). Using the information from the mapping file spring.handlers, it will see that the file to handle the security elements is the final class, org.springframework.security.config.SecurityNamespaceHandler. Spring calls the parse method of this class for every top element in the configuration file that uses the security namespace. Figure 3-7 shows the load-up sequence for this process.
36 www.it-ebooks.info
Chapter 3 ■ Spring SeCurity arChiteCture and deSign
Start Parse XML
Start up
Found Schema configurations
Yes No Look for spring.schemas
Initialize Context
Found Spring.schemas
Load xsd
Look for spring.handlers
Define simple Spring beans
Invoke namespace handler
Found spring.handlers
Figure 3-7. Sequence of loading up a Spring namespace SecurityNamespaceHandler delegates to a series of BeanDefinitionParser objects for the individual parsing of each top-level element. The whole list of elements supported in the Spring Security namespace configuration are defined in the class org.springframework.security.config.Elements as constants. This class is shown in Listing 3-2. Listing 3-2. Constants for all the Spring Security namespace elements package org.springframework.security.config; public abstract class Elements { public public public public public public public public public public public public public public
Chapter 3 ■ Spring Security Architecture and Design
public static final String EXPRESSION_HANDLER = "expression-handler"; public static final String INVOCATION_HANDLING = "pre-post-annotation-handling"; public static final String INVOCATION_ATTRIBUTE_FACTORY = "invocation-attribute-factory"; public static final String PRE_INVOCATION_ADVICE = "pre-invocation-advice"; public static final String POST_INVOCATION_ADVICE = "post-invocation-advice"; public static final String PROTECT = "protect"; public static final String SESSION_MANAGEMENT = "session-management"; public static final String CONCURRENT_SESSIONS = "concurrency-control"; public static final String LOGOUT = "logout"; public static final String FORM_LOGIN = "form-login"; public static final String OPENID_LOGIN = "openid-login"; public static final String OPENID_ATTRIBUTE_EXCHANGE = "attribute-exchange"; public static final String OPENID_ATTRIBUTE = "openid-attribute"; public static final String BASIC_AUTH = "http-basic"; public static final String REMEMBER_ME = "remember-me"; public static final String ANONYMOUS = "anonymous"; public static final String FILTER_CHAIN = "filter-chain"; public static final String GLOBAL_METHOD_SECURITY = "global-method-security"; public static final String PASSWORD_ENCODER = "password-encoder"; public static final String SALT_SOURCE = "salt-source"; public static final String PORT_MAPPINGS = "port-mappings"; public static final String PORT_MAPPING = "port-mapping"; public static final String CUSTOM_FILTER = "custom-filter"; public static final String REQUEST_CACHE = "request-cache"; public static final String X509 = "x509"; public static final String JEE = "jee"; public static final String FILTER_SECURITY_METADATA_SOURCE = "filter-security-metadata-source"; public static final String METHOD_SECURITY_METADATA_SOURCE = "method-security-metadata-source"; @Deprecated public static final String FILTER_INVOCATION_DEFINITION_SOURCE = "filter-invocation-definition-source"; public static final String LDAP_PASSWORD_COMPARE = "password-compare"; public static final String DEBUG = "debug"; public static final String HTTP_FIREWALL = "http-firewall"; } From the list of elements presented in the previous class, the top-level ones as used in the XML configuration files are as follows (in the previous listing, I refer to them by the name of the constant and not by the XML element name): •
LDAP_PROVIDER. This element is used to configure the Lightweight Directory Access Protocol (LDAP) authentication provider for your application in case you require one.
•
LDAP_SERVER. This element is used to configure an LDAP server in your application.
•
LDAP_USER_SERVICE. This element configures the service for retrieving user details from an LDAP server and populating that user’s authorities (Spring Security uses the term “authorities” to refer to the permission names that are granted to a particular user. For example ROLE_USER is an authority).
•
USER_SERVICE. This element defines the in-memory user service where you can store user names, credentials, and authorities directly in the application context definition file.
•
JDBC_USER_SERVICE. This element allows you to set up a database-driven user service, where you specify a DataSource and the queries to retrieve the user information from a database.
38 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
•
AUTHENTICATION_PROVIDER. This element defines a DaoAuthenticationProvider, which is an authentication provider that delegates to an instance of UserDetailsService. The UserDetailsService can be any of the ones defined in the previous three bullet points, or a reference to a customized one.
•
GLOBAL_METHOD_SECURITY. This element is in charge of setting up the global support in your application to the annotations @Secured, @javax.annotation.security.RolesAllowed, @PreAuthorize, and @PostAuthorize. This element will be the one that will handle the registration of a method interceptor that will be aware of all the bean’s methods metadata in order to apply the corresponding security advice.
•
AUTHENTICATION_MANAGER. This element registers a global ProviderManager in the application and sets up the configured AuthenticationProviders on it.
•
METHOD_SECURITY_METADATA_SOURCE. This element registers a MapBasedMethodSecurityMetadataSource in the application context. It will hold a Map>. It does this so that when a request is made to a method, the method can be retrieved and its security constraints can be checked.
•
DEBUG. For development purposes, this element registers a DebugFilter in the security filter chain.
•
HTTP. This is the main element for a web-based secure application. The HTTP element is really powerful. It allows the definition of URL-based security-mapping strategies, the configuration of the filters, the Secure Sockets Layer (SSL) support and other HTTP-related security configurations.
•
HTTP_FIREWALL. This element uses a firewall element and adds it to the filter chain if it is configured. The firewall referenced should be an implementation of Spring’s own HttpFirewall interface.
•
FILTER_INVOCATION_DEFINITION_SOURCE. This element has been deprecated. See the following one.
•
FILTER_SECURITY_METADATA_SOURCE. This element wraps a list of elements. These elements map the relationship between URLs and the ConfigAttributes required for accessing those URLs.
•
FILTER_CHAIN. This element allows you to configure the Spring Security filter chain that will be used in the application, which filters you want to add to the chain, and a request matcher if you want to customize how the chain matches requests. The most important request matchers are: ant based and regexp based.
You will be using the Spring Security namespace thoroughly throughout the book, so many of the elements described here will be revisited in later chapters.
The Filters and Filter Chain The filter chain model is what Spring Security uses to secure web applications. This model is built on top of the standard servlet filter functionality. Working as an Intercepting Filter Pattern, the filter chain in Spring Security is built of a few single-responsibility filters that cover all the different security constraints required by the application. The filter chain in Spring Security preprocesses and postprocesses all the HTTP requests that are sent to the application and then applies security to URLs that require it.
39 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
The Spring Security filter chain is made up of Spring beans; however, standard servlet-based web applications don’t know about Spring beans. For this reason, a special servlet filter is needed that can cross the boundaries between the standard servlet API and life cycle and the Spring application where the bean filters will reside. This is the job of the org.springframework.web.filter.DelegatingFilterProxy defined in the web.xml, which will use under the hood the WebApplicationContextUtils.getWebApplicationContext utility method to retrieve the root application context of the application. These two classes are from the Spring Framework, not from Spring Security. Figure 3-8 shows the configuration of the filter chain.
Figure 3-8. Understanding the Spring Security filter configuration. The filter in the web.xml file has the same name as the bean in the Spring application context so that the listener can find it The filter chain will be fully explained in Chapter 4. However, here I’ll provide an overview of which filters are available and what they do. The available filters are defined as enums in the file org.springframework.security. config.http.SecurityFilters. The enums are then referenced later in the startup process when instantiating the bean definitions for each filter. Here are the defined filters: •
CHANNEL_FILTER. This filter ensures that the request is handled by the correct channel— meaning, in most cases, it determines whether or not the request is handled by HTTPS.
•
CONCURRENT_SESSION_FILTER. This filter is part of the concurrent session-handling mechanism. Its main function is to query the session to see if it has expired (which happens mainly when the maximum number of concurrent sessions per user are reached) and to log out the user if that is the case.
•
SECURITY_CONTEXT_FILTER. This filter populates SecurityContextHolder with a new or existing security context to be used by the rest of the framework.
•
LOGOUT_FILTER. This filter is based, by default, on a particular URL invocation (/j_spring_security_logout). It takes care of the logout process, including tasks such as clearing the cookies, removing the remember-me information, and clearing the security context.
•
X509_FILTER. This filter extracts the principal and credentials from an X509 certificate using the class java.security.cert.X509Certificate and attempts to authenticate with these pre-authenticated values.
•
PRE_AUTH_FILTER. This filter is used with the J2EE authentication mechanism. The J2EE authenticated principal will be used as the pre-authenticated principal in the framework.
•
FORM_LOGIN_FILTER. This filter is used when a user name and password is required on a login form. This filter takes care of authenticating with the requested user name and password. It handles requests to a particular URL (/j_spring_security_check) with a particular set of user-name and password parameters (j_username, j_password).
40 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
•
OPENID_FILTER. This filter processes OpenId authentication requests, handling both the initial request with the OpenId identity to the external server and the redirect from the OpenId server back to the application. All this interaction is managed when the filter detects requests to the preconfigured URL /j_spring_openid_security_check.
•
LOGIN_PAGE_FILTER. This filter generates a default login page when the user doesn’t provide a custom one. It will be activated when the URL /spring_security_login is requested.
•
DIGEST_AUTH_FILTER. This filter processes HTTP Digest authentication headers. It will look for the presence of both Digest and Authorization HTTP request headers. It can be used to provide Digest authentication to standard user agents, like browsers, or to application clients like SOAP. On successful authentication, the SecurityContext will be populated with the valid Authentication object.
•
BASIC_AUTH_FILTER. This filter processes the BASIC authentication headers in an HTTP request. It looks for the header Authorization and tries to authenticate with these credentials.
•
REQUEST_CACHE_FILTER. This filter retrieves a request from the request-cache that matches the current request, and it sends the cached one through the rest of the filter chain.
•
SERVLET_API_SUPPORT_FILTER. This filter wraps the request in a request wrapper that implements the Servlet API security methods, like isUserInRole, and delegates it to SecurityContextHolder. This allows for the convenient use of the request object itself to get the security information. For example, you can use request.getAuthentication to retrieve the Authentication object.
•
JAAS_API_SUPPORT_FILTER. This filter tries to obtain and use javax.security.auth.Subject, which is a final class, and continue the filter chain execution with this subject.
•
REMEMBER_ME_FILTER. If no user is logged in, this filter will look to see whether there is any “remember me” functionality active and any “remember me” Authentication available. If there is, this filter will try to login automatically and authenticate with this “remember me” information.
•
ANONYMOUS_FILTER. This filter checks to see whether there is already an Authentication in the context. If there is not, it creates a new Anonymous one and sets it on the security context.
•
SESSION_MANAGEMENT_FILTER. This filter passes the Authentication object that corresponds to the authenticated user who is logged in to the system to some configured session management processors in order to do session-related handling of the Authentication. Mainly, these processors will do some kind of validation and throw SessionAuthenticationException if appropriate. Currently, these processors (or strategies) include only one main class in the form of org.springframework.security.web. authentication.session.ConcurrentSessionControlStrategy, dealing with both session fixation and concurrent sessions.
•
EXCEPTION_TRANSLATION_FILTER. This filter handles the translation between Spring Security exceptions (like AccessDeniedException) and the corresponding HTTP status code. It also redirects to the application entry point in case the exception is thrown because there is not yet an authenticated user in the system.
•
FILTER_SECURITY_INTERCEPTOR. This filter handles the authorization mechanism for defined URLs. It delegates to its parent class’ (AbstractSecurityInterceptor) functionality (which I’ll cover later in the chapter) the actual workflow logic of granting or not granting the access to the specific resource.
41 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
•
SWITCH_USER_FILTER. This filter allows a user to impersonate another one by visiting a particular URL (/j_spring_security_switch_user, by default). This URL should be secured to allow just certain users access to this functionality. Also, the method attemptSwitchUser in the implementing class SwitchUserFilter can be overridden to add constraints, so that you can use more finely grained information to decide if certain users are allowed or not allowed to impersonate other users.
ConfigAttribute The interface org.springframework.security.access.ConfigAttribute encapsulates the access information metadata present in a secured resource. For example, for our study purposes, ROLE_ADMIN is a ConfigAttribute. There are a few implementations of ConfigAttribute, as you can see in Figure 3-9.
Figure 3-9. ConfiAttribute hierarchy When you annotate a method with @Secured("ROLE_ADMIN") or something similar, or specify a URL with , Spring Security does the following. On startup, as normal Spring functionality, all the bean postprocessors in the ApplicationContext get invoked. And in the case of Spring Security, the process is the following. What happens in the case of web requests is not really that complex. Web requests don’t really use the postprocessor infrastructure. When you use the element , Spring Security uses the class FilterInvocationSecurityMetadataSourceParser to parse this XML. In the parsing process, the private method parseInterceptUrlsForFilterInvocationRequestMap will be invoked. This method maps the information contained in each of the URL patterns in the XML element into a map of Ant-style request paths, like /*, ROLE_USER. Here /* is an Ant pattern and ROLE_USER is a config attribute (this says basically this config attribute is needed to access any URL with this pattern). This map, ultimately, will be set up in an instance of an implementation of the interface org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource inside the FilterSecurityInterceptor, which uses it when each request comes to match the requested URL against the keys in the map to find out if the URL is secured and then extracts the ConfigAttributes against which to check the authorities of the requesting Authentication object. This setup process is shown in Figure 3-10.
42 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
Figure 3-10. ConfigAttribute setup for web applications For method-level security, you have many options—the most common one being the configurations performed through the use of annotations. There are a few different annotations available in the framework; however, the setup treatment by the framework is very similar. In the case of the @Secured annotation, for instance, you need to make Spring aware that this special annotation needs a special security treatment. To do that, you register the following in the security application context XML file: When you set up that definition in the application context XML configuration, Spring Security creates a new MethodSecurityMetadataSourceAdvisor and registers it in the application context. This advisor will be marked as an infrastructure advisor and will be picked up by Spring Core’s InfrastructureAdvisorAutoProxyCreator, which is a BeanPostProcessor that Spring initializes automatically. This processes all the beans in the application context and determines if any of the configured advisors can be applied to any of the beans and their methods. If so, it wraps the bean with the required advisor or advisors. The postprocessor finds the MethodSecurityMetadataSourceAdvisor and eventually calls an implementation of the MethodSecurityMetadataSource.getAttributes method for each bean and all their methods, to determine if they have any ConfigAttribute configured as metadata in them. If the MethodSecurityMetadataSource finds ConfigAttributes in the bean, the InfrastructureAdvisorAutoProxyCreator, from Spring Core, calls its own method (createProxy) to apply MethodSecurityMetadataSourceAdvisor, which internally contains the Security Interceptor as the org.aopalliance.aop.Advice to apply to the bean. Figure 3-11 shows this interaction graphically.
43 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
Figure 3-11. Decorating annotated beans with security Advice Again, this is a lot of work that Spring and Spring Security do on your behalf. At first sight, this looks like simple magic, but it takes a lot of hard work from Spring to do it. And you have to thank the Spring and Spring Security developers for taking care of all this and giving you a simple and powerful API for resolving your security concerns.
The Authentication Object The Authentication object is an abstraction that represents the entity that logs in to the system—most likely, a user. Because it is normally a user authenticating, I’ll assume and use the term “user” in the rest of the book. There are a few implementations of the Authentication object in the framework, as you can see in Figure 3-12.
Figure 3-12. Authentication hierarchy
44 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
An Authentication object is used both when an authentication request is created (when a user logs in), to carry around the different layers and classes of the framework the requesting data, and then when it is validated, containing the authenticated entity and storing it in SecurityContext. The most common behavior is that when you log in to the application a new Authentication object will be created storing your user name, password, and permissions—most of which are technically known as Principal, Credentials, and Authorities, respectively. Authentication is an interface, and it is pretty simple, as Listing 3-3 shows.
■■Note There are many implementations of the Authentication interface, and in the book I will be referring most of the time to the general Authentication interface when we are not interested in the particular implementation type. Of course when I need to talk about the specifics of an implementation detail I will be referring to the concrete classes. Listing 3-3. The Authentication interface package org.springframework.security.core; import java.io.Serializable; import java.security.Principal; import java.util.Collection; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.context.SecurityContextHolder; public interface Authentication extends Principal, Serializable { Collection extends GrantedAuthority> getAuthorities(); Object getCredentials(); Object getDetails(); Object getPrincipal(); boolean isAuthenticated(); void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException; } As Figure 3-12 shows, currently there are a few implementations of Authentication in the framework: •
UsernamePasswordAuthenticationToken. This is a simple implementation that contains, as its name clearly specifies, the user name and password information of the authenticated (or pending authentication) user. It is the most common Authentication implementation used throughout the system, as many of the AuthenticationProvider objects depend directly on this class.
•
PreAuthenticatedAuthenticationToken. This implementation exists for handling pre-authenticated Authentication objects. Pre-authenticated authentications are those where the actual authentication process is handled by an external system, and Spring Security deals only with extracting the principal (or user) information out of the external system’s messages.
45 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
•
OpenIDAuthenticationToken. This is an Authentication implementation used specifically for OpenID authentication schemes. It is used by both the OpenID filter and the OpenID authentication provider.
•
RunAsUserToken. This implementation is used by the RunAsManager, which is called by the Security Interceptor, when the accessed resource contains a ConfigAttribute that starts with the prefix 'RUN_AS_'. If there is a ConfigAttribute with this value, RunAsManager adds new GrantedAuthorities to the authenticated user corresponding to the RUN_AS value.
SecurityContext and SecurityContextHolder The interface org.springframework.security.core.context.SecurityContext (actually, its implementation is SecurityContextImpl) is the place where Spring Security stores the valid Authentication object, associating it with the current thread. The org.springframework.security.core.context.SecurityContextHolder is the class used to access SecurityContext from many parts of the framework. It is built mainly of static methods to store and access SecurityContext, delegating to configurable strategies the way to handle this SecurityContext—for example, one SecurityContext per thread (default), one global SecurityContext, or a custom strategy. The class diagram for these classes can be seen in Figure 3-13, and Listings 3-4 and 3-5 show the two classes.
AuthenticationProvider AuthenticationProvider is the main entry point for authenticating an Authentication object. This interface has only two methods, as Listing 3-6 shows. This is one of the major extension points of the framework, as you can tell by the many classes that currently extend this interface. Each of the implementing classes deals with a particular external provider to authenticate against. So if you come across a particular provider that is not supported and need to authenticate against it, you probably need to implement this interface with the required functionality. You will see examples of this later in the book. Here are some of the existing providers that come with the framework: CasAuthenticationProvider JaasAuthenticationProvider DaoAuthenticationProvider OpenIDAuthenticationProvider RememberMeAuthenticationProvider LdapAuthenticationProvider
48 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
AccessDecisionManager AccessDecisionManager is the class in charge of deciding if a particular Authentication object is allowed or not allowed to access a particular resource. In its main implementations, it delegates to AccessDecisionVoter objects, which basically compares the GrantedAuthorities in the Authentication object against the ConfigAttribute(s) required by the resource that is being accessed, deciding whether or not access should be granted. They emit their vote to allow access or not. The AccessDecisionManager implementations take the output from the voters into consideration and apply a determined strategy on whether or not to grant access. Voters, however, also can abstain from voting. The AccessDecisionManager interface can be seen in Listing 3-7. Its UML class diagram is shown in Figure 3-15.
49 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
Figure 3-15. AccessDecisionManager hierarchy Listing 3-7. AccessDecisionManager package org.springframework.security.access; import java.util.Collection; import org.springframework.security.authentication .InsufficientAuthenticationException; import org.springframework.security.core.Authentication; public interface AccessDecisionManager { void decide(Authentication authentication, Object object, Collection configAttributes) throws AccessDeniedException, InsufficientAuthenticationException; boolean supports(ConfigAttribute attribute); boolean supports(Class> clazz); } The current AccessDecisionManager implementations all delegate to voters, but they work in slightly different ways. The current voters, which are described in the following list, are defined in the package org.springframework.security.access.vote.
AffirmativeBased This access decision manager calls all its configured voters, and if any of them votes that access should be granted, it is enough for the access decision manager to allow access to the secured resource. If no voters vote to grant access and there is at least one voting not to grant it, the access decision manager throws an AccessDeniedException denying access. If there are only abstaining voters, a decision is made based on the AccessDecisionManager’s instance variable allowIfAllAbstainDecisions, which is a Boolean that defaults to false, determining if access should be granted or not when all voters abstain.
ConsensusBased This access decision manager implementation calls all its configured voters to make a decision to either grant or deny access to a resource. The difference with the AffirmativeBased decision manager is that the ConsensusBased decision manager decides to grant access only if there are more voters granting access than voters denying it. So the majority wins in this case. If there are the same number of granting voters as denying voters, the value of the instance variable
50 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
allowIfEqualGrantedDeniedDecisions is used to decide. By default, this variable’s value is “true”, access is granted. When all voters abstain, the access decision will be decided the same way as it is for the AffirmativeBased manager.
UnanimousBased As you probably guessed, this access decision manager will grant access to the resource only if all the configured voters vote in favor of allowing access to the resource. If any voter votes to deny the access, the AccessDeniedException will be thrown. The “all abstain” case is handled the same way as with the other implementations of AccessDecisionManager.
AccessDecisionVoter This discussion of the AccessDecisionManager and its current implementations should have made clear the importance of the Access Decision Voters, because they are the ones, working as a team, that ultimately determine if a particular Authentication object has enough privileges to access a particular resource. The org.springframework.security.access.AccessDecisionVoter interface is very simple as well, and you can see it in Listing 3-8. The main method is “vote”, and as can be deduced from the interface, it will return one of three possible responses (ACCESS_GRANTED, ACCES_ABSTAIN, ACCESS_DENIED), depending on whether the required conditions are satisfied. The satisfaction or not of the conditions is given by analyzing the Authentication object’s rights against the required resource. In practice this basically means that the Authentication’s authorities are compared against the resource’s security attributes looking for matches. Following are the current AccessDecisionVoter implementations: •
org.springframework.security.access.annotation.Jsr250Voter. This voter votes on resources that are secured with JSR 250 annotations—namely, DenyAll, PermitAll, and RolesAllowed. Their names should be very descriptive. DenyAll won’t allow any access at all to the resource, independent of the security information carried by the Authentication object trying to access it. PermitAll will allow access to everyone, regardless of what roles they have. The RolesAllowed annotation can be configured with a series of roles. If an Authentication object tries to access the resource, it must have one of the roles configured in the RolesAllowed annotation in order to get access granted by this voter.
•
org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter. This voter votes on resources with expression configurations based on @PreFilter and @PreAuthorize annotations. @PreFilter and @PreAuthorize annotations support a value attribute that can have a SpEL expression. The PreInvocationAuthorizationAdviceVoter is the one in charge of evaluating the SpEL expressions (of course with the help of Spring’s SpEL evaluation mechanism) provided in these annotations. We will be explaining and using SpEL expressions in several parts of the book so this concept will become clearer as the books advances.
•
org.springframework.security.access.vote.AbstractAclVoter. This is the abstract class that has the skeleton to write voters dealing with domain ACL rules so that other implementing class built on its functionality to add voting behavior. Currently, it is implemented in AclEntryVoter, which votes on users’ permissions on domain objects. This voter will be covered in the chapter dedicated to ACL.
•
org.springframework.security.access.vote.AuthenticatedVoter. This voter votes whenever a ConfigAttribute referencing any of the three possible levels of authentication is present on the secured resource. The three levels are IS_AUTHENTICATED_FULLY, IS_AUTHENTICATED_REMEMBERED, IS_AUTHENTICATED_ANONYMOUSLY. The voter emits a positive vote if the Authentication object’s authentication level matches (or is a stronger level in the hierarchy IS_AUTHENTICATED_FULLY ➤ IS_AUTHENTICATED_REMEMBERED ➤ IS_AUTHENTICATED_ ANONYMOUSLY) the authentication level configured in the resource.
51 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
•
org.springframework.security.access.vote.RoleVoter. This is, perhaps, the most commonly used voter of them all. This voter, by default, is able to vote on resources that have ConfigAttribute(s) containing security metadata starting with the prefix “ROLE_” (which can be overridden). When an Authentication object tries to access the resource, its GrantedAuthorities will be matched against the relevant ConfigAttributes. If there is a match, access is granted. If there isn’t, access is denied.
•
org.springframework.security.access.expression.WebExpressionVoter. This is the voter in charge of evaluating SpEL expressions in the context of web requests in the filter chain—expressions like 'hasRole' in the element. To make use of this voter, and in general to support SpEL expressions in web security, the use-expressions="true" attribute needs to be added to the element.
The voters model is yet another one in the framework that is open for extension and customization. You could easily create your own implementation and add it to the framework. You will see how to do this in Chapter 8. Listing. 3-8. AccessDecisionVoter interface package org.springframework.security.access; import java.util.Collection; import org.springframework.security.core.Authentication; public interface AccessDecisionVoter { int ACCESS_GRANTED = 1; int ACCESS_ABSTAIN = 0; int ACCESS_DENIED = −1; boolean supports(ConfigAttribute attribute); boolean supports(Class> clazz); int vote(Authentication authentication, S object, Collection attributes); }
UserDetailsService and AuthenticationUserDetailsService The interface org.springframework.security.core.userdetails.UserDetailsService is in charge of loading the user information from the underlying user store (in-memory, database, and so on) when an authentication request arrives in the application. UserDetailsService makes use of the provided user name for looking up the rest of the required user data from the datastore. It defines just one method, as you see in Listing 3-9. You can see its hierarchy in Figure 3-16. Listing 3-9. UserDetailsServicepackage org.springframework.security.core.userdetails public interface UserDetailsService { UserDetails loadUserByUsername(String username) throws }
UsernameNotFoundException;
52 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
Figure 3-16. UserDetailsService hierarchy The interface org.springframework.security.core.userdetails.AuthenticationUserDetailsService is more generic—it allows you to retrieve a UserDetails using an Authentication object instead of a user name String, making it more flexible to implement. Actually, there is an implementation of AuthenticationUserDetailsService (UserDetailsByNameServiceWrapper) that simply delegates to a UserDetailsService extracting the user name from the Authentication object. Listing 3-10 shows the AuthenticationUserDetailsService interface. These are the two main strategies (AuthenticationUserDetailsService and UserDetailsService) used for retrieving the user information when attempting authentication. They are usually called from the particular AuthenticationProvider that is being used in the application. For example, the OpenIDAuthenticationProvider and CasAuthenticationProvider delegate to an AuthenticationUserDetailsService to obtain the user details, while the DaoAuthenticationProvider delegates directly to a UserDetailsService. Some other providers don’t use a user details service of any kind (for example, JaasAuthenticationProvider uses its own mechanism to retrieve the Principal from a javax.security.auth.login.LoginContext), and some others use a completely custom one (for example, LdapAuthenticationProvider uses a UserDetailsContextMapper). Listing 3-10. AuthenticationUserDetailsService package org.springframework.security.core.userdetails; public interface AuthenticationUserDetailsService { UserDetails loadUserDetails(T token) throws UsernameNotFoundException; }
UserDetails The interface org.springframework.security.core.userdetails.UserDetails object is the main abstraction in the system, and it’s used to represent a full user in the context of Spring Security. It is also made available to be accessed later in the system from any point that has access to SecurityContext. Normally, developers create their own implementation of this interface to store particular user details they need or want (like email, telephone, address, and so on). Later, they can access this information, which will be encapsulated in the Authentication object, and they can be obtained by calling the getPrincipal method on it. Some of the current UserDetailsService (for example, InMemoryUserDetailsManager) implementations use the class org.springframework.security.core.userdetails.User, which is available in the core of the framework, as the UserDetails implementation returned by the method loadUserByUsername. However, this is another of those configurable points of the framework, and you could easily create your own UserDetails implementation and use that in your application. Listing 3-11 shows the UserDetails interface.
53 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
ACL The ACL is the module in charge of securing your application at the individual domain object level with a fine level of granularity. This means, in a general way, assigning an ID to each domain object in your application and creating a relationship between these objects and the different users of the application. These relationships determine whether or not a determined user is allowed access to a particular domain. The ACL model offers a fine-grained, access-level configuration you can use to define different rules for accessing the objects depending on who is trying to access it. (For example, a user might be allowed read access while another user will have write/read access over the same domain object.) The current support for ACLs is configured to get the configuration rules from a relational database. The DDL (Data Definition Language) for configuring the database comes along with the framework itself, and it can be found in the ACL module. ACL security will be fully covered in Chapter 7.
JSP Taglib If you are working to secure a Java web application, the taglib component of the framework is the one you use to hide or show certain elements in your pages according to your users’ permissions. The tags are simple to use and, at the same time, very convenient for making a more usable web site. They help you adapt the UI of your application on a per-user (or more commonly, per-role) basis. The taglib will be covered in depth in Chapter 4.
54 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
Good Design and Patterns in Spring Security I said it before, but I will repeat it here. One of the great aspects of working with open source software is that you can (and I would say you should) look at the source code and understand the software at a new, deeper level. Also, you can look at the way the software is built, at what is good, and at what is bad (at least by your own subjective standards) and just learn how other developers work. This can have a great impact on the way you work, because you might discover a way of doing things that you couldn’t have learned on your own. Sometimes, of course, you will find things you don’t like, but that is good as well. You can learn from other people’s mistakes as much as you can learn from their successes. For me, Spring in general and Spring Security in particular have achieved something that I found invaluable in the Java development space—that is, they can make us better developers even without us noticing it. For instance, I often ask myself, “How many people would be using a template pattern for accessing databases if they weren’t using Spring, instead of a more awkward DB access layer?” or “How many people would be just programming against implementation classes all the time, creating unnecessary coupling if it wasn’t for Spring’s DI support?” or “How many people would have cross-cutting concerns, like transactions, all over their code base if it wasn’t for how easily Spring brings AOP into the development process?.” I think helping good practices almost without noticing is really a great achievement for Spring. It won’t create great developers by itself for sure, but it helps the average developer to not make mistakes that he might make if he didn’t have the support of the framework, and its principles to adhere to. As you might see from the description of the main components of the framework, Spring Security itself is built with good design principles and patterns in mind. You’ll have a brief look here at some of the things I find interesting in the framework, and from which you can learn about. This section won’t really help you to do more with Spring Security, but it will serve as a way to appreciate the good work that has been done in constructing this fantastic framework.
Strategy Pattern A big part of the pluggability and modularity of the framework is achieved thanks to the wide use of the Strategy pattern. You can find it, for example, in the type of SecurityContext to be used, the AuthenticationProvider hierarchy, the AccessDecisionVoters, and many other elements. Covering design patterns is outside the scope of this book but as a reminder of the strategy pattern’s power I leave you with this definition from the Wikipedia. “The strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it”. That definition shows a great deal of the power that comes when working with interfaces. You could have different implementations of the same interface and pass any of them to a client class for doing different kinds of work. The client classes don’t need to know or care about the implementation details that it is working with. Knowing the interface or contract is enough to leverage its job.
Decorator Pattern Built into Spring’s core AOP framework, you can find the Decorator pattern—mostly in the way that your annotated business classes and methods get security constraints applied to them. Basically, your objects have only certain meta information related to the security constraints that should be applied to them, and then by some “Spring magic” they get wrapped with security handling. Listing 3-12 shows the invoke method of MethodSecurityInterceptor. In the listing, you can see how the objects are decorated with prefunctionality and postfunctionality that surrounds the invocation of the actual method.
55 www.it-ebooks.info
Chapter 3 ■ Spring Security Architecture and Design
SRP Spring Security’s code seems to take very seriously the Single Responsibility Principle. There are many examples of it around the framework, because any object you choose seems to have one and only one responsibility. For example, the AuthenticationProvider deals only with the general concern of authenticating a principal with its credentials in the system. The SecurityInterceptor is simply in charge of intercepting the requests, and it delegates all security-checking logic to collaborating objects. A lot more examples like this can be extracted from the framework.
DI Again, this is built into the Spring Framework itself, and of course as everything in the Spring architecture, this means that it is also inherited by the rest of the Spring projects, including Spring Security. Dependency injection (DI) is one of Spring’s most important features. Almost every component in Spring Security is configured through the use of dependency injection. The AccessDecisionManager is injected into the AbstractSecurityInterceptor, and AccessDecisionVoter implementations are injected into the AccessDecisionManager. And like this, most of the framework is built by composing components together through dependency injection.
Summary This was a complex chapter, but going through the inner workings of a software tool is definitely the best way to understand it and take advantage out of it. And that is what we did. You looked at an in-depth explanation of Spring Security’s architecture, its major components, and the way it works from the inside. You should now understand how the XML namespace works, how AOP fits into the framework, and how, in general, the Servlet Filter functionality is used to enforce web-level security. I demystified the “Spring magic” by going through all the components that help you add security to your applications in a seemingly simple way. You looked at some code snippets from the framework itself to get a greater appreciation of the work done in it, as well as to understand better why things work the way they do. You also studied the modularity inherent in the framework and saw how it helps to create software that is both flexible and extensible. Even with all we covered in this chapter, it is basically an introduction and a reference to have in hand when you read the upcoming chapters and you start looking at the options to secure your applications. From now on, you will understand where everything fits in the framework and how the different components link to each other. In the next chapter, you will start developing an example application. At the beginning, it will be a simple web application, and you will see how to secure it. You will use all your current knowledge of the framework to tweak the configuration and test different ways of implementing security at the web level.
56 www.it-ebooks.info
Chapter 4
Web Security In this chapter, I will explain how to apply security at the web layer for Java web-based applications. You will see in detail the inner work of the security filter chain and the different metadata options at your disposal to define security constraints in your application. I will also cover the Taglib facility for enforcing security constraints at the view level.
Introducing the Simple Example Application In this chapter, you will be working on a dummy test application: an “action movies” web application. The application itself will be very simple. (It won’t have any really useful functionality). However, I will try to cover most of the options available for securing web applications with Spring Security, even if some of the options don’t seem realistic for an application of this kind. First, we will set up the application. As in Chapter 2, we will use Maven to start up and build the project. Again, I will assume certain versions of the different tools we need. Mainly, we will use Java 7 and Maven 3. Go to the command line, to a directory you like, and do the following: mvn archetype:generate -DgroupId=com.apress.pss -DartifactId=terrormovies -DarchetypeArtifactId=maven-archetype-webapp Press enter on each prompt you get from the command line. You should see output like Figure 4-1.
57 www.it-ebooks.info
Chapter 4 ■ Web Security
Figure 4-1. Creating a new Maven web application Now we have an empty web project. At the moment, we will use the same dependencies that we had in our program from Chapter 2. So make sure your pom.xml file has the content from Listing 4-1. Listing 4-1. First pom.xml for terrormovies 4.0.0com.apress.pssterrormovieswar0.0.1-SNAPSHOTterrormovies Maven Webapphttp://maven.apache.orgjavax.servletjavax.servlet-api3.0.1
Just to test that it works, go to the command line, and execute the following in the root of the application: mvn jetty:run
■■Note Jetty (http://jetty.codehaus.org/jetty/ and http://www.eclipse.org/jetty/) is both an HTTP server and a Java Servlet container in the same way as Apache Tomcat. It is a powerful, flexible, open source server that can be used either standalone or embedded in applications. I will use Jetty in all the examples of this book because I find it very convenient to work with in Maven environments. With that said, most, if not all, of the examples should work as they are in any other Servlet container, such as Tomcat. The application should start without a problem, and you should get output like that shown in Figure 4-2. If you open your browser and go to http://localhost:8080/, you will see the message “Hello World!” on the page, as you can see in Figure 4-3.
Figure 4-2. Starting the Jetty application
60 www.it-ebooks.info
Chapter 4 ■ Web Security
Figure 4-3. First iteration of the application displays the simple Hello World! page Let’s start to write some code. In this chapter, you will be working with Spring MVC instead of simple Servlets. Spring MVC is a simple but powerful Model-View-Controller framework that is part of the Spring Framework. It integrates fully with its programming model. MVC frameworks offer the developer a good separation of concerns, and many web frameworks in different programming languages enforce this programming model. Covering Spring MVC in depth is outside the scope of this book, but the examples used in the book should be easy enough to follow. If you want to learn about Spring MVC and Spring in general, you should pick up a book like Pro Spring 3 by Clarence Ho and Rob Harrop (Apress, 2012) or Spring Recipes: A Problem-Solution Approach, Second Edition by Gary Mak, Daniel Rubio, and Josh Long (Apress 2010), which cover a lot of ground in the Spring Framework ecosystem. The first thing you will do is set up Spring MVC in your application. Follow these simple steps: 1.
Add the content of Listing 4-2 to your web.xml file. This code snippet defines a Servlet that will be used to handle all incoming requests to your application. You can see that its implementation is a Spring-specific class. This class is the entry point into the Spring MVC world. The value given to the servlet is relevant because DispatcherServlet will look for a file in the path WEB-INF/terrormovies-servlet.xml, where the name terrormovies-servlet is picked up from the servlet-name value. Listing 4-2. web.xml snippets for setting up Spring MVC
2.
terrormovies org.springframework.web.servlet.DispatcherServlet 1terrormovies/ You create a WEB-INF/terrormovies-servlet.xml file. This file will be the one Spring uses to configure the MVC support in the framework. You will define a minimal file here because that is all you need at the moment. You will create this file with the contents of Listing 4-3. Listing 4-3. terrormovies-servlet.xml file defining a Spring MVC configuration
61 www.it-ebooks.info
Chapter 4 ■ Web Security
3.
xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> Next you will define your first controller. For this first controller, you will focus on the “admin” part of the application. At the beginning, you will handle administration tasks with a particular URL namespace (/admin/*). That way, you can handle security using URL patterns. The controller can be found in Listing 4-4. Listing 4-4. The Admin controller
package com.apress.pss.terrormovies.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequestMapping("/admin") public class AdminController { @RequestMapping(method = RequestMethod.POST, value = "/movies") @ResponseBody public String createMovie(@RequestBody String movie) { System.out.println("Adding movie!! "+movie); return "created"; } } Note that, for the purposes of web security, it doesn’t really matter if you use a Spring MVC controller, like we do here, or if you use simple Servlets as we did in Chapter 2, or for that matter, if you use any other Servlet-based framework for developing your application. Remember that, at the core, the web part of Spring Security basically attaches itself to the standard Java Servlet Filter architecture. So if your application uses Servlets and Filters, you can leverage Spring Security’s web support. You can see that in this controller you specified the URL, the HTTP method, and the body of the request you will receive. The URL is built with a combination between the class-level RequestMapping annotation’s value concatenated with the method-level RequestMapping annotation’s value. So your URL will be /admin/movies. The HTTP method is specified as POST in the RequestMapping annotation on the method. The RequestBody annotation in the method parameter simply tells Spring to populate the movie parameter with the string that comes in the body of the request. And the ResponseBody annotation at the method level tells Spring to use the return value of the method as the body content of the response.
62 www.it-ebooks.info
4
Chapter 4 ■ Web Security
4.
Now you will test your new functionality. First, make sure you restart the application by pressing Ctrl+C and mvn jetty:run again as you did before. You will be using curl for this first simple example. (Later, you will be using the user interface of the application itself.) Curl is a command-line tool you use to send HTTP requests to a server. It can be configured to use any HTTP method, add any arbitrary headers, use certificates, and so on. For testing your new functionality, you start your application (using mvn jetty:run as I explained in Chapter 2). Then, from the command line in another terminal, execute the following:
curl -X POST -d "Die Hard" http://localhost:8080/admin/movies You can see this in Figure 4-4.
Figure 4-4. Executing curl to make a request If you look back in the first terminal where the application is running, you should see the text “Adding movie!! Die Hard” printed in the console as Figure 4-5 clearly shows. That is good. You are reaching the endpoint with your POST request to create movies. But in a real application context, maybe only an admin user should be allowed to create new movies on the system. Ensuring that is your next step.
Figure 4-5. A movie is created 5.
You have defined your first user and your first role. So you need an admin user. But what is an admin user? In this case, an admin user will simply be a user with a ROLE_ADMIN role. You need to decide where you are going to store your users and your roles. Because this is a greenfield fake application, you can decide to do whatever you want. For starters, you will do the easiest thing and store your users and roles in memory with the application. You will then modify this to a more realistic database-backed solution in later chapters. In later chapters, you will also fully explore some of the other providers you can use and even implement your own.
For defining your admin user and roles in memory, you will use the same user-service as in Chapter 2. This is a simple couple of XML elements you need to define in your configuration file applicationContext-security.xml, which you will create in the WEB-INF directory with the contents of Listing 4-5. The lines worth mentioning at this point are the ones that contain the element and its child element . Here, you can see you are defining your new admin user.
63 www.it-ebooks.info
Chapter 4 ■ Web Security
Listing 4-5. In-memory user detail configuration 6.
The next step is to secure your URL, allowing access only to your newly defined admin user. You will make sure that only users with the ROLE_ADMIN role are able to reach your AdminController. To do this, you need to use the element and the element, as shown in Listing 4-6. You add those lines in the same file (applicationContext-security.xml) just after the opening element. Listing 4-6. Securing URLs with role-based access
■■Note There is a catch in defining the security constraints of Spring Security. You cannot define them in the terrormovies-servlet.xml file, because they need to be loaded up with the Servlet listener and the filter chain. So they need to be in a proper WebApplicationContext defined with a Servlet listener, not the Servlet-related one. This is because the DelegatingFilterProxy will look for the root application context defined in the ServletContext that is loaded by the ContextLoaderListener. If you define only a terrormovies-servlet.xml, because the filters load first, before the servlets, the filter won’t be able to find any application context. So it won’t be able to load up correctly. In short, you need to define your Spring Security configuration in one of the root application contexts.
64 www.it-ebooks.info
Chapter 4 ■ Web Security
7.
Next you need to create the Servlet listener that will be aware of the configuration file applicationContext-security.xml. You also need to activate the Spring Security filter chain. To do that, you add the contents of Listing 4-7 to the web.xml file. Listing 4-7. Servlet listener for loading the Spring Security configuration file
8.
contextConfigLocation /WEB-INF/applicationContext-security.xml springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy springSecurityFilterChain/* org.springframework.web.context.ContextLoaderListener Next you try to access the URL again. This time, let’s try the browser. Restart the application, and paste the following URL in your browser’s address bar: http://localhost:8080/admin/movies.
This time, your browser should get redirected to the URL http://localhost:8080/spring_security_login and should show you the familiar login form from Chapter 1, which you can see in Figure 4-6.
Figure 4-6. Login form
65 www.it-ebooks.info
Chapter 4 ■ Web Security
You will now look in more detail at what is going on in the application, and how the configuration you just defined is helping you secure the application. You will follow the request throughout its trip through the framework and look at the different steps that it takes. When you defined the element in your application context file, you got some configuration for free, including the most commonly required security filters. The request will go through all these filters in a predefined order. When you make the HTTP request to the configured URL, and after your Servlet container (in this case, Jetty) deals with it, the request lands in the DelegatingFilterProxy, which in turn delegates the processing to the security FilterChainProxy. Figure 4-7 shows each of the filters that your current request travels through in the framework. SecurityContextPersistenceFilter LogoutFilter UsernamePasswordAuthenticationFilter DefaultLoginPageGeneratingFilter BasicAuthenticationFilter RequestCacheAwareFilter SecurityContextHolderAwareRequestFilter AnonymousAuthenticationFilter SessionManagementFilter ExceptionTranslationFilter FilterSecurityInterceptor
Figure 4-7. The filter chain, with the filters invoked in the first request. They are invoked from the top down The first security filter that gets hit by the request is SecurityContextPersistenceFilter. When the request hits this filter, the framework tries to retrieve a SecurityContext from the standard Servlet session (javax.servlet.http.HttpSession). If there is not a context in the session, or the session itself is null (that is, it still doesn’t exist because it hasn’t been created yet), the framework creates a new empty SecurityContext (or, more accurately, an instance of its implementation class SecurityContextImpl). This is what happens in your current request. A new SecurityContext is created and associated to the current thread of execution. The request is then sent to the next filter in the chain. Figure 4-8 shows this interaction.
66 www.it-ebooks.info
Chapter 4 ■ Web SeCurity
loadContext
getAttribute('SPRING_SECURITY_CONTEXT')
null
newContext setContext
Figure 4-8. Sequence diagram of the SecurityContextPersistenceFilter before forwarding the request to the next filter in the chain The next filter in the chain is the LogoutFilter. Because the request is not for the URL /j_spring_security_ logout, the filter simply forwards the request to the next filter in the chain. This interaction is really simple and is shown in Figure 4-9.
getRequestURI doFilter
Figure 4-9. LogoutFilter no-op interaction when the logout URL is not requested The request reaches the UsernamePasswordAuthenticationFilter. Because this request is not for the URL /j_spring_security_check, the filter simply forwards the request to the next filter in the chain. As with the previous filter, this interaction is simple and can be explained with the same diagram. The next filter to run is the DefaultLoginPageGeneratingFilter. This filter will see that the request is not for the URL /spring_security_login and will forward the request to the next filter in the chain. As in the previous two filters, this interaction is simple and can be explained with the same diagram. The next filter invoked is the BasicAuthenticationFilter. This filter looks for HTTP Basic Authentication headers in the request (the header “Authorization” with a value starting with “Basic”). Because none are found, the filter forwards the request to the next filter in the chain. As in the previous three filters, this interaction is simple and can be explained with the same simple diagram. The only difference is that instead of looking at the path of the request, this filter looks at the headers (request.getHeader("Authorization")). The request arrives at the RequestCacheAwareFilter filter, which won’t find any cached requests matching the current request. So it forwards the original request to the next filter. Cached requests are used normally when you do temporal redirections (like when you redirect to the login page) and then want to redirect back to the previously requested URL. Figure 4-10 shows this interaction.
67 www.it-ebooks.info
Chapter 4 ■ Web Security
getMatchingRequest getAttribute(SAVED_REQUEST)
null or saved request
null or saved request
doFilter(Original request or saved
Figure 4-10. RequestCacheAwareFilter tries to find a previously cached request or uses the original one The next filter invoked is SecurityContextHolderAwareRequestFilter. This filter wraps the request in a SecurityContextHolderAwareRequestWrapper, which implements the Servlet API security methods and forwards this request to the next filter in the chain. The next filter in the chain is the AnonymousAuthenticationFilter. When your request hits this filter, the framework will see that there is no Authentication object currently set in the SecurityContext, and it creates an AnonymousAuthenticationToken. This object plays the role of an anonymous user in the system. As a matter of fact, it will have a username of anonymousUser and will be the granted authority ROLE_ANONYMOUS in the system. The request is then forwarded to the next filter in the chain. Figure 4-11 shows this interaction.
Figure 4-11. AnonymousAuthenticationFilter at work; there is no authentication already set in the SecurityContext The request arrives at the SessionManagementFilter. At this moment, there is still no active Servlet session in the system for the particular agent that is accessing it. This filter checks that the current request is with an anonymous Authentication object (which was set by the previous filter in the context) and, finding that it is, it simply forwards the request to the next filter in the chain. The next filter in the chain is ExceptionTranslationFilter. When the request arrives here, the only processing is to wrap the invocation to the next filter in the chain in a try..catch block. If any subsequent filter, or the request handler itself, throws an exception it will be caught by the catch block in this filter. This actually happens for the current request, as you will see when I describe the next filter. You see the interaction of this filter in Figure 4-12. You can see in the figure that when an exception is caught, the filter can invoke an instance of an implementation of AuthenticationEntryPoint (as AuthenticationEntryPoint is an interface). An AuthenticationEntryPoint has the logic to start a new authentication process, and this normally means showing a login form to the logged-in user.
Figure 4-12. ExceptionTranslationFilter wraps the invocation of the next filter in the chain with a try-catch block so that it can handle any security exception You finally arrive at the last filter in the default-configured chain shown in Figure 4-7. It is FilterSecurityInterceptor. I talked a lot about this filter in previous chapters, but I will refresh the information here in the context of our request. When the request arrives in this filter, the filter creates a new FilterInvocation object that contains the request. This object is passed by the filter to the core AbstractSecurityInterceptor (from which FilterSecurityInterceptor extends). The interceptor (with some helpers’ help) extracts the requested path from the request and checks whether it matches any of the patterns defined in the pattern attribute. If it matches, it retrieves the required authorities from the “access” attribute contained in the element. In this case study, it finds the value ROLE_ADMIN. The interceptor also extracts the Authentication object from the Security Context and sends these two elements (the Authentication object and the required authorities or config attributes) to the AffirmativeBased access-decision manager, which in turn sends them to the configured RoleVoter instance. The RoleVoter extracts the authorities associated with the Authentication object (ROLE_ANONYMOUS, in this case) and tries to match it to the received ConfigAttribute(s) (ROLE_ADMIN). The voter will not find any matches, so it will vote to deny access to the resource. When the AffirmativeBased AccessDecisionManager receives the votes, it will see the deny vote and throw an AccessDeniedException. This exception bubbles all the way up to the catch block in the ExceptionTranslationFilter. The catch block in this filter will see that the current Authentication object stored in the SecurityContext is an Anonymous authentication and will start a new authentication process to obtain a complete Authentication from a user. This authentication process is handled by the class LoginUrlAuthenticationEntryPoint. This class builds a URL (/spring_security_login by default) and sends a redirect response to that URL using the standard Servlet redirect method response.sendRedirect(redirectUrl). Figure 4-13 shows the work of the FilterSecurityInterceptor I just explained.
Figure 4-13. FilterSecurityInterceptor interaction decision process. Access is denied in this case
69 www.it-ebooks.info
Chapter 4 ■ Web Security
When the browser is redirecting and asks for the URL /spring_security_login, the following occurs: •
The process will be the same as for the first request until it reaches the DefaultLoginPageGeneratingFilter. At this point, the filter detects that the request is for the URL /spring_security_login and writes the login form’s HTML data directly in the response object. Then the response will be rendered.
Now try to log in with incorrect credentials. We’ll follow the request through the framework to see what happens: 1.
In the login form, type the username user and the password uspass.
2.
When the form is submitted, the filters are activated again in the same order as before. This time, however, when the request arrives at the UsernamePasswordAuthenticationFilter, the filter checks whether the request is for the URL /j_spring_security_check and sees that this is indeed the case. The filter extracts the username and password authentication information from the HTTP request parameters j_username and j_password, respectively. With this information, it creates the UsernamePasswordAuthenticationToken Authentication object, which then sends it to the AuthenticationManager (or more exactly, its default implementation ProviderManager) for authentication.
3.
The DaoAuthenticationProvider gets called from the ProviderManager with the Authentication object. The DaoAuthentication provider is an implementation of AuthenticationProvider, which uses a strategy of UserDetailsService to retrieve the users from whichever storage they live in. With the configuration you currently have, it will try to find a user with the username “user” using the configured InMemoryUserDetailsManager (the implementation of UserDetailsService that maintains an in-memory user storage in a java.util.Map). Because there is no user with this username, the provider throws a UsernameNotFoundException exception.
4.
The provider itself catches this exception and converts it into a BadCredentialsException to hide the fact that there is no such user in the application; instead; it treats the error as a common username-password combination error.
5.
The exception will be caught by the UsernamePasswordAuthenticationFilter. This filter delegates to an instance of an implementation of AuthenticationFailureHandler, which in turn decides to redirect the response to the URL /spring_security_login?login_error. This way, the login form is shown again in the browser with an error message displayed. You can see this interaction in Figure 4-14.
creates authenticate authenticate loadUserByUsername user throwsAuthenticationException onAuthenticationFailure
Figure 4-14. Authentication filter when authentication details are incorrect
70 www.it-ebooks.info
Chapter 4 ■ Web Security
Let’s now log in with correct credentials: 1.
First, let’s create a new endpoint in the controller to retrieve some simple text. In the controller AdminController, create the method from Listing 4-8. Notice in the listing how you are using the GET HTTP method to handle requests. Then restart the application. Listing 4-8. A simple endpoint method that returns a simple string
2.
3.
@RequestMapping(method = RequestMethod.GET, value = "/movies") @ResponseBody public String createMovie() { return "movie x"; } Go back to the URL http:/localhost:8080/admin/movies, and type admin as the username and admin as the password in the form. Then click the Login button. a.
The request follows the same filter journey as before. This time, InMemoryUserDetailsManager finds a user with the requested username and returns that to DaoAuthenticationProvider, which creates a successful Authentication object.
b.
After successful authentication, the UsernamePasswordAuthenticationFilter delegates to an instance of SavedRequestAwareAuthenticationSuccessHandler, which looks for the original requested URL (/admin/movies) in the session and redirects the response to that URL.
When http://localhost:8080/admin/movies is requested, the request works its way through the filter chain as in the previous cases. This time, though, you already have a fully authenticated entity in the system. The request arrives in the FilterSecurityInterceptor. a.
The FilterSecurityInterceptor receives an access request to the URL /admin/ movies. Then it recovers the necessary credentials to access that URL (ROLE_ ADMIN).
b.
The AffirmativeBased access-decision manager gets called, and in turn calls the RoleVoter voter. The voter evaluates the list of authorities of the authenticated entity and compares them with the required credentials to access the resource. Because the voter finds a match (ROLE_ADMIN is in both the Authentication authorities and the resource’s config attributes), it votes with an ACCESS_GRANTED vote.
c.
The FilterSecurityInterceptor forwards the request to the next element in the request-handling chain, which in this case is Spring’s DispatcherServlet.
d. The request gets to the AdminController, which simply returns the string movie x, which then gets rendered to the browser. Figure 4-15 shows this result.
71 www.it-ebooks.info
Chapter 4 ■ Web Security
Figure 4-15. movie x returned when accessing with correct credentials e.
This is the complete flow of the Authentication and Authorization process. Figure 4-16 shows this full interaction in a pseudo flow chart.
UsernamePasswordAuthenticationFilter receives the Request
Username and Password arrive to the AuthenticationProvider
SavedRequestAwareAuthenticationSuccessHandler find the original requested URL and redirects
UserDetails finds the user
Original Request arrives in FilterSecurityInterceptor
FilterSecurityInterceptor retrieves SecurityMetadata and calls AffimativeBased
FilterSecurityInterceptor forwards Request to Servlet handler
RoleVote gets called and votes to grant access
Figure 4-16. Overall flow of a successful authentication and authorization process
The Special URLs From the preceding explanation, you can see that Spring Security’s support for web security defines a few preconfigured URLs for you to use in your application. These URLs get special treatment in the framework. The main ones are the following: •
/j_spring_security_check This URL is used by the framework to determine that the incoming request is sending authentication information and asking to check these credentials and authenticate correspondingly.
72 www.it-ebooks.info
Chapter 4 ■ Web Security
•
/j_spring_security_logout This URL is used by the framework to log out the currently logged-in user, invalidating the corresponding session and SecurityContext.
•
/spring_security_login This is the URL that Spring Security uses to show the login form for the application. The framework will redirect to this URL when an authentication is needed but doesn’t exist yet.
From the previous URLs, the first thing that comes to mind is how to configure your own Login form in the application and, in general, how to customize the login process instead of using the default one. That is what we’ll do next.
Custom Login Form When you configure the element as you did before, Spring Security takes care of setting up a default login process for you, including a login URL, login form, default URL after login, and some other options. Basically, when Spring Security’s context starts to load up, it will find that there is no custom login page URL configured, so it will assume the default one and create a new instance of DefaultLoginPageGeneratingFilter that will be added to the filter chain. As you saw before, this filter is the one that generates the login form for you. If you want to configure your own form, you need to do the following. The first thing is to tell the framework to replace the default handling with your own. You define the following XML element as a child of the element in the file applicationContext-security.xml: This element tells Spring Security to change its default login-handling mechanism on startup. First, the DefaultLoginPageGeneratingFilter will no longer be instantiated. Let’s try this first configuration out. With the new configuration in place, restart the application and try to access the URL http://localhost:8080/admin/movies. You get redirected to the URL /custom_login and get a 404 HTTP error because you haven’t defined any handler for this URL yet. This 404 page is shown in Figure 4-17.
Figure 4-17. Error 404 that appears when defining a new login handler page
73 www.it-ebooks.info
Chapter 4 ■ Web Security
Let’s create a LoginController controller next to the AdminController in the project. It should look something like Listing 4-9. Listing 4-9. LoginController that handles the /custom_login URL specified in the configuration file package com.apress.pss.terrormovies.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping("") public class LoginController { @RequestMapping(method = RequestMethod.GET, value = "/custom_login") public String showLogin(){ return "login"; } } The last line in the showLogin method returns a “logical view name,” in Spring MVC parlance. This will be the name of the JSP file that has your login form. Now you create the fantastically designed login.jsp from Listing 4-10 in the folder WEB-INF/views in your application. You also add the XML snippet from Listing 4-11 into the terrormovies-servlet.xml file. Listing 4-10. custom login.jsp <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Terror movies Listing 4-11. View resolver definition
74 www.it-ebooks.info
Chapter 4 ■ Web Security
If you restart the application and again go to the http://localhost:8080/admin/movies URL, you should see your new login form when you get redirected to the /custom_login URL. The form is shown in Figure 4-18. If you type admin as both the username and password, you get access to the movie x page as you did before with the default login form.
Figure 4-18. Custom login form If you take a look at the login.jsp, you can see certain names for the username field, password field, and action attribute of the form element. These are not random names. Spring Security expects the use of these particular names in order to treat the authentication process correctly. Also, the form should use POST for sending the information to the server because this is required by the framework. The element supports many more configuration options, including changing the default j_username and j_password names for the authentication request parameters. To try it out, replace the current element with the following one: . Next, change the corresponding text fields in the login.jsp page to be named user_param and pass_param instead of j_username and j_password, respectively. Restart the application, go to the URL /admin/movies, and log in with admin/admin. You should be able to access the application without any problem. The other attributes you can configure in the element are these: •
default-target-url This attribute determines the URL that the logged-in user will be redirected to after successfully logging in, if he hasn’t requested an explicit URL that triggered the authentication process. By default, this URL is the root of the application.
•
authentication-failure-url This attribute specifies the URL that will be used to redirect to in case of a failed login attempt. In the default Spring Security configuration, this URL is /spring_security_login?login_error. This is the same as the normal login URL with a parameter of “login_error” appended to it. You can do something similar with your custom login page.
Let’s give this attribute the value /custom_login?error. Then, in your login.jsp, let’s add the content from Listing 4-12 just after the tag. Listing 4-12. Snippet showing an error in the login.jsp <% if(request.getParameter("error") != null){ out.println("ERROR LOGIN"); } %> If you now restart the application and try to access the URL http://localhost:8080/admin/movies and use an incorrect username and password, you will get the Login page again, but with the error message shown at the top. Look at Figure 4-19 for the page you should be getting.
75 www.it-ebooks.info
Chapter 4 ■ Web Security
Figure 4-19. A custom error shown in the custom form Note that this URL could be a different URL altogether, not related to the login URL at all. But the common pattern is to allow the user another attempt at login, showing her any errors. •
authentication-success-handler-ref Reference to an AuthenticationSuccessHandler bean in the Spring application context. This bean will be called on successful authentication and should handle the next step after authentication, usually deciding the redirect destination in the application. A current implementation in the form of SavedRequestAwareAuthenticationSuccessHandler takes care of redirecting the logged-in user to the original requested URL after successful authentication.
•
authentication-failure-handler-ref Reference to an AuthenticationFailureHandler bean in the Spring application context. It is used to handle failed authentication requests. When an authentication fails, this handler gets called. A standard behavior for this handler is to present the login screen again or return a 401 HTTP status error. This behavior is provided by the concrete class SimpleUrlAuthenticationFailureHandler.
Let’s develop a simple example implementation of the AuthenticationFailureHandler interface. It will simply return a 500 status code when failing to authenticate. Create the class ServerErrorFailureHandler from Listing 4-13 in the package com.apress.pss. terrormovies.security. Listing 4-13. AuthenticationFailureHandler implementation. ServerErrorFailureHandler package com.apress.pss.terrormovies.security; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.AuthenticationFailureHandler; public class ServerErrorFailureHandler implements AuthenticationFailureHandler{
76 www.it-ebooks.info
Chapter 4 ■ Web SeCurity
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { response.sendError(500); } } Then, in the applicationContext-security.xml file, replace the element with the following: . And define the following Spring somewhere in that same file: . Restart the application, go to http://localhost:8080/admin/movies URL, use a random username and password, and click the submit button. You should get a 500 error in the browser as Figure 4-20 shows.
Figure 4-20. Error 500 produced by custom AuthenticationFailureHandler
Basic HTTP Authentication Sometimes, you can’t really use a login form for authenticating users. For instance, if your application is meant to be called by other systems instead of a human user, it doesn’t make sense to show a login form to the other application. This is a pretty common use case. Web services talk to each other without user interaction, ESB systems integrate systems with one another, and JMS clients produce and consume messages from other systems. In the context of HTTP-exposed interfaces that require no human user to access them, a common approach is to use HTTP basic authentication headers. HTTP authentication headers allow you to embed the security information (username and password) in the header of the request that you send to the server, instead of sending it in the body of the request, as is the case for the login form authentication. HTTP uses a standard header for carrying this information. The header is appropriately named “Authorization.” When using this header, the client that is sending the request (for example, a browser) concatenates the username and the password with a colon between them and then Base64 encodes the resulting string, sending the result of this
77 www.it-ebooks.info
Chapter 4 ■ Web Security
in the header. For example, if you use the username bart and the password simpson, the client creates the string bart:simpson and encodes it prior to sending it in the header. Let’s use Basic HTTP authentication in our application. The first and only thing you need to do is replace the element in your configuration file applicationContext-security.xml with the following one: . After replacing it, you restart the application and go to the URL http://localhost:8080/ admin/movies in the browser. A standard HTTP authentication box pops up asking you for your authentication details, as Figure 4-21 shows. Type admin as the username and password, and send the request. You successfully arrive in the movie x page that you already saw a couple of times before.
Figure 4-21. Standard HTTP authentication form. Basic Authentication configuration When you use the configuration element, Spring Security's BasicAuthenticationFilter comes into action. A BasicAuthenticationEntryPoint strategy will be configured into the ExceptionTranslationFilter on startup. When you make the first request to the URL /admin/movies, the framework behaves as before, throwing an access-denied exception that is handled by the ExceptionTranslationFilter. This filter delegates to a particular implementation strategy of AuthenticationEntryPoint—in this case, BasicAuthenticationEntryPoint. BasicAuthenticationEntryPoint adds the header “WWW-Authenticate: Basic realm=“Spring Security Application”” to the response and then sends an HTTP status of 401 (Unauthorized) to the client. The client should know how to handle this code and work accordingly. (In the case of a browser, it simply shows the authentication pop up.) When you introduce the username and password and submit the request, the request again follows the filter chain until it reaches the BasicAuthenticationFilter. This filter checks the request headers, looking for the “Authorization” header starting with “Basic.” The filter extracts the content of the header and uses Base64.decode to decode the string, and then it extracts the username and password. The filter creates a UsernamePasswordAuthenticationToken object and sends it to the authentication manager for authentication in the standard way. The authentication manager will ask the authentication provider to retrieve the user and then create an Authentication object with it. This process is standard and independent of using Basic Authentication or form authentication.
Digest Authentication Digest Authentication is a very close sibling of Basic HTTP Authentication. Its main purpose is to avoid sending clear text passwords on the wire, as Basic Authentication does, by hashing the password prior to sending it to the server. This makes Digest Authentication more complex than Basic Authentication. Digest Authentication works with HTTP headers the same way that Basic Authentication does. Digest Authentication is based in the use of a nonce for hashing the passwords. A nonce is an arbitrary server-generated number that is used in the authentication process and that is used only once. It is passed through the digest computation together with the username, password, nonce, URI being requested, and so on. In the authentication process, both the server and client do the digest computation and they should match.
78 www.it-ebooks.info
Chapter 4 ■ Web Security
The main processing lies in two classes: DigestAuthenticationFilter and DigestAuthenticationEntryPoint. DigestAuthenticationFilter queries the request’s headers looking for the Authorization header, and then it checks that the header’s value starts with “Digest.” If this is the case, the request is carrying the security credentials that will be used for authentication. DigestAuthenticationEntryPoint is the class that is invoked to generate a response that demands that a digest security authentication process begin. This class sets the header “WWW-Authenticate” with the correct values (including the nonce) so that the client agent (the browser) knows it has to start the digest authentication process. To configure it, let’s add the filter to the filter chain. In this case, there is no custom XML element to define it, so you need to create a element and then create s for both the filter and the entry point. The new element should look like this: . Also, it needs to be added as a child of the element. In the element, you add the attribute entry-point-ref="digestEntryPoint", which will allow the ExceptionTranslationFilter to use this entry point when an AccessDeniedException is encountered. Listing 4-14 shows the two new beans you need to define in the file applicationContext-security.xml. You also need to give the ID “userService” to the configured in the same file. Listing 4-14. DigestAuthenticationFilter and DigestAuthenticationEntryPoint bean definitions If you restart the application and try to go to the URL http://localhost:8080/admin/movies, you will be presented with a browser dialog box asking for a username and password exactly like the one that was shown for Basic Authentication. This was the DigestAuthenticationEntryPoint’s work. As I explained before, the entry point will fill the response object with the required headers so that the browser knows it needs to show the login form. Log in with a username and password of admin, and you should be able to access the requested URL. The browser will create its own digested message with the password input included and put it in the header. It will also put the rest of the information—namely, nonce, cnonce, realm, and so on—in the “Digest” header. An example “Digest” header that is sent to the server with your current request is the following: 'Digest username="admin", realm="terrormovies-realm", nonce="MTM1NTY3NDc3NDIy. . ..==", uri="/admin/movies", response="225ea6fbad618cfdf1da7d4f7efe53b8", qop=auth, nc=00000002, cnonce="376a9b27621880bd"' When the request reaches DigestAuthenticationFilter, the headers of the request contain the required digest authentication header. The information in this header arrives as a csv string containing all the required information as I showed you in the last paragraph, including the nonce and the client nonce (cnonce). (A nonce is an arbitrary number used only once in a cryptographic communication. See http://en.wikipedia.org/wiki/Cryptographic_nonce.). The filter extracts the information from the header, retrieves the user from the UserDetailsService, and then computes the digest with the password from the retrieved user to see if the digest matches the one sent in the header by the client. If they match, access is granted.
79 www.it-ebooks.info
Chapter 4 ■ Web Security
Remember-Me Authentication The remember-me authentication functionality is used for allowing returning users of the application to use it without needing to log in every time. Basically, the application will remember certain visitors, allowing them to just open the application and be greeted with their personalized version of the application, as if they were logged in. Remember-me functionality is very convenient for users; however, it is also very dangerous and recommended for private (from home) use only. The problem should be obvious. If you use an application from a public computer and this application remembers your profile information, the next person who accesses that application from that computer will be able to impersonate you with minimum effort. It is also common practice to offer just a limited amount of functionality in the remember-me session. This means that even if you are logged-in automatically thanks to the remember-me functionality, you won’t have access to the whole functionality of the application. More sensitive parts of the application might require you to formally log in to use them. This is the case, for example, with Amazon.com. When you visit Amazon.com and log in, the next time you visit Amazon, the site will remember you, your recommendations, your name, and other information about you. But if you want to buy something, it will ask you to log in fully to access that functionality. Remember-me authentication is typically supported by sending a cookie to the browser, which then, on subsequent sessions in the application, will be sent back to the server for auto login. How does remember-me functionality work in Spring Security? Remember-me functionality in Spring Security is supported mainly by two components: the RememberMeServices interface and the RememberMeAuthenticationFilter class. Let’s see how they work in the context of a request. In the application, replace the element with the from the Basic and Digest examples. Also, remember-me is not enabled by default. To enable it, include the following element inside the element in the configuration file . Now restart the application. When the application starts up, the RememberMeAuthenticationFilter will be in the filter chain of the server. Also, a TokenBasedRememberMeServices will be instantiated and injected into the AbstractAuthenticationProcessingFilter replacing the no-op NullRememberMeServices. Go and visit the URL http://localhost:8080/admin/movies, and log in with admin as the username and password. When the request gets into the application, UsernamePasswordAuthenticationFilter (a subclass of AbstractAuthenticationProcessingFilter) will handle the authentication process in the standard way already explained. After the authentication is successful, UsernamePasswordAuthenticationFilter invokes the configured TokenBasedRememberMeServices's loginSuccess method. This method looks to see if the request contains the parameter _spring_security_remember_me in order to apply the remember-me functionality. (If the property alwaysRemember is set to true in the service, it will also apply the remember-me functionality.) Because you didn’t send this request, nothing will happen. So let’s add the parameter to the login form you have. Open the file login.jsp and somewhere inside the You can see that this file is using a namespace. This is part of the Spring Data project’s MongoDB support. Here you are simply defining where your database is listening for connections (localhost:27017), specifying what the name of your database (example1) is, and creating the MongoTemplate instance you will inject into your UserDetails service. More importantly, you can see that you are defining two converter instances inside a element and then using this converter in the mongoConverter property of the MongoTemplate. You can use these two converters to map the documents from the Mongo database to and from the User objects you are using. These converters are defined in Listings 8-11 and 8-12. Listing 8-11. UserReadConverter Allows You to Convert MongoDB Objects into Your Domain User Object package com.apress.pss.security; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.springframework.core.convert.converter.Converter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import com.mongodb.BasicDBList; import com.mongodb.DBObject;
251 www.it-ebooks.info
Chapter 8 ■ Customizing and Extending Spring Security
public class UserReadConverter implements Converter { public User convert(DBObject source) { return new User((String) source.get("username"), (String) source.get("password"), convertAuthoritiesReading(source)); } private Collection extends GrantedAuthority> convertAuthoritiesReading( DBObject source) { List> stringAuthorities = (BasicDBList) source.get("authorities"); List authorities = new ArrayList(); for (Object stringAuth : stringAuthorities) { authorities.add(new SimpleGrantedAuthority((String)stringAuth)); } return authorities; } } Listing 8-12. UserWriteConverter Allows You to Convert Your Domain User Object into MongoDB Objects package com.apress.pss.security; import org.springframework.core.convert.converter.Converter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; import com.mongodb.BasicDBObject; import com.mongodb.DBObject; public class UserWriteConverter implements Converter { public DBObject convert(User source) { DBObject dbObject = new BasicDBObject(); dbObject.put("username", source.getUsername()); dbObject.put("password", source.getPassword()); dbObject.put("authorities", authoritiesAsStringArray(source)); return dbObject; } private Object authoritiesAsStringArray(User source) { String[] authorities = new String[source.getAuthorities().size()]; int i = 0; for(GrantedAuthority auth: source.getAuthorities()){ authorities[i]=auth.getAuthority(); i++; } return authorities; } }
252 www.it-ebooks.info
Chapter 8 ■ Customizing and Extending Spring Security
The converters in both Listing 8-11 and Listing 8-12 implement the same Converter interface, but they invert the order of conversion of the types in the type parameters of the class. They basically do inverse operations. UserReadConverter makes sure you can read the MongoDB document into a User object. The relevant data is extracted from the document and is transformed when needed (as with the GrantedAuthority) to set it as properties of the object, and of course it calls the correct constructor on the User class. Listing 8-12 is the opposite. It takes a User object and extracts the values from its properties to store them on the MongoDB collection creating a Mongo DBObject, which ultimately will represent a Mongo document. These converters are automatically invoked with the definition you wrote in Listing 8-10. Now you have to change the applicationContext-security.xml file in a couple of ways. The first thing that needs to be done is to import the new applicationContext-mongodb.xml file. The second thing is to define the new UserDetailsService bean and make it part of the security flow by injecting it in the AuthenticationProvider. In the end, your applicationContext-security.xml file should look something like Listing 8-13. Listing 8-13. applicationContext-security.xml with the New MongoUserDetailsService Configured Although everything is configured right now, you still don’t have any users created in your users collection in your example1 Mongo database. There is no 100 percent standard way of doing this from Spring Security’s point of view; however, since version 2.0 it offers the interface UserDetailsManager, which extends UserDetailsService with a set of CRUD methods that allow you to create users, update them, delete them, and change their passwords. This interface has JDBC, LDAP, and InMemory implementations, but, of course, there isn’t a MongoDB one. What you will do is modify your MongoUserDetailsService to implement this interface instead of the simple UserDetailsService. Then you will create a simple program to store a user using this service. The new MongoUserDetailsService is shown in Listing 8-14, and the simple main program to insert a User is shown in Listing 8-15. Listing 8-14. MongoUserDetailsService, Which Allows You to Store a UserDetails Instance in the Mongo Database package com.apress.pss.security; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.security.core.userdetails.User;
253 www.it-ebooks.info
Chapter 8 ■ Customizing and Extending Spring Security
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.provisioning.UserDetailsManager; public class MongoUserDetailsService implements UserDetailsManager { private MongoTemplate mongoTemplate; public MongoUserDetailsService(MongoTemplate mongoTemplate){ this.mongoTemplate = mongoTemplate; } public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { UserDetails user = mongoTemplate.findOne(new Query(Criteria.where("username").is(username)), User.class, "users"); if (user == null){ throw new UsernameNotFoundException("Username "+username+ " not found."); } return user; } public void createUser(UserDetails user) { mongoTemplate.insert(user, "users"); } public void updateUser(UserDetails user) { throw new UnsupportedOperationException(); } public void deleteUser(String username) { throw new UnsupportedOperationException(); } public void changePassword(String oldPassword, String newPassword) { throw new UnsupportedOperationException(); } public boolean userExists(String username) { throw new UnsupportedOperationException(); } } You can see that Listing 8-14 added the method createUser to the class, which simply uses the MongoTemplate to store the user in one line. The listing also has a lot of new methods, which you are not going to implement but which are required by the UserDetailsManager interface.
254 www.it-ebooks.info
Chapter 8 ■ Customizing and Extending Spring Security
Listing 8-15. Small Main Class to Insert One User in the MongoDB, Leveraging the MongoUserDetailsService package com.apress.pss; import java.util.Arrays; import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.provisioning.UserDetailsManager; public class UserInserter { public static void main(String[] args){ ApplicationContext context = new FileSystemXmlApplicationContext("//....../WEB-INF/applicationContext-security.xml"); UserDetailsManager userDetailsManager = context.getBean(UserDetailsManager.class); GrantedAuthority[] authorities = new GrantedAuthority[] {new SimpleGrantedAuthority("ROLE_SCARVAREZ_MEMBER")}; UserDetails user = new User("car", "scarvarez", Arrays.asList(authorities)); userDetailsManager.createUser(user); } } The code in Listing 8-15 is very straightforward. You are creating a new user with username “car”, password “scarvarez”, and the role ROLE_SCARVAREZ_MEMBER. Then you are using your new UserDetailsManager (the MongoUserDetailsService) implementation to store the user in the Mongo database. This is so that you have a user for your tests. Run this main class as you would normally run any Java class with a main method to store the user. After running this class, you should have the user created in the collection. You can check this out by completing the following steps: 1.
Go to the directory where MongoDB was installed.
2.
Go inside the bin folder.
3.
Execute the ./mongo command.
4.
Execute the commands shown in Figure 8-5.
Figure 8-5. MongoDB looking at the newly created user
255 www.it-ebooks.info
Chapter 8 ■ Customizing and Extending Spring Security
The application is ready to go; start it with mvn jetty:run and give it a go. If you visit the URL http://localhost:8080/hello, you will be prompted for the username and password. By introducing “car” and “scarvarez”, respectively, and navigating to the URL http://localhost:8080/hello, you should be able to access the “Hello World” message page, which is shown in Figure 8-6.
Figure 8-6. The “Hello World” page So you have created a new UserDetailsService implementation backed up by MongoDB, and you are using it in the AuthenticationProvider to retrieve the logged-in user.
Password Encryption One thing you might have noticed in this example and all the previous examples is that you are storing and retrieving passwords in plain text. Spring Security offers an abstraction to encrypt passwords in the form of the interface org.springframework.security.authentication.encoding.PasswordEncoder in the core framework. This interface is currently implemented by a series of classes that offer everything from plain-text encoders (such as the default one implemented in class PlaintextPasswordEncoder, which doesn’t do any encryption in the password) to hashing passwords that use recognized encryption algorithms (like Md5PasswordEncoder and ShaPasswordEncoder). To use one of these implementations with your current AuthenticationProvider, you need to define a element in the configuration file as a child of the element. For example, if you want to use Hash-256 as the digest algorithm for your passwords, you make the element look like Listing 8-16. The element currently allows you to define any of seven options in the “hash” attribute: md4, md5, sha, sha-256, {sha}, {ssha}, and plaintext. The {sha} and {ssha} options are used for Lightweight Directory Access Protocol (LDAP) in the LdapShaPasswordEncoder. Another way of configuring the encoder is to use the ref attribute instead of the hash and specify a reference to a bean that holds a PasswordEncoder instance. This, of course, can be an encoder implementation you create yourself. I will do this to be able to reference the same encoder in the writing part of the UserInserter. In doing so, I will be able to store the encoded value of the password, ensuring that I’m using exactly the same algorithm implementation as the one that will be used when logging in to the application. So the passwords should match if they are the same. To do this, I simply define a bean somewhere in the file, as Listing 8-17 shows, and then use the ID of that bean in the ref attribute of the element. Of course, as I mentioned in the previous paragraph, you now need to configure the insertion of the user to use the encoding algorithm as well so that Spring Security is able to match the password when retrieving the user from the database. To do this, you will use the password encoder defined and retrieved from your UserInserter class before storing the password. For that, you make the main method from the UserInserter class look like Listing 8-18. (You need to add the import org.springframework.security.authentication.encoding.PasswordEncoder; to the class from this listing).
256 www.it-ebooks.info
Chapter 8 ■ Customizing and Extending Spring Security
Listing 8-16. with a sha-256 Password Encoder Configured Listing 8-17. sha-256 Individual Bean That Is Then Referenced in the Element Listing 8-18. User Inserter Main Method Now Hashes the Password Before Inserting It public static void main(String[] args){ ApplicationContext context = new FileSystemXmlApplicationContext("//......./WEB-INF/applicationContext-security.xml"); UserDetailsManager userDetailsManager = context.getBean(UserDetailsManager.class); GrantedAuthority[] authorities = new GrantedAuthority[] {new SimpleGrantedAuthority("ROLE_SCARVAREZ_MEMBER")}; PasswordEncoder encoder = context.getBean(PasswordEncoder.class); UserDetails user = new User("car", encoder.encodePassword("scarvarez",null), Arrays.asList(authorities)); userDetailsManager.createUser(user); } If you execute this code now, you will insert the user with the password hashed with the SHA-256 algorithm. You can check that by querying your MongoDB database, as explained in Figure 8-5 and its preceding paragraph. After doing this, you can restart your application and use “car” and “scarvarez” to log in as you did before. You should be able to log in without any problems, with no apparent difference from a user interface point of view. However, now, under the hood your passwords are not stored in plain text anywhere in the application, so they cannot be compromised easily. As I said at the beginning of the section, sometimes you might want to replace the whole AuthenticationProvider implementation instead of simply the UserDetailsService. You could also create another AuthenticationProvider implementation to combine with the existing ones because, if you remember, the AuthenticationManager default implementation (ProviderManager) is able to iterate through a list of AuthenticationProvider instances, and also, it is able to relate to other AuthenticationManager instances in a parent–child relationship. There are many different implementations of AuthenticationProvider, as you saw in Chapter 7, including support for LDAP, JAAS and others. You could implement your own AuthenticationProvider if you need to. You simply need to define an element as a child of the element and use its ref attribute to reference your implementation bean. Most of the AuthenticationProvider implementations decide if they can handle a particular authentication request by consulting the type of the Authentication object that is passed to the authenticate method, using the supports method to check. I explained this process in detail in Chapter 7, so I won’t go into it any further here.
New Voters in AccessDecisionManager As I explained before, AccessDecisionManager’s default implementations (AffirmativeBased, UnanimousBased, and ConsensusBased) work by querying a set of configured AccessDecisionVoters to allow or deny access to a particular resource. It is fairly straightforward to implement your own AccessDecisionVoter, and that is what I will show you here. I will start again from the simple code I explained in the first example of this chapter (refer to that part of the
257 www.it-ebooks.info
Chapter 8 ■ Customizing and Extending Spring Security
chapter for a step-by-step guide to setting up your project) and work from there. Again, do any modifications required to the pom.xml file if you would like to name the application differently. Let’s go directly to business here. You will create an AccessDecisionVoter that will vote on attributes of the form USERNAME_XX, where it will grant access to any user whose username is XX. (You could implement this solution using a SpEL expression, but we want to show the use of custom voters, so this example will do.) The first thing you will do is create the voter implementation itself. It should be something like Listing 8-19. Listing 8-19. UsernameVoter Is a Custom AccessDecisionVoter package com.apress.pss.security; import java.util.Collection; import org.springframework.security.access.AccessDecisionVoter; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.core.Authentication; public class UsernameVoter implements AccessDecisionVoter Next, I simplified the jsp files inside the jsp folder like this: the file helloWorld.jsp just contains the string “Pagina Segura” inside, nothing else. Struts 2 has a built-in system to handle the exceptions that might be thrown in your application. This mechanism would get in the way of Spring Security’s exception-handling system, which depends on AccessDeniedException, among other exceptions, to be thrown to alter the flow of execution. For example, when showing a login form, you need to deactivate the Struts 2 exception-handling mechanism. To do that in the file struts.xml, you add the line just below the other line that contains a defined element. Finally, we need to secure our action. Make your HelloWorldAction look like Listing 9-5. Listing 9-5. HelloWorldAction secured package com.apress.pss.struts; import java.util.Date; import org.springframework.security.access.annotation.Secured; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.validator.annotations.Validation; import com.opensymphony.xwork2.validator.annotations.RequiredStringValidator; import com.opensymphony.xwork2.validator.annotations.RequiredFieldValidator; import com.opensymphony.xwork2.conversion.annotations.Conversion; import com.opensymphony.xwork2.conversion.annotations.TypeConversion;
279 www.it-ebooks.info
Chapter 9 ■ IntegratIng SprIng SeCurIty wIth Other FramewOrkS and LanguageS
public class HelloWorldAction extends ActionSupport { @Secured("ROLE_USER") public String execute() throws Exception { return SUCCESS; } } Now if you restart your application and visit http://localhost:8080/struts-example/helloWorld, you’ll be presented with the standard Spring Security login form. If you use the login username car and password scarvarez, you’ll be able to access the page shown in Figure 9-3.
Figure 9-3. The secured Struts 2 application and the accessed action
Spring Security with Spring Web Flow Spring Web Flow is a framework, built on top of Spring MVC, that allows you to link different steps of a web-driven process into a fluent workflow. In other words, it allows you to define in a declarative way the different steps that a web application can go through while you are interacting with it. Basically, you use it to define a set of rules and transitions between the user interface (UI) parts of a web application and the back-end process that each transition should trigger. Graphically, Spring Web Flow works, in a simplified form, as shown in Figure 9-4. The example is a fake web page for a simplified product. The boxes represent various states (the View state, Action state, Decision state, Subflow state, and others), and the arrows represent transitions. Web Flow Product X Home
see reviews
Product X Reviews
buy product
buy product
Buy products sub flow
Figure 9-4. Simple Spring Web Flow scheme showing that, from a product page, you can go to the review page or buy the product
280 www.it-ebooks.info
Chapter 9 ■ Integrating Spring Security with Other Frameworks and Languages
To implement this simple flow with Spring Web Flow, we’ll create a new project. As is the case for most of the examples, we’ll use Maven to build and manage our project. So let’s do it. From the command line in a place where you want to create your project, execute the following: mvn archetype:create -DarchetypeGroupId=org.apache.maven.archetypes\ -DarchetypeArtifactId=maven-archetype-webapp -DarchetypeVersion=1.0\ -DgroupId=com.apress.pss -DartifactId=webflow-example -Dversion=1.0-SNAPSHOT That will create a new project named webflow-example in the directory. Now we’ll create all the configuration files. Replace the pom.xml file in the new project with the one shown in Listing 9-6, and replace the web.xml file with the one shown in Listing 9-7. Then, in the WEB-INF directory, create the files shown in Listings 9-8 and 9-9 with the names products-servlet.xml and applicationContext-security.xml, respectively. Listing 9-6. The pom.xml file with Spring Security and Spring Web Flow dependencies 4.0.0com.apress.psswebflow-examplewar1.0-SNAPSHOTwebflow-example Maven Webapphttp://maven.apache.org3.1.1.RELEASEorg.springframeworkspring-core${org.springframework.version}org.springframeworkspring-expression${org.springframework.version}org.springframeworkspring-beans${org.springframework.version}org.springframeworkspring-aop${org.springframework.version}
281 www.it-ebooks.info
Chapter 9 ■ Integrating Spring Security with Other Frameworks and Languages
Chapter 9 ■ Integrating Spring Security with Other Frameworks and Languages
org.springframework.webflowspring-webflow2.3.1.RELEASEcommons-loggingcommons-logging1.1.1commons-codeccommons-codec1.3org.mortbay.jettyjetty-maven-plugin8.1.1.v20120215808060000webflow-example Listing 9-7. The web.xml for Spring Web Flow and Spring Security contextConfigLocation /WEB-INF/applicationContext-security.xml
283 www.it-ebooks.info
Chapter 9 ■ Integrating Spring Security with Other Frameworks and Languages
org.springframework.web.context.ContextLoaderListener springSecurityFilterChainorg.springframework.web.filter.DelegatingFilterProxyspringSecurityFilterChain/productsorg.springframework.web.servlet.DispatcherServletproducts/ You should already be familiar with the content of Listing 9-7 It is a web.xml file that includes Spring’s ContextLoaderListener, which loads the Spring application-context file given in context-param “contextConfigLocation”. It also defines Spring’s DispatcherServlet servlet, which takes care of setting up Spring MVC by loading the appropriate configuration file. We are also defining the already familiar springSecurityFilterChain filter. Both the security filter and the dispatcher servlet are configured to handle every URL in the system.
■■Note In Spring MVC, the name of the DispatcherServlet servlet is important because that name will match the name of the Spring configuration file that will be used in the application to configure the application. For example, in our case, by defining the DispatcherServlet with name “products”, Spring will expect to find a file with the name products-servlet.xml in the WEB-INF folder where the beans for the web layer should be defined.
Listing 9-8. The products-servlet.xml file that imports the flow
284 www.it-ebooks.info
Chapter 9 ■ Integrating Spring Security with Other Frameworks and Languages
This file is very simple, and its only job is to import another file (the example-webflow.xml file), which will contain the entire Spring Web Flow configuration. This configuration will remain in a different file just to keep it separated from the main servlet file. Listing 9-9. The applicationContext-security.xml file This is another file you should be able to understand easily by now. Listing 9-9 shows a very basic Spring Security configuration. We are defining just a single user with role ROLE_USER, which will be enough for our tests. We are not defining any URL security rules here because that is not what we want to do in this Spring Web Flow example. We want to add security at the flow level (its states), and that is what I will show you how to do. Now we need to define the web-flow configuration of the application as well as the actual web flows themselves. Again, this will be a simplistic example just to show how the functionality works. The example will be based on Figure 9-4. Listing 9-10 shows the web-flow configuration file, and Listing 9-11 shows our only flow definition. Listing 9-10. The example-webflow.xml in the WEB-INF folder
285 www.it-ebooks.info
Chapter 9 ■ Integrating Spring Security with Other Frameworks and Languages
This file defines the general configuration for Spring Web Flow. The main part of the file is the element flowregistry and its attribute flow-builder-services. This element is where the location of the flows in the application are defined. Currently, we’ll define only one flow which will be in the product.xml file. Also, note the way our views will get resolved when referenced in a view state in a flow. The class org.springframework.webflow.mvc. builder.MvcViewFactoryCreator is the one that will resolve view locations. By default, it will resolve view files by looking in the flow definition directory for files whose names are the names of the view states concatenated with .jsp at the end. This simply means that if a view is named review, it will look for a file named review.jsp. Listing 9-11. The product.xml simple flow in the /WEB-INF/flows/products folder This is the actual flow definition. We are simply defining three view states here. As I said before, each of these view states will map to a physical view file in the application. The view files, by default behavior, should be located in the same directory as this flow file and should be named according to the view states concatenated with .jsp at the end. So we should have, in the WEB-INF/flows/products directory files named main.jsp, review.jsp, and buy.jsp. They are shown in Listings 9-12, 9-13, and 9-14, respectively.
286 www.it-ebooks.info
Chapter 9 ■ Integrating Spring Security with Other Frameworks and Languages
Listing 9-12. The main.jsp for the main view state Ferrari f40
F40
ReviewBuy Listing 9-13. The review.jsp for the review state Ferrari f40