No6-4.从零搭建spring-cloud-alibaba微服务框架,解决微服务间的不鉴权调用等(四,no6-4)

   代码地址与接口看总目录:【学习笔记】记录冷冷-pig项目的学习过程,大概包括Authorization Server、springcloud、Mybatis Plus~~~_清晨敲代码的博客-CSDN博客

之前只零碎的学习过spring-cloud-alibaba,并没有全面了解过,这次学习pig框架时,想着可以根据这个项目学习一下,练练手,于是断断续续的用了几天时间搭建了一下基础框架。目前就先重点记录一下遇到的问题吧,毕竟流程也不是特别复杂,就是有的东西没遇到过了解的也不深~

由于微服务包括认证这里内容太多,所以分了好几篇~

第一篇文章:No6.从零搭建spring-cloud-alibaba微服务框架,实现fegin、gateway、springevent等(一)_清晨敲代码的博客-CSDN博客

文章包括:

1.将服务系统注册到nacos注册中心;

2.通过nacos实现配置动态更新;

3.添加fegin服务,实现服务之间调用;

4.添加网关(学会使用webflux,学会添加过滤器);

5.添加log服务,通过springevent实现,并使用注解使用(使用AOP);

第二篇文章:

No6.从零搭建spring-cloud-alibaba微服务框架,实现数据库调用、用户认证与授权等(二,no6-2)_清晨敲代码的博客-CSDN博客

文章包括:

6.添加 mysql 数据库调用,并使用mybatis-plus操作;

7.在认证模块添加用户认证,基于oauth2的自定义密码模式(已认证用户是基于自定义token加redis持久化,不是session);

第三篇文章:

No6-3.从零搭建spring-cloud-alibaba微服务框架,实现资源端用户认证与授权等(三,no6-3)

8.在资源端模块添加用户认证,基于授权端的认证token添加逻辑(但是没有处理微服务间的不鉴权调用,微服务间的调用接口都是白名单呢!);

本篇内容包括:

9.解决微服务间的不鉴权调用(可修改外部访问鉴权逻辑~)

剩余包括(会有变动):

10.添加用户鉴权逻辑(基于RBAC逻辑,使用springsecuiry安全注解实现)

目录

A9.解决微服务间的不鉴权调用(可修改外部访问鉴权逻辑~)

步骤:

测试:


A9.解决微服务间的不鉴权调用(可修改外部访问鉴权逻辑~)

微服务间调用问题,和 pig 项目里面的实现逻辑,在另一篇文里面总结了,就不复述了,并且修改外部访问鉴权逻辑,也在这篇文章里面可以看:【pig-cloud项目】关于@Inner和@PreAuthorize的理解,以及微服务内外部间的调用认证鉴权理解

步骤:

1.添加 @Inner ;

2.修改 PermitAllUrlProperties ,再创建bean之前,记得包被@Inner注解的方法或类加进白名单中;

3.添加 PigSecurityInnerAspect ,添加微服务内部调用的 header(From) 值;并且添加到 /META-INF/string.factoies 中;

4.在 pig-gateway 模块添加PigRequestGlobalFilter全局过滤器,将请求中的 header(From) 值全部清除。

5.在需要的接口上添加 @Inner 注解;

6.给 pig-upms 模块的配置文件 application.yml 或者nacos中的配置文件中修改,springmvc接口匹配规则由默认的path_pattern_parser修改为ant_path_matcher;

//1.

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Inner {

    /**
     * 是否AOP统一处理
     * @return false, true
     */
    boolean value() default true;

    /**
     * 需要特殊判空的字段(预留)
     * @return {}
     */
    String[] field() default {};

}
//2.修改这个类,使他实现 InitializingBean 类,这样再创建 bean 之前就能够修改对他进行以下操作

@Slf4j
@ConfigurationProperties(prefix = "security.oauth2.ignore")
public class PermitAllUrlProperties implements InitializingBean {

    private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");

    @Getter
    @Setter
    private List urls = new ArrayList<>();

