SpringBoot integrates SpringSecurity to dynamically manage interface permissions
Access management is an indispensable part of background management, today combined with SpringSecurity interface dynamic management.
Dynamic Rights Management
SpringSecurity implements dynamic permission management. The first step is to create a filter. The doFilter method needs to be paid attention to. The IgnoreUrlsConfig white list mentioned in the previous article is also allowed directly, all permissions are implemented in super. BeforeInvocation (fi).
/ * *
* Dynamic permission filter, used to achieve path-based dynamic permission filtering
*
* /
public class DynamicSecurityFilter extends AbstractSecurityInterceptor implements Filter {
@Autowired
private DynamicSecurityMetadataSource dynamicSecurityMetadataSource;
@Autowired
private IgnoreUrlsConfig ignoreUrlsConfig;
@Autowired
public void setMyAccessDecisionManager(DynamicAccessDecisionManager dynamicAccessDecisionManager) {
super.setAccessDecisionManager(dynamicAccessDecisionManager);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
//OPTIONS requests permission directly
if(request.getMethod().equals(HttpMethod.OPTIONS.toString())){
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
return;
}
// Whitelist requests direct release
PathMatcher pathMatcher = new AntPathMatcher();
for (String path : ignoreUrlsConfig.getUrls()) {
if(pathMatcher.match(path,request.getRequestURI())){
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
return;
}
}
// The decide method in AccessDecisionManager is called for authentication
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
@Override
public void destroy() {
}
@Override
public Class<? >getSecureObjectClass() {
return FilterInvocation.class;
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return dynamicSecurityMetadataSource;
}
}
Copy the code
The Decide method in AccessDecisionManager is called for authentication when the super-.beforeInvocation (FI) method is called in DynamicSecurityFilter, and the configAttribut method in Decide is called The ES parameter is obtained by the getAttributes method in SecurityMetadataSource. ConfigAttributes is the configured permission to access the interface. Here is the simplified beforeInvocation source code
public abstract class AbstractSecurityInterceptor implements InitializingBean,
ApplicationEventPublisherAware, MessageSourceAware {
protected InterceptorStatusToken beforeInvocation(Object object) {
// Get metadata
Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
.getAttributes(object);
Authentication authenticated = authenticateIfRequired();
// Perform authentication
try {
this.accessDecisionManager.decide(authenticated, object, attributes);
}
catch (AccessDeniedException accessDeniedException) {
publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
accessDeniedException));
throw accessDeniedException;
}
}
}
Copy the code
Following that, we implement the getAttributes method of the SecurityMetadataSource interface to retrieve the currently accessed path resource
/ * *
* Dynamic permission data source, used to obtain dynamic permission rules
*
* /
public class DynamicSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
private static Map<String, ConfigAttribute> configAttributeMap = null;
@Autowired
private DynamicSecurityService dynamicSecurityService;
@PostConstruct
public void loadDataSource() {
configAttributeMap = dynamicSecurityService.loadDataSource();
}
public void clearDataSource() {
configAttributeMap.clear();
configAttributeMap = null;
}
@Override
public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
if (configAttributeMap == null) this.loadDataSource();
List<ConfigAttribute> configAttributes = new ArrayList<>();
// Get the current access path
String url = ((FilterInvocation) o).getRequestUrl();
String path = URLUtil.getPath(url);
PathMatcher pathMatcher = new AntPathMatcher();
Iterator<String> iterator = configAttributeMap.keySet().iterator();
// Get the resources needed to access the path
while (iterator.hasNext()) {
String pattern = iterator.next();
if (pathMatcher.match(pattern, path)) {
configAttributes.add(configAttributeMap.get(pattern));
}
}
// Return an empty set
return configAttributes;
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<? > aClass) {
return true;
}
}
Copy the code
Our background resources are regularly cached in a MAP object, so when the background resources change, we need to clear the cache and reload in the next query. We need to modify MyMesResourceController injection DynamicSecurityMetadataSource, when change the background resources, you need to call clearDataSource method to clear the cache data.
/ * *
* Background resource management Controller
*
* /
@Controller
@Api(tags = "MyMesResourceController", description = "Background Resource Management")
@RequestMapping("/resource")
public class MyMesResourceController {
@Autowired
private MyMesResourceService resourceService;
@Autowired
private DynamicSecurityMetadataSource dynamicSecurityMetadataSource;
@ApiOperation("Add Background Resources")
@RequestMapping(value = "/create", method = RequestMethod.POST)
@ResponseBody
public CommonResult create(@RequestBody UmsResource umsResource) {
int count = resourceService.create(umsResource);
dynamicSecurityMetadataSource.clearDataSource();
if (count > 0) {
return CommonResult.success(count);
} else {
return CommonResult.failed();
}
}
}
Copy the code
We need to implement the AccessDecisionManager interface to verify permissions. For interfaces that are not configured with resources, we directly allow access. For interfaces configured with resources, we compare the resources required for access with the resources owned by the user, and allow access if they match.
/ * *
* Dynamic permission decision manager, used to determine whether a user has access rights
*
* /
public class DynamicAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
// If no resource is configured on the interface, the interface is allowed
if (CollUtil.isEmpty(configAttributes)) {
return;
}
Iterator<ConfigAttribute> iterator = configAttributes.iterator();
while (iterator.hasNext()) {
ConfigAttribute configAttribute = iterator.next();
// Compare access required resources or user-owned resources
String needAuthority = configAttribute.getAttribute();
for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
if (needAuthority.trim().equals(grantedAuthority.getAuthority())) {
return;
}
}
}
throw new AccessDeniedException("Sorry, you don't have access.");
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
@Override
public boolean supports(Class<? > aClass) {
return true;
}
}
Copy the code
Before us in the DynamicSecurityMetadataSource injected a DynamicSecurityService object, it is my custom a dynamic access business interface, it is mainly used for loading all the background resources rules.
/ * *
* Dynamic permission related business classes
*
* /
public interface DynamicSecurityService {
/ * *
* Load resource ANT wildcard and resource MAP
* /
Map<String, ConfigAttribute> loadDataSource();
}
Copy the code
Combined with SpringSecurity interface dynamic management permissions have been basically realized, tomorrow and the day after tomorrow ready to explain the Redis+AOP optimization permission management
No. The public https://mp.weixin.qq.com/s/nfat2WWWUXdmfUGFBAVEuA