微服务鉴权_就很棒!这样的java微服务下的用户鉴权方案,很值得一看

Java下常用的安全框架主要有Spring Security和shiro,都可提供非常强大的功能,但学习成本较高。在微服务下鉴权多多少少都会对服务有一定的入侵性。 为了降低依赖,减少入侵,让鉴权功能相对应用服务透明,我们采用网关拦截资源请求的方式进行鉴权。

一、整体架构

df59edd4d12dd325b5d686db41c8cd2a.png

用户鉴权模块位于API GateWay服务中,所有的API资源请求都需要从此通过。

  1. 身份认证,通过则缓存用户权限数据,不通过返回401
  2. 用户鉴权,比对当前访问资源(URI和Method)是否在已缓存的用户权限数据中,在则转发请求给对应应用服务,不在则返回403

二、实现步骤

dfcc0d708eed35f2d65a25ca70217e21.png

1. 用户登陆

public LoginUser login(String userName, String password){    // 检查密码    User user = userService.checkUser(userName, password);    LoginUser loginUser = LoginUser.builder()            .userName(userName)            .realName(user.getRealName())            .userToken(UUID.randomUUID().toString())            .loginTime(new Date())            .build();    // 保存session    session.saveSession(loginUser);    // 查询权限    List permissions = permissionRepository.findByUserName(userName);    // 保存用户权限到缓存中    session.saveUserPermissions(userName, permissions);    return loginUser;}// ...// 缓存用户权限到Redispublic void saveUserPermissions(String userName, List permissions) {    String key = String.format("login:permission:%s", userName);    HashOperations hashOperations = redisTemplate.opsForHash();    hashOperations.putAll(key, permissions.stream().collect(            Collectors.toMap(p -> p.getMethod().concat(":").concat(p.getUri()),                    Permission::getName, (k1, k2) -> k2)));    if (expireTime != null) {        redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);    }}
  • 用户验证通过后,下发userToken,保存当前登陆信息,缓存用户授权列表
  • 缓存授权列表时,为了方便读取使用hash方式保存为list,切勿直接将数组对象保存为一个object

2. 拦截请求

@Slf4j@Componentpublic class AuthorizationFilter extends AbstractGatewayFilterFactory {    @Autowired    private Session session;    @Override    public GatewayFilter apply(Object config) {        return (exchange, chain) -> {            ServerHttpRequest request = exchange.getRequest();            ServerHttpResponse response = exchange.getResponse();            String uri = request.getURI().getPath();            String method = request.getMethodValue();            // 1.从AuthenticationFilter中获取userName            String key = "X-User-Name";            if (!request.getHeaders().containsKey(key)) {                response.setStatusCode(HttpStatus.FORBIDDEN);                return response.setComplete();            }            String userName = Objects.requireNonNull(request.getHeaders().get(key)).get(0);            // 2.验证权限            if (!session.checkPermissions(userName, uri, method)) {                log.info("用户:{}, 没有权限", userName);                response.setStatusCode(HttpStatus.FORBIDDEN);                return response.setComplete();            }            return chain.filter(exchange);        };    }}
  • 第一步从取出身份认证模块传递的X-User-Name
  • 第二步去缓存中检查是否有相应的权限
public boolean checkPermissions(String userName, String uri, String method) {    String key = String.format("login:permission:%s", userName);    String hashKey = String.format("%s:%s", method, uri);    if (redisTemplate.opsForHash().hasKey(key, hashKey)){        return  true;    }    String allKey = "login:permission:all";    // 权限列表中没有则通过    return !redisTemplate.opsForHash().hasKey(allKey, hashKey);}
  • 权限列表中没有则通过 主要是放过一些没有必要配置的公共资源,默认都可以访问的资源
  • login:permission:all 所有配置过的权限列表需要在程序启动时放入缓存,并需要保持数据的更新

3. 鉴权Filter配置

spring:  cloud:    gateway:      routes:        - id: cloud-user          uri: lb://cloud-user  # 后端服务名          predicates:            - Path=/user/**   # 路由地址          filters:            - name: AuthenticationFilter  # 身份认证            - name: AuthorizationFilter   # 用户鉴权            - StripPrefix=1 # 去掉前缀
  • 特别注意filter的顺序,必须先做身份认证后再进行鉴权
  • 如果有较多的路由都需要配置,可使用default-filters默认Filter配置

三、其它问题

在做单元测试时,如遇到如下错误

nested exception is java.lang.NoClassDefFoundError: javax/validation/ValidationException

请升级依赖包版本:

org.hibernate.validator    hibernate-validator    6.0.5.Finaljavax.validation    validation-api    2.0.1.Final

四、完整代码

https://gitee.com/hypier/barry-cloud


作者:barry的异想世界
链接:https://juejin.im/post/6865580470151675918
来源:掘金

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值