When a user wants to access a protected resource in Spring Security, the user has some roles and the access to the resource requires some roles. The voting machine and voting mechanism are used when comparing the roles the user has with the roles the resource requires.
When a user wants to access a resource, the voter votes for or against the resource based on the user’s role, and the voting is based on the results of the voter.
In Spring Security, three voting mechanisms are provided by default, but it is possible to not use the voting mechanisms and voters provided by the system and define them completely by ourselves.
This article focuses on the three voting mechanisms and the default voter.
Let’s look at the voter first.
In Spring Security, the voter is regulated by the
AccessDecisionVoter interface, let’s look at the implementation of the
As you can see, there are several implementations of the voter, and we can choose one or more of them, or we can customize the voter, and the default voter is
Let’s look at the definition of AccessDecisionVoter.
Let me explain a little.
- first of all, three constants are defined, and the meaning of each constant can be seen from its name. 1 means yes; 0 means abstain; -1 means reject.
- two supports methods are used to determine whether the voter supports the current request.
- vote is a specific voting method. It is implemented in different implementation classes. Three parameters, authentication indicates the current login subject; object is an ilterInvocation that encapsulates the current request; attributes indicates the set of roles required by the currently accessed interface.
Let’s look at the implementation of each of the voters.
RoleVoter is mainly used to determine whether the current request has the role required by the interface, let’s look at its vote method.
The logic of this method is very simple. If the current login subject is null, it returns
ACCESS_DENIED to deny access; otherwise, it extracts the role information from the authentication of the current login subject and compares it with the attributes, and if it has any of the required roles in the attributes, it returns
ACCESS_GRANTED to deny access. If any of the required roles in attributes is present,
ACCESS_GRANTED is returned, indicating that access is allowed. For example, if the role in attributes is [a,b,c] and the current user has a, then access is allowed, and it is not necessary to have all three roles.
Another thing to note is the supports method of RoleVoter, let’s take a look.
As you can see, there is a rolePrefix prefix involved here, which is
ROLE_. In the supports method, only if the subject role prefix is
ROLE_, this supoorts method will return true and this voter will take effect.
RoleHierarchyVoter is a subclass of RoleVoter, which introduces role hierarchy management, or role inheritance, based on RoleVoter role judgment.
The vote method of the RoleHierarchyVoter class is the same as RoleVoter, the only difference is that the RoleHierarchyVoter class overrides the extractAuthorities method.
After the role hierarchy, you need to get the actual roles available through the getReachableGrantedAuthorities method
This is an expression-based authority control voter, which we won’t go into too much detail here (I’ll explain it in detail when I get a chance). Simply look at its vote method.
If you are skilled in using SpEL, this code should be easy to understand
The code here actually builds a weca object based on the attributes passed in, then builds a ctx object based on the authentication parameters passed in, and finally calls the evaluateAsBoolean method to determine if the permissions match.
The three voters described above are the three we use more often in practical development.
More cold voting machines
Voters that handle Jsr-250 permissions annotations, such as
AuthenticatedVoter is used to determine whether the ConfigAttribute has
IS_AUTHENTICATED_ANONYMOUSLY roles on it.
IS_AUTHENTICATED_FULLYmeans the current authenticated user must be authenticated by username/password, authentication by RememberMe is not valid.
IS_AUTHENTICATED_REMEMBEREDmeans that the currently logged-in user must be authenticated by RememberMe.
IS_AUTHENTICATED_ANONYMOUSLYmeans that the currently logged-in user must be anonymous.
Consider this voter when a project introduces RememberMe and wants to distinguish between different authentication methods.
Provides helper methods for writing domain object ACL options that are not bound to any specific ACL system.
Permissions handled using the
@PreAuthorize annotations are authorized through
Of course, if these voters do not meet the requirements, they can be customized.
2. Voting mechanism
A request doesn’t necessarily have only one voter, there may be multiple voters, so on top of the voters we need voting mechanism.
There are three main voting-related classes.
Their inheritance relationship is shown in the figure above.
All three decision makers call through all the voters in the project, and the default decision maker used is
The differences between the three decision makers are as follows.
AffirmativeBased: Passes if one voter agrees.
ConsensusBased: The request passes if a majority of the voters agree, in case of a tie, it depends on the value of the allowIfEqualGrantedDeniedDecisions parameter.
UnanimousBased: The request is passed if all voters agree.
The specific logic here is relatively simple, I will not analyze the source code, you can see for yourself if you are interested.
3. Where to configure?
When we use expression-based permission control, like the following.
Then the default voter and decision maker are configured in the
Here you can see the default decision maker and voter, and after the decision maker
AffirmativeBased object is created, it also calls the postProcess method to register to the Spring container. If we want to modify the object, it is very easy to do so.
Here is just a demo for you, normally we don’t need to modify it like this . When we use different permission configuration methods, there will be automatically configured corresponding voter and decision maker. Or we configure the voter and decision maker manually, if they are configured by the system, in most cases we don’t need to modify them.