当前位置: 移动技术网 > IT编程>开发语言>Java > java中自定义Spring Security权限控制管理示例(实战篇)

java中自定义Spring Security权限控制管理示例(实战篇)

2019年07月22日  | 移动技术网IT编程  | 我要评论

背景描述

项目中需要做细粒的权限控制,细微至url + httpmethod (满足restful,例如: https://.../xxx/users/1, 某些角色只能查看(http get), 而无权进行增改删(post, put, delete))。

表设计

为避嫌,只列出要用到的关键字段,其余敬请自行脑补。

1.admin_user 管理员用户表, 关键字段( id, role_id )。

2.t_role 角色表, 关键字段( id, privilege_id )。

3.t_privilege 权限表, 关键字段( id, url, method )

三个表的关联关系就不用多说了吧,看字段一眼就能看出。

实现前分析

我们可以逆向思考:

要实现我们的需求,最关键的一步就是让spring security的accessdecisionmanager来判断所请求的url + httpmethod 是否符合我们数据库中的配置。然而,accessdecisionmanager并没有来判定类似需求的相关voter, 因此,我们需要自定义一个voter的实现(默认注册的affirmativebased的策略是只要有voter投出access_granted票,则判定为通过,这也正符合我们的需求)。实现voter后,有一个关键参数(collection

总结一下思路步骤:

1.自定义voter实现。

2.自定义configattribute实现。

3.自定义securitymetadatasource实现。

4.authentication包含用户实例(这个其实不用说,大家应该都已经这么做了)。

5.自定义grantedauthority实现。

项目实战

1.自定义grantedauthority实现

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.自定义认证用户实例

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.自定义urlconfigattribute实现

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.自定义securitymetadatasource实现

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.自定义voter实现

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.自定义filtersecurityinterceptor实现

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);
  }
}

配置文件关键配置

<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> 

好啦,接下来享受你的spring security权限控制之旅吧。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网