    @Override
    public void afterPropertiesSet() throws Exception {
        //拿到 bean ,RequestMappingHandlerMapping的作用是在容器启动后将系统中所有控制器方法的请求条件(RequestMappingInfo)和控制器方法(HandlerMethod)的对应关系注册到RequestMappingHandlerMapping Bean的内存中,
        RequestMappingHandlerMapping mapping = SpringUtil.getBean("requestMappingHandlerMapping");
        //拿到映射的mapper
        Map map = mapping.getHandlerMethods();

        map.keySet().forEach(info -> {
            HandlerMethod handlerMethod = map.get(info);

            // 获取方法上边的注解 替代path variable 为 *。这里注意,如果是 /user/{id} 修改为: /user/*  ,如果是 /user/id 还是: /user/id
            //​todo 必须注意,如果是 /{id} 会修改为: /*  ,就相当于,当前类同级下的匹配路径都会被添加到ignore-urls中!!!!
            Inner method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Inner.class);
            Optional.ofNullable(method).ifPresent(inner -> info.getPatternsCondition().getPatterns()
                    .forEach(url -> urls.add(ReUtil.replaceAll(url, PATTERN, "*"))));


            // 获取类上边的注解, 替代path variable 为 *。这里注意,如果是 /user/{id} 修改为: /user/*  ,如果是 /user/id 还是: /user/id
            Inner controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Inner.class);
            Optional.ofNullable(controller).ifPresent(inner ->
                    info.getPatternsCondition().getPatterns().forEach(url -> urls.add(ReUtil.replaceAll(url, PATTERN, "*"))));
        });
    }
}
//3.@Inner 注解切面

@Slf4j
@Aspect
@RequiredArgsConstructor
public class PigSecurityInnerAspect implements Ordered {

    private final HttpServletRequest request;

    @SneakyThrows
    @Around("@within(inner) || @annotation(inner)")
    public Object around(ProceedingJoinPoint point, Inner inner) {
        // 实际注入的inner实体由表达式后一个注解决定,即是方法上的@Inner注解实体,若方法上无@Inner注解,则获取类上的
        if (inner == null) {
            Class clazz = point.getTarget().getClass();
            inner = AnnotationUtils.findAnnotation(clazz, Inner.class);
        }
        //拿到 header(From) 参数值
        String header = request.getHeader(SecurityConstants.FROM);
        //是 aop 统一处理,并且 header(From) 值又正确,就跳过直接执行方法;否则抛出无权限异常
        if (inner.value() && !StrUtil.equals(SecurityConstants.FROM_SECRET_VALUE, header)) {
            log.warn("访问接口 {} 没有权限", point.getSignature().getName());
            throw new AccessDeniedException("Access is denied");
        }
        return point.proceed();
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 1;
    }

}

//--------------
//添加到 /META-INF/string.factoies
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.pig4cloud.pig.common.security.service.PigRedisOAuth2AuthorizationService,\
  com.pig4cloud.pig.common.security.service.PigRemoteRegisteredClientRepository,\
  com.pig4cloud.pig.common.security.component.PigSecurityInnerAspect
//4.网关的全局拦截器,作用所有的微服务

public class PigRequestGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1. 清洗请求头中from 参数;修改请求里面的内容然后再重新构建一下
        ServerHttpRequest request = exchange.getRequest().mutate()
                .headers(httpHeaders -> httpHeaders.remove(SecurityConstants.FROM)).build();

        //将修改后的请求放入 ServerWebExchange 里面
        addOriginalRequestUrl(exchange, request.getURI());

        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 10;
    }
}
//5. @Inner 注解的value默认是他true表示只有远程调用才可以使用,若value=false表示任意的访问都使用

@RestController
@RequiredArgsConstructor
@RequestMapping("/user")
public class UserController {

    private final SysUserService userService;
...

    /**
     * @Description: 获取指定用户名的用户全部信息,用于用户认证
     * @param username
     * @Return: com.pig4cloud.pig.common.core.util.R
     */
    @Inner
    @GetMapping("/info/{username}")
    public R info(@PathVariable String username) {
        SysUser sysUser = userService.getOne(Wrappers.lambdaQuery().eq(SysUser::getUsername, username));
        if(sysUser == null){
            return R.failed("用户不存在");
        }

        //这里拿到用户的其他关联信息,并赋值给VO
        UserInfoDTO userInfoDTO = userService.getUserInfo(sysUser);
        return R.ok(userInfoDTO);
    }
...

}
//6.在 nacos 上的 application.yml 中添加
# Spring 相关
spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher

但是要注意:凡是被 @Inner 标注的接口都不会获取用户认证信息,并且会被加入 security 白名单!

测试:

No6-4.从零搭建spring-cloud-alibaba微服务框架,解决微服务间的不鉴权调用等(四,no6-4)_第1张图片

No6-4.从零搭建spring-cloud-alibaba微服务框架,解决微服务间的不鉴权调用等(四,no6-4)_第2张图片

No6-4.从零搭建spring-cloud-alibaba微服务框架,解决微服务间的不鉴权调用等(四,no6-4)_第3张图片

No6-4.从零搭建spring-cloud-alibaba微服务框架,解决微服务间的不鉴权调用等(四,no6-4)_第4张图片

No6-4.从零搭建spring-cloud-alibaba微服务框架,解决微服务间的不鉴权调用等(四,no6-4)_第5张图片


你可能感兴趣的:(pig学习,springsecurity,spring,微服务,java)