Expression based security with Spring Security

Many web apps have a very simple security concept based on user roles. You might have some admin, some common users and maybe some more roles like a moderator or a super-user. Such a role concept can be easily implemented with Spring Security. For example, you could secure your app based on routes which are available for certain roles:

If you want to be more flexible, you could also annotate your controllers instead of making a central configuration. While a central security configuration can become quite complex if you have a lot of routes with different security rules, using the @PreAuthorize annotation is very simple. All security constraints are right at the method where they apply.

But what if your security model has more constraints than roles?

Spring Security makes it easy to handle roles. But there are a lot of situations where roles are not enough. Imagine an order system (something like Amazon) where an user can cancel an order. To do this, you would introduce an user role which will have the permission to cancel an order. So anonymous users will not be allowed to cancel an order. That’s good, but only half of the story! There is one more (maybe the most important) security constraint: An user can only cancel those orders which belongs to him! And not orders of somebody else!

Such a rule is a typical business rule which can be handled by a service. The service might load all orders which belongs to a customer from the database, iterate over them and check if the given order ID is contained in the list. This could look like this:

If we have such a service which simply returns true or false we can easily use it together with Spring Security:

By using the @PreAuthorize annotation, you can use any Spring Bean. In the example above, we use the OrderService: @orderService.ownsOrder(#orderId). We pass the input parameter directly from the request mapping. The service simply returns true or false and Spring will either allow or forbid the access to the controller method.

It’s also possible to combine those rules:

This makes it possible to build complex security expressions which can use roles as well as Spring beans. By using Spring beans you can implement all types of security checks.

Best regards,
Thomas

More

A mocked Spring security configuration for testing

Spring security is great! It gives you the possibility to secure your app and to create a login with a few simple lines of code. However, it could also become annoying during development, when you have to log yourself in again and again. So why not create a mocked Spring authentication for development and testing?

Create a new profile to active the mock

Before we implement the actual mock, we first create a new profile to active or deactive it. Spring profiles give you the opportunity to decide which bean Spring should use when starting your app. This means you can say “Hey Spring! Use my mock!” or “Hey Spring! Use my production implementation!”. Let’s call our profile “SECURITY_MOCK”. First, we add the profile to our production security configuration, so that we can switch it off and use the mock instead. Note that this is the only change we need to do to our already existing code!

Now we create our mock configuration:

Using a profile like shown above, we can decide which implementation Spring will use. We just need to pass the profile to our application properties:

A mocked authentication provider

Now that we have defined a profile to switch our mock on and off, we need to do the actual implementation. The first thing we do, is to write a mocked authentication provider. Spring will use this authentication provider to check the user, validate its password and to return the user details. The authentication provider depends on our own implementation, but it might look like that:

A couple of things are important right here:

  • Use the SECURITY_MOCK profile again and annotate the class with @Primary so it will replace any other bean when using the mock profile!
  • As I’m pretty sure you want to use a custom user class, set it to the details!

The mocked security config

After you implemented the mocked authentication provider, the actual security config will be very easy. The only thing you need to do is to use your authentication provider:

Usage

After you implemented both, the mocked security config and the authentication provider mock, the usage is straight forward. You just activate the profile (in your application properties) and then you can use the authenticated user in our code:

Best regards,
Thomas

A Windows SSO (for Java on client and server)

A couple of months ago I worked on a single sign-on (SSO) for a Windows client and server made in Java. The scenario was the following:

  • A client made with Java running on Windows
  • A server made with Java running on Windows
  • Both where logged-in to the same domain (an Active Directory LDAP)

The question was, how the server could get the identity (the name of the Windows account) of the client and – of course – how it could trust this information. But if the client would just send a name (e.g. from Java’s System.getProperty("user.name"); method), the client could send anything.

The solution for this dilemma (trust what the client sends to you) is to use a (so called) trusted third party. A trusted third party is an instance which both, client and server, know and trust. The client authenticates itself to this party and the server can verify requests against it. In the scenario above, the domain of the company (an Active Directory LDAP) is the trusted third party. Each client identifies itself against this domain when it logs-in to Windows. Its Windows username and password are checked by the domain/LDAP. On the other side, the server has also access to the domain controller and can verify information send by the client.

The nice thing about this is, that the Windows domain is already configured on nearly every machine which stands in a company. Every company, bigger than maybe five people, will have a Windows domain to log-in. Therefor, a SSO based on the Windows domain will work right out of the box in most cases and we don’t need and configuration in our Java code, since it is already configured in Windows.

Java Native Access (JNA)

To use Windows and the domain controller for authentication, we can use native Windows APIs. To call those APIs in Java, we can use the Java Native Access (JNA) library, which you can find on GitHub at https://github.com/twall/jna and on Maven central:

For example, to get all user groups of the current user, you would do:

Waffle

On top of JNA exists a library called Waffle which encapsulates all functionality you need to implement user authentication. You can find it on GitHub at https://github.com/dblock/waffle and also on Maven central:

You can use Waffle to create a token on the client, send it to the server (e.g. over HTTP or whatever) and to validate that token on the server. At the end of this process (create, send and validate) you will know on the server who the client is – for sure!

Here is an example of how to identify a client on the server. Note that this piece of code is executed completely on one machine. However, you could easily split it into two parts, one on the client and one on the server. The only thing you would need to do, is to exchange the byte[] tokens between client and server. I commented the appropriate lines of code.

(By the way, I asked this myself on Stackoverflow some times ago).

The only thing that is a little bit complicated with that solution is, that you need to do a small handshake between client and server. The client will send a token to the server, which will response with another token, which the client needs to answer again to get the final “you are authenticated” token from the server. To do this, you need to hold some state on the server for the duration of the handshake. Since the handshake is done in a second or two, I just used a limited cache from Google’s Guava library to hold maybe 100 client contexts on the server.

The exchanged tokens are validated against the underlying Windows and its domain.

Best regards,
Thomas