SpringSecurity实现动态管理权限(三)

SpringBoot整合SpringSecurity实现接口动态管理权限

接上一篇权限管理是后台管理不可缺少的部分,今天结合SpringSecurity实现接口的动态管理。

动态权限管理

SpringSecurity实现权限动态管理,第一步需要创建一个过滤器,doFilter方法需要注意,对于OPTIONS直接放行,否则会出现跨域问题。并且对在上篇文章提到的IgnoreUrlsConfig中的白名单也是直接放行,所有的权限操作都会在super.beforeInvocation(fi)中实现。

/**
 * 动态权限过滤器,用于实现基于路径的动态权限过滤
 * 
 */
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请求直接放行
        if(request.getMethod().equals(HttpMethod.OPTIONS.toString())){
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
            return;
        }
        //白名单请求直接放行
        PathMatcher pathMatcher = new AntPathMatcher();
        for (String path : ignoreUrlsConfig.getUrls()) {
            if(pathMatcher.match(path,request.getRequestURI())){
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
                return;
            }
        }
        //此处会调用AccessDecisionManager中的decide方法进行鉴权操作
        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;
    }

}

在DynamicSecurityFilter中调用super.beforeInvocation(fi)方法时会调用AccessDecisionManager中的decide方法用于鉴权操作,而decide方法中的configAttributes参数会通过SecurityMetadataSource中的getAttributes方法来获取,configAttributes其实就是配置好的访问当前接口所需要的权限,下面是简化版的beforeInvocation源码

public abstract class AbstractSecurityInterceptor implements InitializingBean,
        ApplicationEventPublisherAware, MessageSourceAware {


protected InterceptorStatusToken beforeInvocation(Object object) {

        //获取元数据
        Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
                .getAttributes(object);

        Authentication authenticated = authenticateIfRequired();

        //进行鉴权操作
        try {
            this.accessDecisionManager.decide(authenticated, object, attributes);
        }
        catch (AccessDeniedException accessDeniedException) {
            publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
                    accessDeniedException));

            throw accessDeniedException;
        }
    }
}

上面的介绍,接下来我们实现SecurityMetadataSource接口的getAttributes方法,来获取当前访问的路径资源

/**
 * 动态权限数据源,用于获取动态权限规则
 * 
 */
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<>();
        //获取当前访问的路径
        String url = ((FilterInvocation) o).getRequestUrl();
        String path = URLUtil.getPath(url);
        PathMatcher pathMatcher = new AntPathMatcher();
        Iterator<String> iterator = configAttributeMap.keySet().iterator();
        //获取访问该路径所需资源
        while (iterator.hasNext()) {
            String pattern = iterator.next();
            if (pathMatcher.match(pattern, path)) {
                configAttributes.add(configAttributeMap.get(pattern));
            }
        }
        // 未设置操作请求权限,返回空集合
        return configAttributes;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }

}

我们的后台资源被规则缓存到了一个MAP对象中,所有当后台资源变化时,需要清除缓存,在下次查询的时候重新加载。我们需要修改MyMesResourceController注入DynamicSecurityMetadataSource,当修改后台资源时,需要调用clearDataSource方法来清空缓存的数据。

/**
 * 后台资源管理Controller
 * 
 */
@Controller
@Api(tags = "MyMesResourceController", description = "后台资源管理")
@RequestMapping("/resource")
public class MyMesResourceController {

    @Autowired
    private MyMesResourceService resourceService;
    @Autowired
    private DynamicSecurityMetadataSource dynamicSecurityMetadataSource;

    @ApiOperation("添加后台资源")
    @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();
        }
    }
 }

我们需要实现AccessDecisionManager接口来实现权限校验,对于没有配置资源的接口我们直接允许访问,对于配置了资源的接口,我们把访问所需资源和用户拥有的资源进行比对,如果匹配则允许访问。

/**
 * 动态权限决策管理器,用于判断用户是否有访问权限
 * 
 */
public class DynamicAccessDecisionManager implements AccessDecisionManager {

    @Override
    public void decide(Authentication authentication, Object object,
                       Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        // 当接口未被配置资源时直接放行
        if (CollUtil.isEmpty(configAttributes)) {
            return;
        }
        Iterator<ConfigAttribute> iterator = configAttributes.iterator();
        while (iterator.hasNext()) {
            ConfigAttribute configAttribute = iterator.next();
            //将访问所需资源或用户拥有资源进行比对
            String needAuthority = configAttribute.getAttribute();
            for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
                if (needAuthority.trim().equals(grantedAuthority.getAuthority())) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("抱歉,您没有访问权限");
    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }

}

我们之前在DynamicSecurityMetadataSource中注入了一个DynamicSecurityService对象,它是我自定义的一个动态权限业务接口,其主要用于加载所有的后台资源规则。

/**
 * 动态权限相关业务类
 *
 */
public interface DynamicSecurityService {
    /**
     * 加载资源ANT通配符和资源对应MAP
     */
    Map<String, ConfigAttribute> loadDataSource();
}

结合SpringSecurity实现接口的动态管理权限基本已经实现,明天后天准备讲解一下Redis+AOP优化权限管理

公众号

 

公众号https://mp.weixin.qq.com/s/nfat2WWWUXdmfUGFBAVEuA

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Security是一个功能强大且灵活的框架,用于实现Java应用程序的安全性和权限管理。下面是使用Spring Security实现权限管理的一般步骤: 1. 添加Spring Security依赖:在你的项目中添加Spring Security的依赖,可以通过Maven或者Gradle进行配置。 2. 配置Spring Security:创建一个配置类,继承自WebSecurityConfigurerAdapter,并重写configure方法来配置安全性和权限规则。你可以定义用户角色、访问控制规则、登录页面等。 3. UserDetailsService实现实现UserDetailsService接口,该接口用于根据用户名加载用户信息,包括用户名、密码和所属角色等。你可以从数据库、缓存或其他来源加载用户信息。 4. 密码加密:为了保护用户密码,建议使用密码加密算法对密码进行加密存储。Spring Security提供了PasswordEncoder接口及其实现类,你可以选择适合你的需求的加密算法。 5. 定义用户角色和权限:通过角色和权限的定义,你可以控制用户对特定资源的访问权限。可以使用@PreAuthorize注解或者在配置类中进行定义。 6. 登录认证:Spring Security提供了表单登录认证的功能。你可以通过配置登录页面、登录处理URL、登录成功和失败的处理逻辑等来完成登录认证流程。 7. 配置注销功能:配置注销功能可让用户安全退出应用程序。通过配置注销URL和注销成功处理的逻辑,用户可以通过访问注销URL来完成注销操作。 8. 配置记住我功能:如果你希望用户在关闭浏览器后仍然保持登录状态,可以启用记住我功能。通过配置记住我参数、记住我令牌的生成和验证等,实现记住我功能。 9. 安全防护:为了增强应用程序的安全性,你可以配置防护机制,如防止跨站请求伪造(CSRF)攻击、点击劫持等。 以上是实现权限管理的一般步骤,具体实现方式可以根据你的需求和项目架构进行调整。Spring Security提供了丰富的功能和灵活的配置选项,可以帮助你构建安全性强大的Java应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值