V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zhaoxixiangban
V2EX  ›  程序员

spring-security,有没有对需要权限控制的 url 存入数据库的实现。

  •  1
     
  •   zhaoxixiangban · 2020-06-19 10:45:24 +08:00 · 3783 次点击
    这是一个创建于 1613 天前的主题,其中的信息可能已经有所发展或是发生改变。
    29 条回复    2020-06-21 08:23:44 +08:00
    admin7785
        1
    admin7785  
       2020-06-19 11:23:44 +08:00 via iPhone
    我用了 rabbitmq,获取方法的接口信息 然后拼接存入数据库
    jorneyr
        2
    jorneyr  
       2020-06-19 11:29:05 +08:00
    考虑过这个问题,基于链接的授权:
    1. Spring Security 中设置所有 (某些) 链接都需要角色 USER 才能访问
    2. AuthenticationFilter 获取用户登录信息时 (基于 token) 查询用户是否可以访问此链接,如果可以设置角色为 USER 使得其有权访问,否则设置为没有权限的用户角色如 NO 即可
    siweipancc
        3
    siweipancc  
       2020-06-19 12:18:51 +08:00 via iPhone
    你这个,可以用 PreAuth 注解,在 el 引用一个 验证 bean 对现有凭据进行拦截。我待会写一个 demo 看看。
    skypyb
        4
    skypyb  
       2020-06-19 12:49:10 +08:00 via Android
    。。。不就是接口级权限么,网上实现应该很多啊。spring security 里有授权管理器,自己重写一遍覆盖掉默认的就行了
    hantsy
        5
    hantsy  
       2020-06-19 13:24:13 +08:00
    实现不难,但是完全没必要。国内很多需求都是扯蛋的,一般项目几种 Role 就基本可以了。

    以前一个项目,客户要求实现自己 一套颗粒度很细的权限管理,自己可以编辑配置。实现过程要花点时间,难度不大,全部( API 的 URLPattern,HTTPMETHOD,一条不同操作对应一条授权 Permission )保存到数据库,用户页面可以打开授权,整页面都是 CheckBox 的 Permissions,客户看到那个后自己都晕了,感觉自己蠢了。
    ourslay
        6
    ourslay  
       2020-06-19 13:35:41 +08:00
    https://docs.spring.io/spring-security/site/docs/5.4.0-M1/reference/html5/#el-access-web-beans
    `
    public class WebSecurity {
    public boolean check(Authentication authentication, HttpServletRequest request) {
    ...
    }
    }
    You could refer to the method using:

    <http>
    <intercept-url pattern="/user/**"
    access="@webSecurity.check(authentication,request)"/>
    ...
    </http>
    or in Java configuration

    http
    .authorizeRequests(authorize -> authorize
    .antMatchers("/user/**").access("@webSecurity.check(authentication,request)")
    ...
    )
    `
    Reactive 应用就更简单
    `
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
    .authorizeExchange()
    .pathMatchers("/api/**").access(customerAccessCheck)
    ...
    return http.build();
    }
    `
    siweipancc
        7
    siweipancc  
       2020-06-19 13:42:57 +08:00
    // ---------model
    @Entity
    @Data
    @Accessors(chain = true)
    public class AuthUrl implements Serializable {
    @Id
    @GeneratedValue
    private Long id;

    private String url;

    @ElementCollection
    private List<Long> uIds;
    }

    // ----- 控制层 @PreAuthorize("@userProfileManager.currentUserAuthForUlr(authentication,httpServletRequest.requestURL.toString())")
    @PutMapping("password")
    public ResponseEntity<String> changePassword(@RequestParam String newPassword, Authentication authentication) {
    profileManager.changePassword(newPassword, authentication);
    return ResponseEntity.ok("success");
    }

    // -------- service
    public class UserProfileManager {
    // 各种注入
    public boolean currentUserAuthForUlr(Authentication authentication, String url) {
    Optional<UserProfile> optional = userProfileMapper.findOne(Example.of(new UserProfile().setEmail(authentication.getPrincipal().toString())));
    UserProfile profile = optional.orElseThrow(() -> new BadCredentialsException("请重新登录"));
    AuthUrl authUrl = authUrlMapper.findOne(Example.of(new AuthUrl().setUrl(url))).orElse(null);
    if (authUrl == null) {
    return true;
    }
    List<Long> uIds = authUrl.getUIds();
    return uIds == null || uIds.isEmpty() || uIds.contains(profile.getId());
    }


    public void changePassword(String newPassword, Authentication authentication) {
    Optional<UserProfile> optional = userProfileMapper.findOne(Example.of(new UserProfile().setEmail(authentication.getPrincipal().toString())));
    UserProfile profile = optional.orElseThrow(() -> new BadCredentialsException("请重新登录"));
    String encode = passwordEncoder.encode(newPassword);
    profile.setPassword(encode);
    userProfileMapper.saveAndFlush(profile);
    }
    // **
    }
    tctc4869
        8
    tctc4869  
       2020-06-19 15:47:56 +08:00
    @hantsy 你的权限系统的思路,能说明一下么?
    zhaoxixiangban
        9
    zhaoxixiangban  
    OP
       2020-06-19 15:59:13 +08:00
    @skypyb #4 需要前端进行自定义配置角色权限的,类似五楼的需求。- -· 汗 ,所以需要把可配置的权限都给到前端
    Vegetable
        10
    Vegetable  
       2020-06-19 16:01:03 +08:00
    目测 xy problem
    zhaoxixiangban
        11
    zhaoxixiangban  
    OP
       2020-06-19 16:02:06 +08:00
    @siweipancc #7 嗯 现在后台权限这边问题不大,像上面五楼说的类似,需要用户对细粒度的权限进行自定义配置,就需要我们把相应的接口数据都存起来给前端,接口列表数据这部分都是怎么实现的呢?手动加数据库?
    zhaoxixiangban
        12
    zhaoxixiangban  
    OP
       2020-06-19 16:04:36 +08:00
    @hantsy #5 对就是这种需求 ,操作接口是自己手动加数据库吗
    zhaoxixiangban
        13
    zhaoxixiangban  
    OP
       2020-06-19 16:05:39 +08:00
    @jorneyr #2
    @ourslay #6
    抱歉是我没有说清楚 ,问题点是五楼阐述的这种状况。
    zhaoxixiangban
        14
    zhaoxixiangban  
    OP
       2020-06-19 16:06:29 +08:00
    @admin7785 #1 具体怎么个搞法
    siweipancc
        15
    siweipancc  
       2020-06-19 18:20:35 +08:00 via iPhone
    @zhaoxixiangban 我建议你不要这么玩,最后双头坑,角色或者组跟模块绑定的设计才能继续维护。
    daimubai
        16
    daimubai  
       2020-06-19 22:11:38 +08:00
    手动加数据库,然后加载权限信息到用户,最后配置全局权限控制;
    zhenjiachen
        17
    zhenjiachen  
       2020-06-20 08:31:29 +08:00 via iPhone
    看一下我的实现,可能有 bug,不过可以借鉴一下。https://github.com/chenzhenjia/niubi-commons/blob/master/README.md
    hantsy
        18
    hantsy  
       2020-06-20 10:59:21 +08:00
    @ourslay URL 没有动态配置。
    @siweipancc 很好的利用 Spring Security 更简单。
    @zhaoxixiangban @tctc4869
    思路:
    1, 权限定义用一个表( JPA Entity ),类似结构:
    name 唯一,另外( urlPattern 与 httpMethod )组合唯一。

    Permission{
    name//唯一,比如 PERM_GET_ALL_POSTS
    urlPattern//比如:/posts/*
    httpMethod// 比如:GET, 可用 enum 或直接用 Spring web HttpMethod. 常用的有 GET,POST,PUT,DELETE,PATCH
    longDescription//其它辅助说明(用于页面补充)
    }

    那么 user 与 Permission 权限关系:
    user->permission 1:n

    2. Repository 类,PermissionRepository
    3. Spring Security 中直接使用 Apply 。

    allPermissions= permissionRepository.findAll(Sort.by(...))

    http
    .authorizeRequests(authorize ->
    allPermissions.forEach(perm->{
    authorize
    .antMatchers(per.urlPattern, per.httpMethod).hasAuthority(perm.name)//这里查 Spring Security 文档,我记得以前不用 AntMatcher,而使用 RegexMatcher,表达比 ANT 方式更丰富。
    })

    4. 配置一个超级管理员,绕过所有权限(不读数据库),可以维护系统的 permission 列表。
    默认给定一个管理员,配置所有权限。管理员可以用管理用户,可以管理用户权限(页面可以是 Checkbox,或者两列选择,等),最终影响 user -> permission 关系表。
    所有新注册用户默认应该协商好,应该给那些权限,可以设计一些 DUMMY 权限比如 READ,那么在 persmission 表所有 httpmethod GET 权限就拿到了( UserDetailsService 中转换成实际 permissions 的 Authories )。
    5. (扩展) 所有权限的初始数据可以用 Reflection 生成,在系统启动时初始化(添加,更新)。
    6. (扩展) 可以添加 Role,role->Permission 可以是一对多的关系, 相当于权限分组了。在 UserDetailsService 的 findByusername 将所有的 Role 转换成 Permission 添加到 authories 中(因为上面的 Spring Security 中只配置比较 Permission )。这样的话,数据库关系变成 user->role>permission 1:n,1:n 。
    7. (扩展) 所有前端的权限可以登录时拿到一个个人的允许的 permission 列表,可以用来辅助前端的页面控制。比如没有授权 PERM_GET_ALL_POSTS 的时候,可以从页面把顶级菜单上的 posts 去掉了。API 资源本身就是可以归类的,我从来没用过 V 站那些资源共享版本中贴的那些什么功能模块与权限配置表。

    我遵循的一个原则,用户细粗度的行为(一个点击,一个操作)在一个应用程序中基本是可以固定下来的。所以 每一个不同操作最终变成一个不同的权限,Permission 在你的项目(应用)上线时候,基本可以是固定下来的,而 Role 完全可以由用户随便添加,随意修改。
    hantsy
        19
    hantsy  
       2020-06-20 11:01:39 +08:00
    user->permission,user->role>permission 应该都是 n:m 多对多。
    hantsy
        20
    hantsy  
       2020-06-20 11:14:14 +08:00
    回到之前的观点:完全没必要这样灵活设计。之前加了 ROLE 之后 ,ROLE A 包含了 perm a, ROLE B 没有包含 perm a 。那么问题,遇到客户一个用户添加了 A,B,它觉得莫名其妙的来了,为什么 ROLE B 没有 perm a,我(用户)却有 perm a 。那么更复杂的问题来了,当一个用户设置了 ROLE, A,B,C,D,那么是不是要用户去决定 A,B,C,D 之间的权限是继承还是覆盖关系???

    这种灵活设计,对用户和开发人员都是作茧自缚,用处不大,技术没含量,很费事。基本过去十几年的项目经验可以肯定的说,基本上都是可以固定的 ROLE 搞定,结果大量的时间去开发所有权限系统。为什么用户会强调 ROLE 要灵活配置,说白了一点,从需求角度来分析,就是它自己需求不确定,不知道要做什么东西。
    zzl22100048
        21
    zzl22100048  
       2020-06-20 11:43:04 +08:00 via iPhone
    看 keycloak 的细粒度权限控制
    hantsy
        22
    hantsy  
       2020-06-20 14:03:06 +08:00
    @zzl22100048 Keycloak 推荐过好多次了。国内没几个人用的,在 V 站看到的人都是觉得自己开发的权限 /安全系统最好的。
    tctc4869
        23
    tctc4869  
       2020-06-20 17:39:10 +08:00
    @hantsy 问一下,ABAC 与 RBAC 两种权限模型,有什么本质区别么?没用过 ABAC 权限模型,我目前接触到的权限体系就是关于 RBAC 的,有博客说 ABAC 的权限配置比 RBAC 更灵活,配置权限也更复杂。到底是怎么样的呢?
    tctc4869
        24
    tctc4869  
       2020-06-20 17:49:54 +08:00
    @hantsy 浏览了很多有关权限系统的说明博客,关于 ABAC 权限体系,如果要在用途归纳起来,我感觉就是基于平台应用级别的权限系统,不同的组织,在这个平台上,可以有不一样的权限体系。至少每个组织对内权限体系,可以不一样。而 RBAC 权限模型做不到这一点,或者说很困难,至少如果有两个客户群体对内有不一样的权限需求,RBAC 实现起来就会很困难。是这样的么?比如做一个面向多企业客户级别的平台应用,那么权限系统采用 ABAC 是最合适的么
    hantsy
        25
    hantsy  
       2020-06-20 20:22:43 +08:00
    这个自己 Google 下吧,https://www.dnsstuff.com/rbac-vs-abac-access-control#rbac-vs-abac
    我只关注过 RBAC,似乎 Java EE 标准体系也只有基于 ROLE (和 Group,同等 ROLE )的设计。
    hantsy
        26
    hantsy  
       2020-06-20 20:36:36 +08:00
    ABAC 看介绍这只是更细一些,而且是另外一个方向。
    RBAC 以 ROLE,行为(权限)为基础的。

    ABAC 更的多关注对一个资源的个别属性的访问控制权限,更细的控制。这个除非是基于传统数据模型的的开发,比如传统的 MIS 之类的系统。我觉得用在 API 的控制不大可能 。传统基于数据的开发,实现也不难。以前的 JBoss 下的 http://picketlink.org/ 应该相应的方案,记得见过了。数据模型,比如 JPA Entity,每个属性用某个 Annotation 标注,全部运行时可以提取出来 ,一样可以存放在数据库或者基于 LDAP 这种层层目录访问的方式 ,然后剩下就是就是在数据 CRUD 操作下控制了。
    hantsy
        27
    hantsy  
       2020-06-20 20:52:28 +08:00
    @tctc4869 很多时候面对客户,只是把有些东西在脑子里面想得太复杂。中国人这种细粒度的控制,说白了也是我们文化的一部分,控制欲太强了。国外角色更多是系统上的分工,国内的讲权限(实际国内客户很少跟谈角色这个词,很多没有角色这个概念)更多的讲操控上的权力,可以授权,可以取消授权。

    其实只要弄清楚,归类出来有几种人可以用这个系统(应用),角色就可以满足。欧美的项目我做很多,有电商,有支付,没有一个要这个种权限配置系统 。有些项目,人家一开始需求就很明确的提出了有几种人用这个系统,操作方式上有应该什么差别。
    tctc4869
        28
    tctc4869  
       2020-06-21 07:47:47 +08:00 via Android
    @hantsy 那我有另一个问题,如果有一个项目,要面向不同类型的客户群体,而且可能每个客户群体内部有不一样的权限需求。这个时候权限系统要怎么配置,若是在 rbac 权限体系下,是进行角色分类上的不同的客户关系扩展,还在重新做针对客户需求的一套应用?还是用 abac 模型体系下设计呢?
    hantsy
        29
    hantsy  
       2020-06-21 08:23:44 +08:00
    业务相关的权限本身就和开公的一些权限标准(规范等)没太大的关系,自己根据业务需求决定了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3534 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 35ms · UTC 04:57 · PVG 12:57 · LAX 20:57 · JFK 23:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.