( In this section, we'll look at several authorization methods for spring security and a brief source tracking. When you first came into contact with spring security, it was difficult to find a lot of articles and examples on the web in order to achieve its authorization, especially its custom authorization, but now it is fruitful to try learning with official documents and demo.
Access Control Based on Expression Spel
( Spring Security uses Spring EL for expression support and does not know that Spring EL's children's shoes learn by themselves. According to Document https://docs.spring.io/spring-security/site/docs/5.4.9/reference/html5/#el-access looks at the upper and lower inheritance relationships of the SecurityExpressionRoot class in IDEA, and SecurityExpressionOperations declares each expression interface, which ultimately implements each specific expression logic by WebSecurityExpressionRoot and MethodSecurityExpressionRoot. The inheritance relationship is as follows:
( Here we can see that the most common way to authorize our applications is based on Web\Method. Let's look again at the most basic SPELs defined in the SecurityExpressionRoot class:
( We will briefly introduce several expression interfaces:
Expression Description hasRole(String role) Returns true if the current principal has the specified role hasAnyRole(String... roles) Returns true if the current principal has any provided roles hasAuthority(String authority) Returns true if the current principal has the specified permissions. hasAnyAuthority(String... authorities) Returns true if the current principal has any provided permissions authentication Allow direct access to the current Authentication object obtained from the SecurityContext principal Allow direct access to principal objects representing the current userAuthorization Method
Web/Url-based security expression
This allows for security validation of either a single Url or a batch of Urls, such as
@Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() // p1 permission is required for the interface / admin/api/hello .antMatchers("/admin/api/hello").hasAuthority("p1") // User privileges for interfaces accessing/user/api/** .antMatchers("/user/api/**").hasRole("USER") .antMatchers("/app/api/**").permitAll() .antMatchers("/css/**", "/index").permitAll() .antMatchers("/user/**").hasRole("USER") .and() .formLogin() .loginPage("/login") .failureUrl("/login-error") .permitAll(); }
Method-based security expression
Method Security Expressions
Method security is a bit more complicated than a simple allow or deny rule. Spring Security 3.0 introduced some new annotations in order to allow comprehensive support for the use of expressions.
Spring Security 3.0 introduced new annotations to fully support the use of expressions, such as @PreAuthorize, @PreFilter, @PostAuthorize and @PostFilter, which believe you have a foundation for web development.
-
@PreAuthorize: Authenticate before accessing the method
-
@PreFilter: Same as above
-
@PostAuthorize: Authenticate after accessing a method
-
@PostFiltert: Same as above
public class AdminController { @GetMapping("hello") @PostAuthorize("hasRole('User')") public String hello() { return "hello, admin"; } @GetMapping("p1") @PreAuthorize("hasAuthority('p1')") public String p1() { return "hello, p1"; } }
However, method-based needs to be annotated in the configuration class beforehand to turn on method validation.
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
There are two other ways (based on AOP, spring security native note @Secure) that interested partners can refer to on their own, as described on the website https://docs.spring.io/spring-security/site/docs/5.4.9/reference/html5/#secure-object-impls
Authorization Principles
( According to Document https://docs.spring.io/spring-security/site/docs/5.4.9/reference/html5/#secure-object-impls indicates that spring security provides interceptors to control access to secure objects, such as method calls, web requests. AccessDecisionManager makes a pre-decision about whether to allow calls to continue.
( View the AccessDecisionManager interface:
decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
Parameter description:
- authentication: current logon object principal
- object:Current Security Protected object
- configAttributes: The permission attributes necessary to access the current object
Looking at its three implementation classes, the default implementation determines whether the current security object can be accessed based on the voting mechanism of each implementation class:
AffirmativeBased: The current protected object can be accessed as long as one of the configAttributes permissions is satisfied
ConsensusBased: More than half of the permissions are met to access the current protected object
All permissions in UnanimousBased:configAttributes are satisfied to access the current protected object
Since we don't know which implementation class the default is, we break the decision method on all three classes so that we know which implementation class the default is.
Internal voting enables interested partners to explore themselves so that we can get a general idea of the default authorization implementation mechanism for spring security. We then implement our custom authorization based on that mechanism.
Give a schematic diagram of the official website
- First, FilterSecurityInterceptor gets an Authentication from SecurityContextHolder
- Second, the FilterSecurityInterceptor creates a FilterInvocation from the HTTP ServletRequest, HttpServletResponse, and FilterChain passed in to the FilterSecurityInterceptor
- Next, it passes FilterInvocation to SecurityMetadataSource to get ConfigAttributes
- Finally, it passes Authentication, FilterInvocation, and ConfigAttributes to AccessDecision Manager.
- If the authorization is denied, an AccessDeniedException is thrown. In this case, ExceptionTranslationFilter handles AccessDeniedException
- If access is granted, the FilterSecurityInterceptor continues to use FilterChain, which allows the application to handle normally.
Customize Authorization Method
( According to the gourd ladle, we need it first
1. Customize an AccessDecisionManager implementation class to determine whether it can authenticate and access protected objects;@Component public class CustomUrlDecisionManager implements AccessDecisionManager { @Override public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { for (ConfigAttribute configAttribute : configAttributes) { String needRole = configAttribute.getAttribute(); if ("ROLE_LOGIN".equals(needRole)) { if (authentication instanceof AnonymousAuthenticationToken) { throw new AccessDeniedException("Not logged in yet, please log in!"); }else { return; } } Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities(); for (GrantedAuthority authority : authorities) { if (authority.getAuthority().equals(needRole)) { return; } } } throw new AccessDeniedException("Insufficient privileges, please contact your administrator!"); } @Override public boolean supports(ConfigAttribute attribute) { return true; } @Override public boolean supports(Class<?> clazz) { return true; } }2. Next, implement a FilterInvocationSecurityMetadataSource implementation class that gives you the specific permissions you need to access protected objects.
/** * The purpose of this class is to analyze the roles required by a request based on the address of the user's request */ @Component public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource { @Autowired MenuService menuService; AntPathMatcher antPathMatcher = new AntPathMatcher(); @Override public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { String requestUrl = ((FilterInvocation) object).getRequestUrl(); List<Menu> menus = menuService.getAllMenusWithRole(); for (Menu menu : menus) { if (antPathMatcher.match(menu.getUrl(), requestUrl)) { List<Role> roles = menu.getRoles(); String[] str = new String[roles.size()]; for (int i = 0; i < roles.size(); i++) { str[i] = roles.get(i).getName(); } return SecurityConfig.createList(str); } } return SecurityConfig.createList("ROLE_LOGIN"); } @Override public Collection<ConfigAttribute> getAllConfigAttributes() { return null; } @Override public boolean supports(Class<?> clazz) { return true; } }3. Add the above two objects to the interceptor and reset the two attributes of FilterSecurityInterceptor
http.authorizeRequests() .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() { @Override public <O extends FilterSecurityInterceptor> O postProcess(O object) { object.setAccessDecisionManager(customUrlDecisionManager); object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource); return object; } })
( Believe that here, the small partner can also implement the authorization method according to their actual project needs, if it is AOP/@security mode, you need to read the documentation again. Okay, that's where the chapter on spring security ends, and I'll continue with the chapter on spring security oauth2.