springboot+security整合3

  这篇讲解如何自定义鉴权过程,实现根据数据库查询出的url和method是否匹配当前请求的url和method来决定有没有权限。security鉴权过程如下:

一、 重写metadataSource类

  1. 编写MyGranteAuthority类,让权限包含url和method两个部分。
public class MyGrantedAuthority implements GrantedAuthority {
    private String method;
    private String url;

    public MyGrantedAuthority(String method, String url) {
        this.method = method;
        this.url = url;
    }

    @Override
    public String getAuthority() {
        return url;
    }

    public String getMethod() {
        return method;
    }

    public String getUrl() {
        return url;
    }

    @Override
    public boolean equals(Object obj) {
        if(this==obj) return true;
        if(obj==null||getClass()!= obj.getClass()) return false;
        MyGrantedAuthority grantedAuthority = (MyGrantedAuthority)obj;
        if(this.method.equals(grantedAuthority.getMethod())&&this.url.equals(grantedAuthority.getUrl()))
            return true;
        return false;
    }
}
  1. 编写MyConfigAttribute类,实现ConfigAttribute接口,代码如下:
public class MyConfigAttribute implements ConfigAttribute {
    private HttpServletRequest httpServletRequest;
    private MyGrantedAuthority myGrantedAuthority;

    public MyConfigAttribute(HttpServletRequest httpServletRequest) {
        this.httpServletRequest = httpServletRequest;
    }

    public MyConfigAttribute(HttpServletRequest httpServletRequest, MyGrantedAuthority myGrantedAuthority) {
        this.httpServletRequest = httpServletRequest;
        this.myGrantedAuthority = myGrantedAuthority;
    }

    public HttpServletRequest getHttpServletRequest() {
        return httpServletRequest;
    }

    @Override
    public String getAttribute() {
        return myGrantedAuthority.getUrl();
    }

    public MyGrantedAuthority getMyGrantedAuthority() {
        return myGrantedAuthority;
    }
}
  1. 编写MySecurityMetadataSource类,获取当前url所需要的权限
@Component
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private JurisdictionMapper jurisdictionMapper;
    private List<Jurisdiction> jurisdictions;

    private void loadResource() {
        this.jurisdictions = jurisdictionMapper.selectAllPermission();
    }


    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        if (jurisdictions == null) this.loadResource();
        HttpServletRequest request = ((FilterInvocation) object).getRequest();
        Set<ConfigAttribute> allConfigAttribute = new HashSet<>();
        AntPathRequestMatcher matcher;
        for (Jurisdiction jurisdiction : jurisdictions) {
            //使用AntPathRequestMatcher比较可让url支持ant风格,例如/user/*/a
            //*匹配一个或多个字符,**匹配任意字符或目录
            matcher = new AntPathRequestMatcher(jurisdiction.getUrl(), jurisdiction.getMethod()); 
            if (matcher.matches(request)) {
                ConfigAttribute configAttribute = new MyConfigAttribute(request,new MyGrantedAuthority(jurisdiction.getMethod(),jurisdiction.getUrl()));
                allConfigAttribute.add(configAttribute);
                //这里是获取到一个权限就返回,根据校验规则也可获取多个然后返回
                return allConfigAttribute;
            }
        }
        //未匹配到,说明无需权限验证
        return null;
    }

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

    @Override
    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}

二、 编写MyAccessDecisionManager类

  实现AccessDecisionManager接口以实现权限判断,直接return说明验证通过,如不通过需要抛出对应错误,代码如下:

@Component
public class MyAccessDecisionManager implements AccessDecisionManager{
    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
            throws AccessDeniedException, InsufficientAuthenticationException {
        //无需验证放行
        if(configAttributes==null || configAttributes.size()==0)
            return;
        if(!authentication.isAuthenticated()){
            throw new InsufficientAuthenticationException("未登录");
        }
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        for(ConfigAttribute attribute : configAttributes){
            MyConfigAttribute urlConfigAttribute = (MyConfigAttribute)attribute;
            for(GrantedAuthority authority: authorities){
                MyGrantedAuthority myGrantedAuthority = (MyGrantedAuthority)authority;
                if(urlConfigAttribute.getMyGrantedAuthority().equals(myGrantedAuthority))
                    return;
            }
        }
        throw new AccessDeniedException("无权限");
    }

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

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

三、 编写MyFilterSecurityInterceptor类

  该类继承AbstractSecurityInterceptor类,实现Filter接口,代码如下:

@Component
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {

    //注入上面编写的两个类
    @Autowired
    private MySecurityMetadataSource mySecurityMetadataSource;

    @Autowired
    public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
        super.setAccessDecisionManager(myAccessDecisionManager);
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
    }


    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        FilterInvocation fi = new FilterInvocation(request, response, chain);
        invoke(fi);
    }

    public void invoke(FilterInvocation fi) throws IOException, ServletException {
        //这里进行权限验证
        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 this.mySecurityMetadataSource;
    }
}

四、 加入到security的过滤器链中

.addFilterBefore(urlFilterSecurityInterceptor,FilterSecurityInterceptor.class)

完成

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Hongten

Struts2 ActionWildcard(通配符配置)约定优于配置

新建web project:struts2_0500_actionwildcard

11020
来自专栏别先生

Spring的核心之IoC容器创建对象

Spring的Ioc容器,是Spring的核心内容:   作用:对象的创建和处理对象的依赖关系。 Spring容器创建对象有以下几种方式:   1:调...

22850
来自专栏GopherCoder

『Go 内置库第一季:json』

在日常开发过程中,使用最频繁的当然是内置库,无数的开源项目,无不是在内置库的基础之上进行衍生、开发,所以其实是有很大的必要进行梳理学习。

12120
来自专栏大前端开发

理解和使用ES6中的Symbol

ES6中引入了一种新的基础数据类型:Symbol,不过很多开发者可能都不怎么了解它,或者觉得在实际的开发工作中并没有什么场景应用到它,那么今天我们来讲讲这个数据...

42830
来自专栏用户2442861的专栏

HttpEntity的类型及其使用

  代表底层流的基本实体。通常是在http报文中获取的实体。他只有一个空参的构造方法。刚创建时没有内容,长度为负值。需要通过两个方法,把值赋进去。

1.3K10
来自专栏飞雪无情的博客

Groovy基础

以下内容节选自《Android Gradle权威指南》,更多关于《Android Gradle权威指南》的内容请参见http://yuedu.baidu.com...

15020
来自专栏菩提树下的杨过

lombok在IntelliJ IDEA下的使用

lombok是一款可以精减java代码、提升开发人员生产效率的辅助工具,利用注解在编译期自动生成setter/getter/toString()/constru...

24090
来自专栏Ryan Miao

SpringMVC参数校验(针对`@RequestBody`返回`400`)

SpringMVC参数校验(针对@RequestBody返回400) From https://ryan-miao.github.io/2017/05/20/s...

59440
来自专栏函数式编程语言及工具

Scalaz(49)- scalaz-stream: 深入了解-Sink/Channel

   一个完整的scalaz-stream有以下几个部分组成:Source -> Transducer -> Sink,用直白文字来描述就是:“输入 -> 传换...

22980
来自专栏清晨我上码

基于session的国际化实现方式

15620

扫码关注云+社区

领取腾讯云代金券