The Application Setup and Introduction

This video is available to view for members only.

Click here to Join!

Already a member?


In this first video we are going to take a look at the demo application we will be using throughout this short series on Securing parts of a Symfony application.

We will be building on the code from the FOSUserBundle video tutorial series. The reasoning for this is that we need to have the concept of Users before we can start restricting access for certain users or user roles.

That said, there is nothing special about the code from the FOSUserBundle series that you need to know - so if you are already comfortable with FOSUserBundle, or Users inside a Symfony application then you should have no problems with this video.

Securing Symfony Applications Through access_control Entries

Likely the first way you will secure your application / website is through the access_control list inside security.yml.

This is the section of your security configuration where you can add in a list of regular expressions, and for every single request that hits your application, the requested route will be checked against the access_control entry, and if matching will trigger the associated logic.

Sounds confusing?

Well, fortunately there's a really helpful Symfony Cookbook article on this very subject.

There's a variety of options you can configure in each access_control entry. Again, the cookbook entry is a great place to read up on this further.

However, what I will say is that for most applications, especially when you are starting out, you can get away with knowing just two regular expressions for the vast majority of your access_control entries. These are:

  • ^ - beginning
  • $ - ending

The characters are a little crazy, but the gist is really simple.

We might add in an entry as follows:

# app/config/security.yml
        - { path: ^/admin/, role: ROLE_ADMIN }

This rule will match any of the following requests that come in to our application:


And so on.

If your requested route matches any of these, the user you are currently logged in as will need to have the required role - ROLE_ADMIN in this case - to be able to access the resource.

We could restrict this down to just /admin/ by changing the entry regular expression to:

# app/config/security.yml
        - { path: ^/admin/$, role: ROLE_ADMIN }

Now the only route that will match is

As in, start at the root: /, and end after the text matches admin/.

With these two characters, you can do most of what you need to do, most of the time :)

Careful though, as forgotting to use the ^ can cause unexpected results:

# app/config/security.yml
        - { path: /admin/, role: ROLE_ADMIN }

Without the ^, this would match:


Note the last entry? It still contains /admin/, but because we never told our rule where to start, it will match anywhere in the string.

This can cause... unexpected situations.

You will very likely use the access_control list (again, not to be confused with the Access Control List (ACL)) frequently.

It is extremely powerful when combined with further restrictions, such as the @Security annotation, or Security Voters.

@Security Annotation

Whilst the access_control list can restrict large sections of your application from unauthorised access, it is not so great at fine grained control.

What if we had a requirement to restrict parts of our /admin/ section down further?

We likely could create some rather ahem, interesting regular expressions to meet our needs. However, whilst a regex like this:


Will likely make sense to the developer as he or she is writing it, you can bet your bottom dollar that anyone - including that very same dev - will struggle to understand that in 6 months time. At least at first glance.

In case you were wondering, apparently that is a regex for matching IP addresses. I will take Google's word for it.

Instead, Symfony best practice steers us towards the @Security annotation.

Now, in my opinion, annotations are not a prefferable option, if others exist. In this instance, I would really opt for a Security Voter - which we will come too shortly - because I can write tests for code, which I cannot do for annotations.

However, the @Security annotation is, as mentioned, best practice, so let's investigate a little further, before jumping into more depth in the next video.

Reproducing our access_control Entry with an @Security Annotation

To recap, our access_control entry is:

# app/config/security.yml
        - { path: ^/admin/, role: ROLE_ADMIN }

This secures any controller that is serving pages under /admin/.

We could recreate this on a per controller basis by using the @Security annotation:

// src/AppBundle/Controller/AdminController.php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

 * @Route("/admin")
 * @Security("has_role('ROLE_ADMIN')")
class AdminController extends Controller

At this point, we could comment out or remove the access_control entry for our /admin/ resource, and we would still be secure.

Personally, I don't like this approach.

If you have more than one controller for your admin area, you now have to reproduce your security setup on a per controller basis. This is not so great on the DRY front (Don't Repeat Yourself).

Your high level security rules now live all over your codebase, instead of in the obvious place - security.yml. This same argument applies to your Routes as well, if using annotations.

This is why I would rather stick to using access_control entries for restricting routes at a top level.

That's not to say that the @Security annotation isn't useful though, as we shall see in the next video.

Share This Episode

If you have found this video helpful, please consider sharing. I really appreciate it.

Episodes in this series

# Title Duration
1 The Application Setup and Introduction 06:17
2 Using Symfony's Security Annotation 06:43
3 Simple Security Voters 08:28
4 Customising your Access Decision Stategy 04:58