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

发布时间 - 2026-01-10 23:01:21    点击率:

背景描述

项目中需要做细粒的权限控制,细微至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权限控制之旅吧。

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


# spring  # security  # 权限  # springsecurity自定义  # 权限控制  # 详解Spring Security 中的四种权限控制方式  # spring security动态配置url权限的2种实现方法  # SpringSecurity动态加载用户角色权限实现登录及鉴权功能  # Spring security实现登陆和权限角色控制  # 解决Spring Security的权限配置不生效问题  # SpringBoot整合Security实现权限控制框架(案例详解)  # Spring security实现权限管理示例  # SpringBoot2.0 整合 SpringSecurity 框架实现用户权限安全管理方法  # Spring Security动态权限的实现方法详解  # 基于Spring Security的动态权限系统设计与实现  # 自定义  # 数据库中  # 就能  # 也就  # 我们可以  # 这也  # 之旅  # 要用  # 这么做  # 多说  # 好啦  # 最关键  # 配置文件  # 大家多多  # 是否符合  # 有一个  # 默认为  # 投出  # 关联关系  # 只要有 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: Python文件流缓冲机制_IO性能解析【教程】  Laravel如何集成第三方登录_Laravel Socialite实现微信QQ微博登录  如何在IIS中新建站点并解决端口绑定冲突?  Laravel如何使用Guzzle调用外部接口_Laravel发起HTTP请求与JSON数据解析【详解】  PHP的CURL方法curl_setopt()函数案例介绍(抓取网页,POST数据)  如何在阿里云ECS服务器部署织梦CMS网站?  独立制作一个网站多少钱,建立网站需要花多少钱?  大连网站制作费用,大连新青年网站,五年四班里的视频怎样下载啊?  电商网站制作价格怎么算,网上拍卖流程以及规则?  iOS验证手机号的正则表达式  浅谈redis在项目中的应用  Laravel怎么连接多个数据库_Laravel多数据库连接配置  网站建设要注意的标准 促进网站用户好感度!  Laravel Eloquent访问器与修改器是什么_Laravel Accessors & Mutators数据处理技巧  北京网站制作费用多少,建立一个公司网站的费用.有哪些部分,分别要多少钱?  Laravel如何生成API文档?(Swagger/OpenAPI教程)  php结合redis实现高并发下的抢购、秒杀功能的实例  如何在VPS电脑上快速搭建网站?  瓜子二手车官方网站在线入口 瓜子二手车网页版官网通道入口  如何快速搭建支持数据库操作的智能建站平台?  矢量图网站制作软件,用千图网的一张矢量图做公司app首页,该网站并未说明版权等问题,这样做算不算侵权?应该如何解决?  Laravel怎么实现搜索功能_Laravel使用Eloquent实现模糊查询与多条件搜索【实例】  canvas 画布在主流浏览器中的尺寸限制详细介绍  如何在万网自助建站中设置域名及备案?  桂林网站制作公司有哪些,桂林马拉松怎么报名?  如何在HTML表单中获取用户输入并结合JavaScript动态控制复利计算循环  香港服务器建站指南:免备案优势与SEO优化技巧全解析  Laravel如何将应用部署到生产服务器_Laravel生产环境部署流程  Laravel如何实现数据导出到CSV文件_Laravel原生流式输出大数据量CSV【方案】  Laravel如何使用Service Container和依赖注入?(代码示例)  Win11关机界面怎么改_Win11自定义关机画面设置【工具】  Laravel PHP版本要求一览_Laravel各版本环境要求对照  如何用PHP工具快速搭建高效网站?  Laravel如何获取当前登录用户信息_Laravel Auth门面使用与Session用户读取【技巧】  微信推文制作网站有哪些,怎么做微信推文,急?  佛山企业网站制作公司有哪些,沟通100网上服务官网?  阿里云网站搭建费用解析:服务器价格与建站成本优化指南  如何快速生成凡客建站的专业级图册?  如何在腾讯云服务器快速搭建个人网站?  如何在Windows虚拟主机上快速搭建网站?  Python3.6正式版新特性预览  Chrome浏览器标签页分组怎么用_谷歌浏览器整理标签页技巧【效率】  如何在香港免费服务器上快速搭建网站?  js代码实现下拉菜单【推荐】  浅谈Javascript中的Label语句  Laravel如何实现多对多模型关联?(Eloquent教程)  香港服务器网站卡顿?如何解决网络延迟与负载问题?  高防服务器租用首荐平台,企业级优惠套餐快速部署  如何快速搭建高效简练网站?  Laravel如何集成微信支付SDK_Laravel使用yansongda-pay实现扫码支付【实战】