前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >说说SpringBoot以及微服务中的几种鉴权方式

说说SpringBoot以及微服务中的几种鉴权方式

原创
作者头像
Karos
发布2023-12-15 11:39:56
4740
发布2023-12-15 11:39:56
举报
文章被收录于专栏:MyBlog-KarosMyBlog-Karos

AOP鉴权 或 拦截器/过滤器鉴权

这个方法是利用Spring-AOP的机制,进行鉴权,可以使用execution进行鉴权

也可以使用@annotion进行鉴权,方式很多

这里再来说说网关鉴权

GateWay鉴权

我们可以在网关层面进行鉴权,在这个时候防止流量下放

在最开始的时候接口进入鉴权白名单,我们可以直接放行,在各个服务进行鉴权

代码语言:javascript
复制
 @Around("@annotation(authCheck)")
    public Object doInterceptor(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable {
        // 获取管理信息
        String mustRole = authCheck.mustRole();
        List<String> anyRole = Arrays.stream(authCheck.anyRole())
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toList());
        Boolean checkLogin = authCheck.checkLogin();
        // 获取请求信息
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        // 获取路由信息
        String method = request.getMethod().toUpperCase(Locale.ROOT);
        String requestURI = request.getRequestURI();
        String contextPath = request.getContextPath();
        // 判断在gateway上是否鉴权
        String authed = request.getHeader("Authed");
        if (!"gateway".equals(authed)) {
            log.info("\n新注册到认证中心的接口,路由信息如下:\n" +
                    "method: [{}]\n" +
                    "requestURI: [{}]\n" +
                    "contextPath:  [{}]",method,requestURI,contextPath);
            // 通过rpc获取 认证中心 的 route,曾经是否注册过
                Auth one = authService.getOne(method, requestURI, contextPath);
            // 如果是新增的路由,那么就加上
                if (ObjectUtils.isEmpty(one)) {
                    one = new Auth()
                            .setMethod(method)
                            .setUri(requestURI)
                            .setRoute(contextPath)
                            .setIsDef(true)
                            .setOpen(true);
                }
            // 必须有该权限才通过
                if (StringUtils.isNotBlank(mustRole)) {
                    if (!anyRole.contains(mustRole)) {
                        anyRole.add(mustRole);
                    }
                }
                one.setOpen(true).setIsDef(true).setAuthRoles(anyRole).setCheckLogin(checkLogin).setIsDef(true);    //设置成真值,一样的交给网关执行
                boolean state = authService.saveOrUpdate(one);
            log.info("认证中心保存更新状态state: {}",state);
            // 网关没有鉴权,那么在这里鉴权
            log.info("AOP鉴权开始");
            String token = request.getHeader(CommonConstant.TOKEN_HEADER);
            User userFromToken = AuthUtil.getUserFromToken(token);
            if (ObjectUtils.isEmpty(userFromToken)) {
                throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "未登录或者状态过期,请能够重新登录!");
            }
            if (checkLogin==false&&!anyRole.contains(userFromToken.getUserRole())||UserConstant.BAN_ROLE.equals(userFromToken.getUserRole())) {
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
            }
        }
        else{
            //如果已经鉴权,通过流量染色
            log.info("已在gateway进行鉴权,即将进入业务层,路由信息如下\n" +
                    "method: [{}]\n" +
                    "requestURI: [{}]\n" +
                    "contextPath:  [{}]",method,requestURI,contextPath);
        }
        // 进行响应日志记录
        return handelResponse(joinPoint);
    }

鉴权之前,我们通过RPC注册到鉴权中心,后续通过gateway的时候先流量脱色,再鉴权,最后再染色

代码语言:javascript
复制
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 请求
        ServerHttpRequest request = exchange.getRequest();
        // 响应
        ServerHttpResponse response = exchange.getResponse();
        // 获取路由路径
        RequestPath path = request.getPath();
        log.info("forward path:{}",path);
        // 这里是一个重要的鉴权逻辑,防止伪造染色
        exchange=exchange.mutate().request(request.mutate().headers(httpHeaders -> httpHeaders.remove("Authed")  ).build()).build();
        String requestMethod = request.getMethod().name();
        // 如果不是get请求,那么进行重放检测
        if(!HttpMethod.GET.matches(requestMethod)){
            Mono<Void> voidMono = doReplayDetection(exchange);
            if (ObjectUtils.isNotEmpty(voidMono)){
                return voidMono;
            }
        }
        request.mutate().headers(headers -> headers.add("x-forwarded-for",request.getRemoteAddress().getAddress().getHostAddress()));
        //通过 全局鉴权容器进行遍历、匹配
        for(GlobalContainer.Route v:GlobalContainer.authRouteList){
            String method = v.getMethod();
            String url = v.getUrl();
            //当 当前转发路由、方法 和 需要鉴权的路由方法一直时,进行鉴权
            log.info("url:{} path:{}",url,path);
            log.info("method:{} name:{}",method,requestMethod);
            if (url.equals(path.value())&&method.equals(requestMethod)){
                // 进行鉴权操作
                // 通过Token获取用户信息
                User currentUser = AuthUtil.getUserFromToken(exchange.getRequest().getHeaders().getFirst("Authorization"));
                if (ObjectUtils.isEmpty(currentUser)){
                    return handleOther(null,
                            ResultUtils.error(ErrorCode.FORBIDDEN_ERROR,"Token无效,请重新登录"),
                            response);
                }
                // 是否只需要检查登录状态
                Boolean checkLogin = v.getCheckLogin();
                log.info("v.getCheckLogin():{}", checkLogin);
                if (BooleanUtils.isTrue(checkLogin)){
                    return handleResponse(exchange, chain);
                }
                // 获取用户权限信息
                String userRole = currentUser.getUserRole();
                // 被BAN了的用户
                if (UserConstant.BAN_ROLE.equals(userRole)){
                    return handleNoAuth(response);
                }
                log.info("userRole:{} roles:{} user:{}",userRole,v.getRole(),currentUser);
                boolean contains = v.getRole().contains(userRole);
                if (!contains){
                   return handleNoAuth(response);
                }
                // 鉴权完毕,修改请求头
                ServerHttpRequest build = request.mutate().headers(header-> header.add("Authed","gateway")).build();
                exchange=exchange.mutate().request(build).build();
                break;
            }
        }
        // 交给响应事件
        return handleResponse(exchange, chain);
    }
​

继承HandlerMethodArgumentResolver鉴权

代码语言:javascript
复制
public interface HandlerMethodArgumentResolver {
    boolean supportsParameter(MethodParameter var1);
​
    @Nullable
    Object resolveArgument(MethodParameter var1, @Nullable ModelAndViewContainer var2, NativeWebRequest var3, @Nullable WebDataBinderFactory var4) throws Exception;
}

这个类的作用主要是用于方法拦截

第一个supportsParmeter是拦截条件

第二个resolveArgument是执行的计划,满足supportsParmeter的话就执行resolveArgument,执行完后我们会把返回值给到满足条件的参数上面

然后我们对编写的类进行一个注册

代码语言:javascript
复制
@Configuration
public class WebConfig  extends WebMvcConfigurerAdapter{
    @Autowired
    类 对象;
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(对象);
    }
}

所以说,可玩性很高,我们可以判断一个注解是否在某个参数上,而那个参数是用户类型,然后我们可以在resolveArgument中进行鉴权,并且将鉴权后的结果(.e.g: 登录用户对象 等)返回给参数

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • AOP鉴权 或 拦截器/过滤器鉴权
  • GateWay鉴权
  • 继承HandlerMethodArgumentResolver鉴权
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档