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

请教下 Spring Cloud GateWay 网关的鉴权和数据权限, 怎么把 token 转换成用户信息 进入其他服务?

  •  
  •   wuzhizuiguo · 358 天前 · 5189 次点击
    这是一个创建于 358 天前的主题,其中的信息可能已经有所发展或是发生改变。

    例如简单判断有没有权限调用某个接口,我在 gateway 里的 filter 判断一下这个 token 存不存在 redis 中(token -> 用户信息).

    假设现在有个接口是 api.test.com/user/mail/list/ 接口, 需要传入 userId, 获取到这个用户的邮件列表, 怎么让 gateway 转发请求到 user-service 时, 将请求 header 里的 token 转化为 userId,

    userController 里的方法可能是这样的 List<mail> mailList(Long userId);</mail>

    除了网关外其他服务 RPC 调用该接口, 也可以只传入 userId 参数 这样的请问该怎么实现?

    如果是以前的方法,我可能会把这个方法改为 List<mail> mailList(Long usrId,HttpServletRequest request);</mail>

    然后取出 request 中的 token,去 redis 中验证一下, 然后去取出 userId, 但是这样 其他 b.c.d..服务要是 fegin RPC 调用 这个方法时, 就也需要传入 HttpServletRequest/或者 token.

    请教下大佬,Spring Cloud GateWay 网关的鉴权和数据权限该怎么做?

    23 条回复    2020-07-31 09:43:11 +08:00
    gaius
        1
    gaius   358 天前 via Android
    网关调鉴权服务完事把用户信息和权限列表写到 header
    359889741
        2
    359889741   358 天前
    filter 里面读出 userId 来,再往业务服务传递不就行了
    wuzhizuiguo
        3
    wuzhizuiguo   358 天前
    @gaius 谢谢. 这样在 user-service 里, 对应的方法怎么获取 header 里的写入的信息, 感觉需要在接收的 controller 方法里,添加上 HttpServletRequest 参数, 这样的话 admin 等其他服务也 rpc 调用这个接口 会不会有影响.
    wuzhizuiguo
        4
    wuzhizuiguo   358 天前
    @359889741 谢谢, 这样感觉可行, 是收经过网关 需要鉴权的的都要 把 token 里的 userId 取出来了. 是指修改请求的参数, 把 userId 放入到 post 的请求 body 里 或者 get 的 param 中吗
    hantsy
        5
    hantsy   358 天前
    也就是说其它里面的 Service 不做安全验证,这样跟裸奔有什么差别。
    hantsy
        6
    hantsy   358 天前
    既然用到 Redis,直接上 Spring Session ( Redis )就好了,Session 里面想丢什么就什么了,一个用户对象也可以。
    hantsy
        7
    hantsy   358 天前
    用 Spring Session+Spring Security 控制安全就可以了,https://github.com/hantsy/spring-microservice-sample
    wuzhizuiguo
        8
    wuzhizuiguo   358 天前
    @hantsy 这个我看网上是这样的, 其他的 service 不对外开放(放到内网 或者防火墙关了访问端口), 即经过网关校验后 各个服务之间是可以信任的.
    hantsy
        9
    hantsy   358 天前
    不知道这种思维方式哪里来的。。。
    你网关是会对外吧,如果别人能够进入你网关呢?不一样可以再进入里面的服务。
    wuzhizuiguo
        10
    wuzhizuiguo   358 天前
    @hantsy 是的.. 网关是对外的,如果网关通过了, 那按照我这种,确实是可以按照它的接口权限 访问对应的接口 /服务. 有博客也提到了 spring security . 谢谢, 我也去了解学习下
    yang957862615
        11
    yang957862615   358 天前   ❤️ 1
    实现 GlobalFilter 接口之后使用 ServerWebExchange.getRequest().mutate().header(String headerName, String... headerValues)这个方法就行
    wuzhizuiguo
        12
    wuzhizuiguo   358 天前
    @yang957862615 谢谢. 那再下个服务里怎么获取到这个 header 里的值呢? 感觉方法里还是需要添加 HttpServletRequest 参数...
    yang957862615
        13
    yang957862615   358 天前
    @wuzhizuiguo springboot 可以在类里面注入 HttpServletRequest 实例就可以不用在方法上加这个参数
    humpy
        14
    humpy   358 天前
    用拿 header 的注解就行了

    @RequestHeader("X-USER-ID") String userId
    caotian
        15
    caotian   358 天前
    如果 JWT 可以满足要求, 不需要用 redis 来存 token, gateway 可以只验证 jwt 是否有效, 然后取出 jwt 里的 userId, 转发请求的时候, 再把 userId 放到 header 里
    wuzhizuiguo
        16
    wuzhizuiguo   358 天前
    wuzhizuiguo
        17
    wuzhizuiguo   358 天前
    @humpy 还可以这样.. 第一次看到 谢谢.
    wuzhizuiguo
        18
    wuzhizuiguo   358 天前
    @caotian 谢谢. 是的, jwt token 也可以.
    rykinia
        19
    rykinia   358 天前
    @hantsy gateway 设置规则,让对外的接口为 /api/**。比如实际的接口是 /user,那么外网调用的 URI 则应该是 /api/user,然后 gateway 转发,这样外网应该没法直接通过 /user 访问吧
    wuzhizuiguo
        20
    wuzhizuiguo   358 天前
    @humpy @RequestHeader("X-USER-ID") String userId 这个单纯请求是可以的. 但是其他服务 rpc 调用这个接口怎么办?
    @PostMapping(value = "/list")
    public Result list(@RequestHeader(value = "X-USER-ID", required = false) Long userId);
    RPC 方法:
    @PostMapping(value = "/user/mail/list")
    public Result mailList(@RequestParam("userId") Long userId);

    rcp 调用方法里里面的 userId,没有成功传入到 user-service,因为没有 header
    wuzhizuiguo
        21
    wuzhizuiguo   357 天前
    @yang957862615 这个方法 重写请求头 header 的时候, 需要重新 build 一下,不能直接取出来写入. http://lhyd.top/?p=312761
    //向 headers 中放文件,记得 build
    ServerHttpRequest host = exchange.getRequest().mutate().header("a", "888").build();
    //将现在的 request 变成 change 对象
    ServerWebExchange build = exchange.mutate().request(host).build();
    return chain.filter(build);
    wuzhizuiguo
        22
    wuzhizuiguo   349 天前
    头晕. 暂时放弃了.
    选用 将 token(存 redis) 或者 JWT token , 在 tokenFilter(继承 GlobalFilter)中放入 header 之中(jwt 参照 https://blog.csdn.net/CrazyLai1996/article/details/86430457)

    然后取本人的数据信息,则使用 @RequestHeader("X-USER-ID") String userId 这种形式去取数据,

    那么如果权限高一点的(例如管理员), 那么 RPC 调用其他用户的邮件列表, 可以使用如下方法

    用户访问自己的:
    @PostMapping("/mail/list")
    public Result x(@RequestHeader(value = "auth-userId",required = false)Long authUserId){
    return mailService.list(authUserId);
    }

    用户访问别人的
    @PostMapping("/mail/list/{userId}")
    public Result maillist(@PathVariable("userId") Long userId){
    return mailService.list(userId);
    }

    RPC 方法
    @PostMapping("/user/test/mail/list/{userId}")
    public Result mm(@PathVariable("userId")Long userId);

    这个 RPC 方法能不能调用, 还是需要 更上面一层的 token 来区分的, 例如传的 userId 能不能不是他本人的, 需要配合 role /permission, 来决定.

    即: 这种可能是, /admin/user/list , 然后可以传入 userId 参数, 加上自己的 header 中的 token,来决定 给 rpc 方法中传递的 userId 到底是直接的,还是别人的.

    在这个问题中,我最大的困扰是, 怎么将 token 背后的用户信息良好的传递到各个服务中去,
    我觉得比较麻烦,但是可行的是, 每个方法里都加个 token 或者 httpservletrequest, 每次需要的时候 都去根据 header/ token (再根据 redis 其他的转换成信息), 但是想想每个 controller service rpc 方法都要带上 token 或者其他的 那也太恐怖了, 不过还有一点.. 如果一个 controller 方法 含有 Httpservletrequest, 那么它的 rpc 方法大概也得带着这么个参数, 那会一直循环循环吗?

    还有就是, 各个服务全关了外网端口, 所有请求都要经过除了网关外的 另一个特定服务, 例如 admin,在这个地方进行鉴权, 查看有没有对应的权限 /数据权限,

    还是经验不足啊...
    yema50
        23
    yema50   217 天前
    @yang957862615 可以的
    关于   ·   帮助文档   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   4512 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 06:36 · PVG 14:36 · LAX 22:36 · JFK 01:36
    ♥ Do have faith in what you're doing.