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

背景描述

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

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

(0)

相关推荐

  • 权限控制之粗粒度与细粒度概念及实现简单介绍

    本文简单介绍了权限控制之粗粒度与细粒度概念及实现,下面看看具体内容. 1.   什么是粗粒度和细粒度权限 粗粒度权限管理,对资源类型的权限管理.资源类型比如:菜单.url连接.用户添加页面.用户信息.类方法.页面中按钮.. 粗粒度权限管理比如:超级管理员可以访问户添加页面.用户信息等全部页面. 部门管理员可以访问用户信息页面包括 页面中所有按钮. 细粒度权限管理,对资源实例的权限管理.资源实例就资源类型的具体化,比如:用户id为001的修改连接,1110班的用户信息.行政部的员工. 细粒度权限管

  • spring boot 1.5.4 集成shiro+cas,实现单点登录和权限控制

    1.添加maven依赖(先安装好cas-server-3.5.2,安装步骤请查看本文参考文章) <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.4</version> </dependency> <dependency> <groupId>

  • angular基于路由控制ui-router实现系统权限控制

    前端去实现权限控制听起来有点扯淡(实际也有点扯淡),掩耳盗铃,主要是担心安全问题,但是如果在前后端分离的情况下,需要做一个带有权限控制的后台管理系统,angular基于ui-router应该怎么做呢? 权限的设计中比较常见的就是RBAC基于角色的访问控制,基本思想是,对系统操作的各种权限不是直接授予具体的用户,而是在用户集合与权限集合之间建立一个角色集合.每一种角色对应一组相应的权限. 一旦用户被分配了适当的角色后,该用户就拥有此角色的所有操作权限.这样做的好处是,不必在每次创建用户时都进行分配

  • Angular2使用Guard和Resolve进行验证和权限控制

    我们在开发web应用时,在服务器端都会控制某种或某个用户是否有权限调用某个接口.在前端,我们除了根据用户的角色或其他特性来控制一些页面元素是否显示以外,也需要控制用户是否能够进入某些页面(例如通过直接输入URL的方式直接进入).要控制是否显示,我们可以使用 *ngIf . [hidden] 等方式.而对于控制用户能否进入某个页面,Angular2的路由框架也提供了非常方便的方式来实现这个功能. Angular2提供了2种组件, Guard 和 Resolve . Guard 顾名思义就是用来保护

  • Angular中使用ui router实现系统权限控制及开发遇到问题

    前端去实现权限控制听起来有点扯淡(实际也有点扯淡),掩耳盗铃,主要是担心安全问题,但是如果在前后端分离的情况下,需要做一个带有权限控制的后台管理系统,angular基于ui-router应该怎么做呢? 权限的设计中比较常见的就是RBAC基于角色的访问控制,基本思想是,对系统操作的各种权限不是直接授予具体的用户,而是在用户集合与权限集合之间建立一个角色集合.每一种角色对应一组相应的权限. 一旦用户被分配了适当的角色后,该用户就拥有此角色的所有操作权限.这样做的好处是,不必在每次创建用户时都进行分配

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

    背景描述 项目中需要做细粒的权限控制,细微至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_privi

  • Java中的Spring Security配置过滤器

    目录 AbstractAuthenticationFilterConfigurer 改造过程 配置类效果 前言: CaptchaAuthenticationFilter是通过模仿UsernamePasswordAuthenticationFilter实现的.同样的道理,由于UsernamePasswordAuthenticationFilter的配置是由FormLoginConfigurer来完成的,应该也能模仿一下FormLoginConfigurer,写一个配置类CaptchaAuthent

  • 详解Spring Security 中的四种权限控制方式

    Spring Security 中对于权限控制默认已经提供了很多了,但是,一个优秀的框架必须具备良好的扩展性,恰好,Spring Security 的扩展性就非常棒,我们既可以使用 Spring Security 提供的方式做授权,也可以自定义授权逻辑.一句话,你想怎么玩都可以! 今天松哥来和大家介绍一下 Spring Security 中四种常见的权限控制方式. 表达式控制 URL 路径权限 表达式控制方法权限 使用过滤注解 动态权限 四种方式,我们分别来看.  1.表达式控制 URL 路径权

  • 自定义Spring Security的身份验证失败处理方法

    1.概述 在本快速教程中,我们将演示如何在Spring Boot应用程序中自定义Spring Security的身份验证失败处理.目标是使用表单登录方法对用户进行身份验证. 2.认证和授权(Authentication and Authorization) 身份验证和授权通常结合使用,因为它们在授予系统访问权限时起着重要且同样重要的作用. 但是,它们具有不同的含义,并在验证请求时应用不同的约束: 身份验证 - 在授权之前;它是关于验证收到的凭证;我们验证用户名和密码是否与我们的应用程序识别的用户

  • Spring Security权限管理实现接口动态权限控制

    SpringBoot实战电商项目mall(30k+star)地址:https://github.com/macrozheng/mall 摘要 权限控管理作为后台管理系统中必要的功能,mall项目中结合Spring Security实现了基于路径的动态权限控制,可以对后台接口访问进行细粒度的控制,今天我们来讲下它的后端实现原理. 前置知识 学习本文需要一些Spring Security的知识,对Spring Security不太了解的朋友可以看下以下文章. mall整合SpringSecurity

  • Springboot+Spring Security实现前后端分离登录认证及权限控制的示例代码

    目录 前言 本文主要的功能 一.准备工作 1.统一错误码枚举 2.统一json返回体 3.返回体构造工具 4.pom 5.配置文件 二.数据库表设计 初始化表数据语句 三.Spring Security核心配置:WebSecurityConfig 四.用户登录认证逻辑:UserDetailsService 1.创建自定义UserDetailsService 2.准备service和dao层方法 五.用户密码加密 六.屏蔽Spring Security默认重定向登录页面以实现前后端分离功能 1.实

  • Spring Security实现统一登录与权限控制的示例代码

    目录 项目介绍 统一认证中心 配置授权服务器 配置WebSecurity 登录 菜单 鉴权 资源访问的一些配置 有用的文档 项目介绍 最开始是一个单体应用,所有功能模块都写在一个项目里,后来觉得项目越来越大,于是决定把一些功能拆分出去,形成一个一个独立的微服务,于是就有个问题了,登录.退出.权限控制这些东西怎么办呢?总不能每个服务都复制一套吧,最好的方式是将认证与鉴权也单独抽离出来作为公共的服务,业务系统只专心做业务接口开发即可,完全不用理会权限这些与之不相关的东西了.于是,便有了下面的架构图:

  • Spring Security权限管理小结

    目录 1 Spring Security配置用户名和密码 方式一:在application.properties文件中配置 方式二:代码配置 一.查询user用户所具有的角色 二.配置SecurityConfig 2 HttpSecurity的配置 3 登录/注销表单详细配置 4 多个HttpSecurity的配置 5 密码加密 6 方法安全 7 基于数据库的认证 8 角色继承(在securityConfig中加入代码段) 9 动态配置权限 1 Spring Security配置用户名和密码 方

  • Spring Boot中使用 Spring Security 构建权限系统的示例代码

    Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作. 权限控制是非常常见的功能,在各种后台管理里权限控制更是重中之重.在Spring Boot中使用 Spring Security 构建权限系统是非常轻松和简单的.下面我们就来快速入门 Spring Security .在开始前我们需要一对

  • Spring Boot中整合Spring Security并自定义验证代码实例

    最终效果 1.实现页面访问权限限制 2.用户角色区分,并按照角色区分页面权限 3.实现在数据库中存储用户信息以及角色信息 4.自定义验证代码 效果如下: 1.免验证页面 2.登陆页面 在用户未登录时,访问任意有权限要求的页面都会自动跳转到登陆页面. 3.需登陆才能查看的页面 用户登陆后,可以正常访问页面资源,同时可以正确显示用户登录名: 4.用户有角色区分,可以指定部分页面只允许有相应用户角色的人使用 4.1.只有ADMIN觉得用户才能查看的页面(权限不足) 4.2.只有ADMIN觉得用户才能查

随机推荐