summary
Earlier, we have briefly introduced the authentication process of verifying user name and password.
Here, let's take a look at the permission verification process of Spring Security.
First, let's use a simple example to get a general idea of what Spring Security authorization is.
Examples
First of all, let's try to keep it simple. Make a slight modification on the previous project, and the project structure has basically not changed. Just modify our UserDetailsService, add user permissions, and then modify our test interface to add permission restrictions to it.
UserDetailsService
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import java.util.List; @Service public class MyUserDetailService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { String authority = "ROLE_admin,ROLE_teacher,read,write"; List<GrantedAuthority> grantedAuthorities = AuthorityUtils.commaSeparatedStringToAuthorityList(authority); return User.builder() .username("bob") .password("$2a$10$344aKAgXr17q7u.8l5i7Cu8wUJr/cxBIniLsVtf/WwFrPx0khY62K") .authorities(grantedAuthorities) .build(); } }
If we do not make special configuration changes, Spring Security uses SimpleGrantedAuthority by default.
SimpleGrantedAuthority permission is very Simple. It is worthy of the prefix Simple. There is only one string variable role to represent the role.
How to design permission classes is one thing, and how to verify permissions is another and more important.
For example, looking at the source code of SimpleGrantedAuthority, the purpose of the design must be to indicate whether a user has such role attributes as admin, editor and tech.
However, together with @ PreAuthorize, @ PostAuthorize, @ Secured, they were manipulated in 18 ways, becoming:
Can verify: permissions and roles
However, SimpleGrantedAuthority should abide by the rules: the ROLE of SimpleGrantedAuthority identifying the ROLE must have: ROLE_ Prefix, of course, can be resisted, and it's OK to configure it as other prefixes, but SimpleGrantedAuthority itself has no say, and its autonomy is not in itself, so it can only be slaughtered by others.
Spring Security User is awesome, receiving SimpleGrantedAuthority parameters, you can also receive list that represents the permissions, and the bottom is still converted to SimpleGrantedAuthority list. More awesome, of course, is that User provides a roles interface, which can directly set up the role role instead of adding ROLE_ to it itself. Prefix.
For example, to configure the test user before us:
auth.inMemoryAuthentication() .withUser("tim") .password("111111") .roles("admin") .and() .withUser("allen") .password("222222") .roles("user");
Of course, like the above example code, you can write it into a string, separate it with commas, and then use the tool class method authorityutils Commaseparatedstringtoauthoritylist conversion.
IndexController
import org.springframework.security.access.annotation.Secured; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/index") public class IndexController { @RequestMapping("/hello") public String hello(){ return "Hello World"; } @PreAuthorize("hasRole('admin')") @RequestMapping("/admin") public String admin() { return "admin"; } @PreAuthorize("hasRole('admin') && hasAuthority('update')") @RequestMapping("/admin-update") public String adminAndUpdate() { return "adminAndUpdate"; } @PreAuthorize("hasAuthority('read')") @RequestMapping("/read") public String read() { return "read"; } @PreAuthorize("hasAuthority('update')") @RequestMapping("/update") public String update() { return "update"; } @Secured({"ROLE_user"}) @RequestMapping("/user") public String user() { return "user"; } }
If you don't delve into the principles of @ PreAuthorize, @ PostAuthorize, @ Secured, it's very simple to use. You just need to add the above and below annotations, and then you can use it safely.
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
- @PreAuthorize is called before interface execution.
- @PostAuthorize is called after interface execution.
- @ROLE must be added when the ROLE is verified by Secured_ prefix
Add other permission control methods
In addition to @ PreAuthorize, @ PostAuthorize, @ Secured control permissions, we can also configure permissions through HttpSecurity.
Add in securityconfig (inheriting WebSecurityConfigurerAdapter):
@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/home", "/js/*","/img/*").permitAll() .antMatchers("/admin/**","/upload/**").hasAnyRole("admin") .antMatchers("/order/**").hasAnyRole("user","admin") .antMatchers("/tech/**").hasAnyRole("tech","admin") .anyRequest().authenticated(); http.formLogin(); }
HttpSecurity can also be configured with many things, which we will introduce later.
Above, we have a brief understanding of the permission control and verification provided by Spring Security, which is generally enough.
But if we want to:
- Design your own authority system
- Modify the rules of permission verification
- Custom permissions and custom checks
So what?
Next, let's briefly understand the permission and authentication process of Spring Security.
GrantedAuthority
First of all, grant authority is the abstraction of user permissions, such as the role and identification principal.
Spring Security provides us with three GrantedAuthority implementation classes:
- Simple granted authority: the most important authority is the role
- SwitchUserGrantedAuthority: stores the Authentication of the original user when the user switches, like the user switching operation of Windows user
- JAAS grant authority: in addition to the role, there is also a Principal
JASS(Java Authentication and Authorization Service) in JaasGrantedAuthority, Java authentication and authorization service. This class is more designed as JaasAuthenticationProvider and DefaultJaasAuthenticationProvider services, but you need to implement the AuthorityGranter yourself.
How to design GrantedAuthority is very flexible. It can be designed according to the actual situation. The key to the problem is how to verify it.
In the Spring Security authorization system, there is a very important class FilterSecurityInterceptor. Although it ends with Interceptor, it is an Interceptor. It controls the authorization process. In order to make it too messy, we will introduce it in the filter of the later article.
Here, let's first introduce the voters (access decision voter) and voting Manager (access decision manager) related to the specific determination of permissions.
AccessDecisionVoter
Voters have three options that have been solidified in their bodies:
// agree int ACCESS_GRANTED = 1; // waiver int ACCESS_ABSTAIN = 0; // refuse int ACCESS_DENIED = -1;
Spring Security has provided us with many default voters:
The voting method of RoleVoter is to reject the permission if the authority is empty, and agree if there is a role role and check permission equals in the permission.
RoleHierarchy means role inheritance. For example, the role admin can access all permissions of the role tech, which can be expressed as:
@Bean protected RoleHierarchy roleHierarchy() { RoleHierarchyImpl hierarchy = new RoleHierarchyImpl(); hierarchy.setHierarchy("ROLE_admin > ROLE_tech"); return hierarchy; }
WebExpressionVoter decides whether to agree or reject according to whether the calculated value of the SpEL expression is true.
The @ PreAuthorize, @ PostAuthorize, @ Secured we introduced earlier are the WebExpressionVoter used.
If you are not familiar with Spring EL, you can take a look:
Spring EL notes 1
Spring EL notes II
AccessDecisionManager
AccessDecisionManager is a voting manager. Generally, there will not be only one voter in a vote. The function of voting manager is to hold the list of voters, and then count a result according to the votes of voters.
This involves different voting statistics strategies, so Spring Security provides us with three specific implementation classes:
- Affirmative based, hegemonic mode, means that one vote is passed
- Consensisbased, democratic model, means that the minority is subordinate to the majority
- Unanimusbased, investor mode, means one vote veto
When using @ PreAuthorize, @ PostAuthorize, @ Secured, the default is confirmativebased. As long as there is one vote, it will pass.
TIP: compared with AccessDecisionManager, it is AfterInvocationManager, which manages some afterinvocationproviders to determine the return value of the call.
Permission verification SpEL
Finally, let's talk about the verification of spiel:
@PreAuthorize("hasRole('admin') && hasAuthority('update')")
MethodSecurityExpressionRoot is actually used, which inherits SecurityExpressionRoot. The top-level interface is SecurityExpressionOperations. The whole inheritance system is:
The hasAnyAuthorityName method of SecurityExpressionRoot is actually executed:
private boolean hasAnyAuthorityName(String prefix, String... roles) { Set<String> roleSet = getAuthoritySet(); for (String role : roles) { String defaultedRole = getRoleWithDefaultPrefix(prefix, role); if (roleSet.contains(defaultedRole)) { return true; } } return false; }