This article mainly introduces the custom Spring Security permission control management example in java (practical article), which has certain reference value. Interested friends can refer to it.
Background description
The project requires fine-grained permission control, down to url + httpmethod (satisfying restful, for example: http://www.php.cn /, some roles can only view (HTTP GET), but do not have the right to add, modify or delete (POST, PUT, DELETE)).
Table design
To avoid suspicion, only the key fields to be used are listed, please figure out the rest by yourself.
1.admin_user administrator user table, key fields (id, role_id).
2.t_role role table, key fields (id, privilege_id).
3.t_privilege permission table, key fields (id, url, method)
Needless to say more about the relationship between the three tables, you can see it at a glance at the fields out.
Pre-implementation analysis
We can think in reverse:
To realize our needs, the most critical step is to let Spring Security's AccessDecisionManager judge Whether the requested url + httpmethod matches the configuration in our database. However, AccessDecisionManager does not determine related Voters with similar needs. Therefore, we need to customize a Voter implementation (the default registered AffirmativeBased strategy is that as long as a Voter casts an ACCESS_GRANTED vote, it will be determined as passed, which is in line with our need). After implementing the voter, there is a key parameter (Collection
Summarize the steps of the idea:
1. Customize the voter implementation.
2. Customize the ConfigAttribute implementation.
3. Customize SecurityMetadataSource implementation
4.Authentication contains user instances (it goes without saying, everyone should have already done this)
5. Customize GrantedAuthority implementation.
Project actual combat
1. Customized GrantedAuthority implementation
UrlGrantedAuthority.java
public class UrlGrantedAuthority implements GrantedAuthority { private final String httpMethod; private final String url; public UrlGrantedAuthority(String httpMethod, String url) { this.httpMethod = httpMethod; this.url = url; } @Override public String getAuthority() { return url; } public String getHttpMethod() { return httpMethod; } public String getUrl() { return url; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; UrlGrantedAuthority target = (UrlGrantedAuthority) o; if (httpMethod.equals(target.getHttpMethod()) && url.equals(target.getUrl())) return true; return false; } @Override public int hashCode() { int result = httpMethod != null ? httpMethod.hashCode() : 0; result = 31 * result + (url != null ? url.hashCode() : 0); return result; } }
2. Custom authentication user instance
public class SystemUser implements UserDetails { private final Admin admin; private List<MenuOutput> menuOutputList; private final List<GrantedAuthority> grantedAuthorities; public SystemUser(Admin admin, List<AdminPrivilege> grantedPrivileges, List<MenuOutput> menuOutputList) { this.admin = admin; this.grantedAuthorities = grantedPrivileges.stream().map(it -> { String method = it.getMethod() != null ? it.getMethod().getLabel() : null; return new UrlGrantedAuthority(method, it.getUrl()); }).collect(Collectors.toList()); this.menuOutputList = menuOutputList; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return this.grantedAuthorities; } @Override public String getPassword() { return admin.getPassword(); } @Override public String getUsername() { return null; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } public Long getId() { return admin.getId(); } public Admin getAdmin() { return admin; } public List<MenuOutput> getMenuOutputList() { return menuOutputList; } public String getSalt() { return admin.getSalt(); } }
3. Custom UrlConfigAttribute implementation
public class UrlConfigAttribute implements ConfigAttribute { private final HttpServletRequest httpServletRequest; public UrlConfigAttribute(HttpServletRequest httpServletRequest) { this.httpServletRequest = httpServletRequest; } @Override public String getAttribute() { return null; } public HttpServletRequest getHttpServletRequest() { return httpServletRequest; } }
4. Customized SecurityMetadataSource implementation
public class UrlFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource { @Override public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { final HttpServletRequest request = ((FilterInvocation) object).getRequest(); Set<ConfigAttribute> allAttributes = new HashSet<>(); ConfigAttribute configAttribute = new UrlConfigAttribute(request); allAttributes.add(configAttribute); return allAttributes; } @Override public Collection<ConfigAttribute> getAllConfigAttributes() { return null; } @Override public boolean supports(Class<?> clazz) { return FilterInvocation.class.isAssignableFrom(clazz); } }
5. Customized voter implementation
public class UrlMatchVoter implements AccessDecisionVoter<Object> { @Override public boolean supports(ConfigAttribute attribute) { if (attribute instanceof UrlConfigAttribute) return true; return false; } @Override public boolean supports(Class<?> clazz) { return true; } @Override public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) { if(authentication == null) { return ACCESS_DENIED; } Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities(); for (ConfigAttribute attribute : attributes) { if (!(attribute instanceof UrlConfigAttribute)) continue; UrlConfigAttribute urlConfigAttribute = (UrlConfigAttribute) attribute; for (GrantedAuthority authority : authorities) { if (!(authority instanceof UrlGrantedAuthority)) continue; UrlGrantedAuthority urlGrantedAuthority = (UrlGrantedAuthority) authority; if (StringUtils.isBlank(urlGrantedAuthority.getAuthority())) continue; //如果数据库的method字段为null,则默认为所有方法都支持 String httpMethod = StringUtils.isNotBlank(urlGrantedAuthority.getHttpMethod()) ? urlGrantedAuthority.getHttpMethod() : urlConfigAttribute.getHttpServletRequest().getMethod(); //用Spring已经实现的AntPathRequestMatcher进行匹配,这样我们数据库中的url也就支持ant风格的配置了(例如:/xxx/user/**) AntPathRequestMatcher antPathRequestMatcher = new AntPathRequestMatcher(urlGrantedAuthority.getAuthority(), httpMethod); if (antPathRequestMatcher.matches(urlConfigAttribute.getHttpServletRequest())) return ACCESS_GRANTED; } } return ACCESS_ABSTAIN; } }
6. Custom FilterSecurityInterceptor implementation
public class UrlFilterSecurityInterceptor extends FilterSecurityInterceptor { public UrlFilterSecurityInterceptor() { super(); } @Override public void init(FilterConfig arg0) throws ServletException { super.init(arg0); } @Override public void destroy() { super.destroy(); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { super.doFilter(request, response, chain); } @Override public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() { return super.getSecurityMetadataSource(); } @Override public SecurityMetadataSource obtainSecurityMetadataSource() { return super.obtainSecurityMetadataSource(); } @Override public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource newSource) { super.setSecurityMetadataSource(newSource); } @Override public Class<?> getSecureObjectClass() { return super.getSecureObjectClass(); } @Override public void invoke(FilterInvocation fi) throws IOException, ServletException { super.invoke(fi); } @Override public boolean isObserveOncePerRequest() { return super.isObserveOncePerRequest(); } @Override public void setObserveOncePerRequest(boolean observeOncePerRequest) { super.setObserveOncePerRequest(observeOncePerRequest); } }
Configuration file Key configuration
<security:http> ... <security:custom-filter ref="filterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR" /> </security:http> <security:authentication-manager alias="authenticationManager"> <security:authentication-provider ref="daoAuthenticationProvider"/> </security:authentication-manager> <bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased"> <constructor-arg> <list> <bean id="authenticatedVoter" class="org.springframework.security.access.vote.AuthenticatedVoter" /> <bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter" /> <bean id="urlMatchVoter" class="com.mobisist.app.security.access.voter.UrlMatchVoter" /> </list> </constructor-arg> </bean> <bean id="securityMetadataSource" class="com.mobisist.app.security.access.UrlFilterInvocationSecurityMetadataSource" /> <bean id="filterSecurityInterceptor" class="com.mobisist.app.security.access.UrlFilterSecurityInterceptor"> <property name="authenticationManager" ref="authenticationManager"/> <property name="accessDecisionManager" ref="accessDecisionManager"/> <property name="securityMetadataSource" ref="securityMetadataSource" /> </bean>
Okay, let’s enjoy your Spring Security permission control journey
The above is in java. Customize the content of Spring Security permission control management example (Practice). For more related content, please pay attention to the PHP Chinese website (www.php.cn)!